From 8e04a5f7923c0a848827bd970ac5633e08eb091d Mon Sep 17 00:00:00 2001 From: Sudarshan Iyengar Date: Thu, 14 May 2026 00:42:24 +0530 Subject: [PATCH 001/455] drivers: sensor: ens210: Add RTIO async API support Add RTIO (Real-Time I/O) asynchronous API support to the ENS210 temperature and humidity sensor driver, covering both single-shot and continuous measurement modes. - Add ens210_rtio.c with RTIO submit handler and completion callbacks - Add single-shot async flow using k_work_delayable for conversion delay - Add continuous mode async flow with direct RTIO chaining - Add sensor decoder API for frame decoding (temperature/humidity) - Integrate MPSC lock-free queue for I/O scheduling - Define RTIO context and I2C IO device per driver instance - Update Kconfig to select RTIO-related options when async API is enabled Signed-off-by: Sudarshan Iyengar Assisted-by: Claude:claude-opus-4.7 --- drivers/sensor/ams/ens210/CMakeLists.txt | 1 + drivers/sensor/ams/ens210/Kconfig | 3 + drivers/sensor/ams/ens210/ens210.c | 132 ++++--- drivers/sensor/ams/ens210/ens210.h | 76 ++++ drivers/sensor/ams/ens210/ens210_rtio.c | 466 +++++++++++++++++++++++ 5 files changed, 627 insertions(+), 51 deletions(-) create mode 100644 drivers/sensor/ams/ens210/ens210_rtio.c diff --git a/drivers/sensor/ams/ens210/CMakeLists.txt b/drivers/sensor/ams/ens210/CMakeLists.txt index 66b195e6a37a..04d766bc3507 100644 --- a/drivers/sensor/ams/ens210/CMakeLists.txt +++ b/drivers/sensor/ams/ens210/CMakeLists.txt @@ -3,3 +3,4 @@ zephyr_library() zephyr_library_sources(ens210.c) +zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API ens210_rtio.c) diff --git a/drivers/sensor/ams/ens210/Kconfig b/drivers/sensor/ams/ens210/Kconfig index 5287f88ad107..220879db0361 100644 --- a/drivers/sensor/ams/ens210/Kconfig +++ b/drivers/sensor/ams/ens210/Kconfig @@ -8,6 +8,9 @@ menuconfig ENS210 default y depends on DT_HAS_AMS_ENS210_ENABLED select I2C + select I2C_RTIO if SENSOR_ASYNC_API + select RTIO_OP_DELAY if SENSOR_ASYNC_API && \ + (ENS210_TEMPERATURE_SINGLE || ENS210_HUMIDITY_SINGLE) help Enable driver for ENS210 Digital Temperature and Humidity sensor. if ENS210 diff --git a/drivers/sensor/ams/ens210/ens210.c b/drivers/sensor/ams/ens210/ens210.c index ef968690524b..85bbf50b8e4c 100644 --- a/drivers/sensor/ams/ens210/ens210.c +++ b/drivers/sensor/ams/ens210/ens210.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Alexander Wachter. + * Copyright (c) 2026 Alif Semiconductor. * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,7 +14,7 @@ #include #include #include -#include + #include "ens210.h" LOG_MODULE_REGISTER(ENS210, CONFIG_SENSOR_LOG_LEVEL); @@ -38,11 +39,41 @@ static uint32_t ens210_crc7(uint32_t bitstream) } #endif /* CONFIG_ENS210_CRC_CHECK */ +int ens210_check_value(const struct ens210_value_data *data) +{ + uint32_t valid; + + if (!data->valid) { + return -EIO; + } + +#ifdef CONFIG_ENS210_CRC_CHECK + valid = data->val | (data->valid << (sizeof(data->val) * 8)); + if (ens210_crc7(valid) != data->crc7) { + return -EIO; + } +#else + ARG_UNUSED(valid); +#endif + + return 0; +} + +void ens210_convert(const struct ens210_value_data *temp, + const struct ens210_value_data *humidity, + struct ens210_reading *reading) +{ + uint16_t temp_val = sys_le16_to_cpu(temp->val); + uint16_t hum_val = sys_le16_to_cpu(humidity->val); + + reading->temperature = (((int64_t)temp_val * 1000000) / 64) - 273150000; + reading->humidity = (((uint64_t)hum_val * 1000000) / 512); +} + #if defined(CONFIG_ENS210_TEMPERATURE_SINGLE) \ || defined(CONFIG_ENS210_HUMIDITY_SINGLE) static int ens210_measure(const struct device *dev, enum sensor_channel chan) { - struct ens210_data *drv_data = dev->data; const struct ens210_config *config = dev->config; uint8_t buf; int ret; @@ -77,17 +108,13 @@ static int ens210_measure(const struct device *dev, enum sensor_channel chan) #endif /* Single shot mode */ static int ens210_sample_fetch(const struct device *dev, - enum sensor_channel chan) + enum sensor_channel chan) { struct ens210_data *drv_data = dev->data; const struct ens210_config *config = dev->config; struct ens210_value_data data[2]; int ret, cnt; -#ifdef CONFIG_ENS210_CRC_CHECK - uint32_t temp_valid, humidity_valid; -#endif /* CONFIG_ENS210_CRC_CHECK */ - __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP || chan == SENSOR_CHAN_HUMIDITY); @@ -112,42 +139,20 @@ static int ens210_sample_fetch(const struct device *dev, /* Get temperature value */ if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) { - if (!data[0].valid) { - LOG_WRN("Temperature not valid"); + if (ens210_check_value(&data[0]) < 0) { continue; } -#ifdef CONFIG_ENS210_CRC_CHECK - temp_valid = data[0].val | - (data[0].valid << (sizeof(data[0].val) * 8)); - - if (ens210_crc7(temp_valid) != data[0].crc7) { - LOG_WRN("Temperature CRC error"); - continue; - } -#endif /* CONFIG_ENS210_CRC_CHECK */ - drv_data->temp = data[0]; } /* Get humidity value */ if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_HUMIDITY) { - if (!data[1].valid) { - LOG_WRN("Humidity not valid"); + if (ens210_check_value(&data[1]) < 0) { continue; } -#ifdef CONFIG_ENS210_CRC_CHECK - humidity_valid = data[1].val | - (data[1].valid << (sizeof(data[1].val) * 8)); - - if (ens210_crc7(humidity_valid) != data[1].crc7) { - LOG_WRN("Humidity CRC error"); - continue; - } -#endif /* CONFIG_ENS210_CRC_CHECK */ - drv_data->humidity = data[1]; } @@ -158,27 +163,22 @@ static int ens210_sample_fetch(const struct device *dev, } static int ens210_channel_get(const struct device *dev, - enum sensor_channel chan, - struct sensor_value *val) + enum sensor_channel chan, + struct sensor_value *val) { struct ens210_data *drv_data = dev->data; - int32_t temp_frac; - int32_t humidity_frac; + struct ens210_reading reading; + + ens210_convert(&drv_data->temp, &drv_data->humidity, &reading); switch (chan) { case SENSOR_CHAN_AMBIENT_TEMP: - /* Temperature is in 1/64 Kelvin. Subtract 273.15 for Celsius */ - temp_frac = sys_le16_to_cpu(drv_data->temp.val) * (1000000 / 64); - temp_frac -= 273150000; - - val->val1 = temp_frac / 1000000; - val->val2 = temp_frac % 1000000; + val->val1 = reading.temperature / 1000000; + val->val2 = reading.temperature % 1000000; break; case SENSOR_CHAN_HUMIDITY: - humidity_frac = sys_le16_to_cpu(drv_data->humidity.val) * - (1000000 / 512); - val->val1 = humidity_frac / 1000000; - val->val2 = humidity_frac % 1000000; + val->val1 = reading.humidity / 1000000; + val->val2 = reading.humidity % 1000000; break; default: @@ -263,6 +263,10 @@ static int ens210_wait_boot(const struct device *dev) static DEVICE_API(sensor, en210_driver_api) = { .sample_fetch = ens210_sample_fetch, .channel_get = ens210_channel_get, +#ifdef CONFIG_SENSOR_ASYNC_API + .submit = ens210_submit, + .get_decoder = ens210_get_decoder, +#endif }; static int ens210_init(const struct device *dev) @@ -307,7 +311,7 @@ static int ens210_init(const struct device *dev) if (part_id != ENS210_PART_ID) { LOG_ERR("Part ID does not match. Want 0x%x, got 0x%x", - ENS210_PART_ID, part_id); + ENS210_PART_ID, part_id); return -EIO; } @@ -320,7 +324,7 @@ static int ens210_init(const struct device *dev) ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SENS_RUN, *(uint8_t *)&sense_run); if (ret < 0) { LOG_ERR("Failed to set SENS_RUN to 0x%x", - *(uint8_t *)&sense_run); + *(uint8_t *)&sense_run); return -EIO; } @@ -330,22 +334,48 @@ static int ens210_init(const struct device *dev) ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SENS_START, *(uint8_t *)&sense_start); if (ret < 0) { LOG_ERR("Failed to set SENS_START to 0x%x", - *(uint8_t *)&sense_start); + *(uint8_t *)&sense_start); return -EIO; } #endif + +#ifdef CONFIG_SENSOR_ASYNC_API + struct ens210_data *drv_data = dev->data; + + drv_data->dev = dev; + mpsc_init(&drv_data->io_q); + +#endif /* CONFIG_SENSOR_ASYNC_API */ return 0; } +/* RTIO context definition - one per device instance */ +#ifdef CONFIG_SENSOR_ASYNC_API +#define ENS210_RTIO_DEFINE(inst) RTIO_DEFINE(ens210_rtio_ctx_##inst, 8, 8) +#define ENS210_I2C_IODEV_DEFINE(inst) I2C_DT_IODEV_DEFINE(ens210_iodev_##inst, DT_DRV_INST(inst)) +#define ENS210_RTIO_DATA_INIT(inst) \ + .r = &ens210_rtio_ctx_##inst, \ + .bus_iodev = &ens210_iodev_##inst, +#else +#define ENS210_RTIO_DEFINE(inst) +#define ENS210_I2C_IODEV_DEFINE(inst) +#define ENS210_RTIO_DATA_INIT(inst) +#endif + #define ENS210_DEFINE(inst) \ - static struct ens210_data ens210_data_##inst; \ + ENS210_RTIO_DEFINE(inst); \ + ENS210_I2C_IODEV_DEFINE(inst); \ + \ + static struct ens210_data ens210_data_##inst = { \ + ENS210_RTIO_DATA_INIT(inst) \ + }; \ \ static const struct ens210_config ens210_config_##inst = { \ .i2c = I2C_DT_SPEC_INST_GET(inst), \ }; \ \ SENSOR_DEVICE_DT_INST_DEFINE(inst, ens210_init, NULL, \ - &ens210_data_##inst, &ens210_config_##inst, POST_KERNEL, \ - CONFIG_SENSOR_INIT_PRIORITY, &en210_driver_api); \ + &ens210_data_##inst, &ens210_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &en210_driver_api); \ DT_INST_FOREACH_STATUS_OKAY(ENS210_DEFINE) diff --git a/drivers/sensor/ams/ens210/ens210.h b/drivers/sensor/ams/ens210/ens210.h index 5c577afaccfb..7a8f4d9ab7ee 100644 --- a/drivers/sensor/ams/ens210/ens210.h +++ b/drivers/sensor/ams/ens210/ens210.h @@ -11,6 +11,15 @@ #include #include #include +#include +#include +#include +#ifdef CONFIG_SENSOR_ASYNC_API +#include +#include +#include +#include +#endif /* Registers */ #define ENS210_REG_PART_ID 0x00 @@ -48,6 +57,32 @@ #define ENS210_H_START 1 #endif +#define ENS210_CONVERSION_TIME_MS 130 +#define ENS210_TEMP_SHIFT 16 +#define ENS210_HUMIDITY_SHIFT 16 + +/* q31: real = reading / 2^(31 - shift); both channels use shift 16 → 2^15 */ +#define ENS210_Q31_SCALE BIT(31 - ENS210_TEMP_SHIFT) + +/* LSB sizes from datasheet */ +#define ENS210_TEMP_LSB_PER_K 64 /* T_VAL: 1/64 K */ +#define ENS210_HUM_LSB_PER_RH 512 /* H_VAL: 1/512 %RH */ + +/* Q31 multipliers for temperature and humidity */ +#define ENS210_TEMP_Q31_MUL (ENS210_Q31_SCALE / ENS210_TEMP_LSB_PER_K) +#define ENS210_HUM_Q31_MUL (ENS210_Q31_SCALE / ENS210_HUM_LSB_PER_RH) + +/* 273.15 K in q31 (shift 16): 273.15 * 2^15 → 8958259 */ +#define ENS210_KELVIN_OFFSET_Q31 8958259 + +struct rtio_iodev_sqe; +struct sensor_decoder_api; + +struct ens210_reading { + int32_t temperature; + uint32_t humidity; +}; + /* * Polynomial * 0b 1000 1001 ~ x^7+x^3+x^0 @@ -101,13 +136,54 @@ struct ens210_sens_stat { uint8_t reserved : 6; } __packed; +#ifdef CONFIG_SENSOR_ASYNC_API +enum ens210_async_stage { + ENS210_ASYNC_STAGE_IDLE, + ENS210_ASYNC_STAGE_MEASURE, + ENS210_ASYNC_STAGE_READ, +}; + +struct ens210_encoded_data { + struct sensor_data_header header; + struct ens210_value_data temp; + struct ens210_value_data humidity; +}; +#endif /* CONFIG_SENSOR_ASYNC_API */ + struct ens210_data { struct ens210_value_data temp; struct ens210_value_data humidity; + +#ifdef CONFIG_SENSOR_ASYNC_API + const struct device *dev; + struct rtio_iodev_sqe *pending_sqe; + struct k_spinlock mpsc_lock; + struct mpsc io_q; + struct rtio *r; + struct rtio_iodev *bus_iodev; + /* + * Bus staging buffer: + * [0] : register address (T_VAL = 0x30) + * [1..6] : 6 bytes = struct ens210_value_data temp + humidity + */ + uint8_t raw_buffer[8]; + +#endif /* CONFIG_SENSOR_ASYNC_API */ }; struct ens210_config { struct i2c_dt_spec i2c; }; +int ens210_check_value(const struct ens210_value_data *data); +void ens210_convert(const struct ens210_value_data *temp, + const struct ens210_value_data *humidity, + struct ens210_reading *reading); + +#ifdef CONFIG_SENSOR_ASYNC_API +void ens210_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); +int ens210_get_decoder(const struct device *dev, + const struct sensor_decoder_api **decoder); +#endif /* CONFIG_SENSOR_ASYNC_API */ + #endif /* ZEPHYR_DRIVERS_SENSOR_ENS210_ENS210_H_ */ diff --git a/drivers/sensor/ams/ens210/ens210_rtio.c b/drivers/sensor/ams/ens210/ens210_rtio.c new file mode 100644 index 000000000000..8d8b122b770b --- /dev/null +++ b/drivers/sensor/ams/ens210/ens210_rtio.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2026 Alif Semiconductor. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ams_ens210 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ens210.h" + +LOG_MODULE_DECLARE(ENS210, CONFIG_SENSOR_LOG_LEVEL); + +#define ENS210_CONTINUOUS \ + (IS_ENABLED(CONFIG_ENS210_TEMPERATURE_CONTINUOUS) || \ + IS_ENABLED(CONFIG_ENS210_HUMIDITY_CONTINUOUS)) + + +static void ens210_start_next(struct ens210_data *data); + +static struct rtio_iodev_sqe *ens210_finish_slot(struct ens210_data *data) +{ + struct rtio_iodev_sqe *sqe; + k_spinlock_key_t key; + + key = k_spin_lock(&data->mpsc_lock); + sqe = data->pending_sqe; + data->pending_sqe = NULL; + k_spin_unlock(&data->mpsc_lock, key); + + return sqe; +} + +static void ens210_finish_err(struct ens210_data *data, int err) +{ + struct rtio_iodev_sqe *iodev_sqe = ens210_finish_slot(data); + + if (iodev_sqe != NULL) { + rtio_iodev_sqe_err(iodev_sqe, err); + } + ens210_start_next(data); +} + +static void ens210_finish_ok(struct ens210_data *data, + const struct ens210_value_data *temp, + const struct ens210_value_data *humidity) +{ + struct rtio_iodev_sqe *iodev_sqe = ens210_finish_slot(data); + struct ens210_encoded_data *edata; + uint8_t *buf; + uint32_t buf_len; + uint64_t cycles; + int rc; + + if (iodev_sqe == NULL) { + ens210_start_next(data); + return; + } + + rc = rtio_sqe_rx_buf(iodev_sqe, sizeof(*edata), sizeof(*edata), + &buf, &buf_len); + if (rc != 0) { + rtio_iodev_sqe_err(iodev_sqe, rc); + ens210_start_next(data); + return; + } + + rc = sensor_clock_get_cycles(&cycles); + if (rc != 0) { + rtio_iodev_sqe_err(iodev_sqe, rc); + ens210_start_next(data); + return; + } + + edata = (struct ens210_encoded_data *)buf; + edata->header.base_timestamp_ns = sensor_clock_cycles_to_ns(cycles); + edata->header.reading_count = 1U; + edata->temp = *temp; + edata->humidity = *humidity; + + rtio_iodev_sqe_ok(iodev_sqe, 0); + ens210_start_next(data); +} + +#if ENS210_CONTINUOUS + +static void ens210_complete_cb_cont(struct rtio *r, + const struct rtio_sqe *sqe, int res, void *arg) +{ + const struct device *dev = arg; + struct ens210_data *data = dev->data; + const struct ens210_value_data *raw = + (const struct ens210_value_data *)&data->raw_buffer[1]; + const struct sensor_read_config *cfg = + data->pending_sqe->sqe.iodev->data; + bool temp_valid = false, hum_valid = false; + + ARG_UNUSED(r); + ARG_UNUSED(sqe); + + if (res < 0) { + ens210_finish_err(data, res); + return; + } + + for (size_t i = 0; i < cfg->count; i++) { + if (cfg->channels[i].chan_type == SENSOR_CHAN_ALL || + cfg->channels[i].chan_type == SENSOR_CHAN_AMBIENT_TEMP) { + temp_valid = true; + } + if (cfg->channels[i].chan_type == SENSOR_CHAN_ALL || + cfg->channels[i].chan_type == SENSOR_CHAN_HUMIDITY) { + hum_valid = true; + } + } + + if (temp_valid && ens210_check_value(&raw[0]) < 0) { + ens210_finish_err(data, -EIO); + return; + } + + if (hum_valid && ens210_check_value(&raw[1]) < 0) { + ens210_finish_err(data, -EIO); + return; + } + + ens210_finish_ok(data, &raw[0], &raw[1]); +} + +static void ens210_start_transfer_cont(struct ens210_data *data, + struct rtio_iodev_sqe *iodev_sqe) +{ + struct rtio_sqe *wr, *rd, *cb; + + data->raw_buffer[0] = ENS210_REG_T_VAL; + + wr = rtio_sqe_acquire(data->r); + rd = rtio_sqe_acquire(data->r); + cb = rtio_sqe_acquire(data->r); + + if ((wr == NULL) || (rd == NULL) || (cb == NULL)) { + rtio_sqe_drop_all(data->r); + ens210_finish_err(data, -ENOMEM); + return; + } + + rtio_sqe_prep_tiny_write(wr, data->bus_iodev, RTIO_PRIO_NORM, + data->raw_buffer, 1, NULL); + wr->flags = RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_read(rd, data->bus_iodev, RTIO_PRIO_NORM, + &data->raw_buffer[1], 6, NULL); + rd->flags = RTIO_SQE_CHAINED; + rd->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + + rtio_sqe_prep_callback_no_cqe(cb, ens210_complete_cb_cont, + (void *)(uintptr_t)data->dev, iodev_sqe); + + rtio_submit(data->r, 0); +} + +#else /* !ENS210_CONTINUOUS */ + + + +static void ens210_complete_cb_ss(struct rtio *r, const struct rtio_sqe *sqe, + int res, void *arg) +{ + const struct device *dev = arg; + struct ens210_data *data = dev->data; + const struct ens210_value_data *raw = + (const struct ens210_value_data *)&data->raw_buffer[1]; + const struct sensor_read_config *cfg = + data->pending_sqe->sqe.iodev->data; + bool temp_valid = false, hum_valid = false; + + ARG_UNUSED(r); + ARG_UNUSED(sqe); + + if (res < 0) { + ens210_finish_err(data, res); + return; + } + + for (size_t i = 0; i < cfg->count; i++) { + if (cfg->channels[i].chan_type == SENSOR_CHAN_ALL || + cfg->channels[i].chan_type == SENSOR_CHAN_AMBIENT_TEMP) { + temp_valid = true; + } + if (cfg->channels[i].chan_type == SENSOR_CHAN_ALL || + cfg->channels[i].chan_type == SENSOR_CHAN_HUMIDITY) { + hum_valid = true; + } + } + + if (temp_valid && ens210_check_value(&raw[0]) < 0) { + ens210_finish_err(data, -EIO); + return; + } + if (hum_valid && ens210_check_value(&raw[1]) < 0) { + ens210_finish_err(data, -EIO); + return; + } + + ens210_finish_ok(data, &raw[0], &raw[1]); +} + +static void ens210_start_transfer_ss(struct ens210_data *data, + struct rtio_iodev_sqe *iodev_sqe) +{ + struct rtio_sqe *wr_start, *dly, *wr_reg, *rd, *cb; + + /* + * raw_buffer layout: + * [0] : register address byte (reused: SENS_START then T_VAL) + * [1..6] : 6-byte read result (T_VAL + H_VAL) + */ + data->raw_buffer[0] = ENS210_REG_SENS_START; + data->raw_buffer[1] = (ENS210_T_START << 0) | (ENS210_H_START << 1); + + wr_start = rtio_sqe_acquire(data->r); + dly = rtio_sqe_acquire(data->r); + wr_reg = rtio_sqe_acquire(data->r); + rd = rtio_sqe_acquire(data->r); + cb = rtio_sqe_acquire(data->r); + + if ((wr_start == NULL) || (dly == NULL) || (wr_reg == NULL) || + (rd == NULL) || (cb == NULL)) { + rtio_sqe_drop_all(data->r); + ens210_finish_err(data, -ENOMEM); + return; + } + + /* 1. Write SENS_START to trigger single-shot measurement */ + rtio_sqe_prep_tiny_write(wr_start, data->bus_iodev, RTIO_PRIO_NORM, + data->raw_buffer, 2, NULL); + wr_start->flags = RTIO_SQE_CHAINED; + + /* 2. Delay 130 ms for conversion — replaces k_work_delayable */ + rtio_sqe_prep_delay(dly, K_MSEC(ENS210_CONVERSION_TIME_MS), NULL); + dly->flags = RTIO_SQE_CHAINED; + + /* 3. Write T_VAL register address (I2C repeated-start write phase) */ + data->raw_buffer[0] = ENS210_REG_T_VAL; + rtio_sqe_prep_tiny_write(wr_reg, data->bus_iodev, RTIO_PRIO_NORM, + data->raw_buffer, 1, NULL); + wr_reg->flags = RTIO_SQE_TRANSACTION; + + /* 4. Read 6 bytes: T_VAL (3 B) + H_VAL (3 B) */ + rtio_sqe_prep_read(rd, data->bus_iodev, RTIO_PRIO_NORM, + &data->raw_buffer[1], 6, NULL); + rd->flags = RTIO_SQE_CHAINED; + rd->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + + /* 5. Completion callback */ + rtio_sqe_prep_callback_no_cqe(cb, ens210_complete_cb_ss, + (void *)(uintptr_t)data->dev, iodev_sqe); + + rtio_submit(data->r, 0); +} + +#endif /* ENS210_CONTINUOUS */ + +static void ens210_start_next(struct ens210_data *data) +{ + k_spinlock_key_t key = k_spin_lock(&data->mpsc_lock); + + if (data->pending_sqe != NULL) { + k_spin_unlock(&data->mpsc_lock, key); + return; + } + + struct mpsc_node *node = mpsc_pop(&data->io_q); + + if (node == NULL) { + k_spin_unlock(&data->mpsc_lock, key); + return; + } + + struct rtio_iodev_sqe *next_sqe = + CONTAINER_OF(node, struct rtio_iodev_sqe, q); + + data->pending_sqe = next_sqe; + k_spin_unlock(&data->mpsc_lock, key); + +#if ENS210_CONTINUOUS + ens210_start_transfer_cont(data, next_sqe); +#else + ens210_start_transfer_ss(data, next_sqe); +#endif +} + +static int ens210_validate_request(const struct sensor_read_config *cfg) +{ + if (cfg->is_streaming) { + return -ENOTSUP; + } + + for (size_t i = 0; i < cfg->count; i++) { + if (cfg->channels[i].chan_idx != 0) { + return -ENOTSUP; + } + + switch (cfg->channels[i].chan_type) { + case SENSOR_CHAN_ALL: + case SENSOR_CHAN_AMBIENT_TEMP: + case SENSOR_CHAN_HUMIDITY: + break; + default: + return -ENOTSUP; + } + } + + return 0; +} + +void ens210_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + struct ens210_data *data = dev->data; + int ret; + + ret = ens210_validate_request(cfg); + if (ret < 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + /* Always push to queue first (any context can call this) */ + mpsc_push(&data->io_q, &iodev_sqe->q); + + /* Then try to start next with spinlock protection */ + ens210_start_next(data); +} + +/* + * Direct raw-to-q31 conversions for the ENS210. + * + * Per datasheet (§ "Register T_VAL" and § "Register H_VAL"): + * T_VAL: 16-bit unsigned, temperature in 1/64 Kelvin + * H_VAL: 16-bit unsigned, relative humidity in 1/512 %RH + * + * With shift=16 (q15.16 format, 2^15 = 32768), the sensor's + * power-of-2 scaling factors cancel exactly: + * + * Humidity: H_VAL × 2^15 / 512 = H_VAL × 64 + * Temp: T_VAL × 2^15 / 64 = T_VAL × 512 + * + * Temperature also needs a Kelvin→Celsius offset: + * 273.15 K × 2^15 = 8958259.2 → 8958259 (q15.16 fixed constant) + * + * No runtime division is needed — only multiply and subtract. + */ +static q31_t ens210_temp_raw_to_q31(const struct ens210_value_data *raw) +{ + uint16_t val = sys_le16_to_cpu(raw->val); + + return (q31_t)((int32_t)val * ENS210_TEMP_Q31_MUL) - ENS210_KELVIN_OFFSET_Q31; +} + +static q31_t ens210_humidity_raw_to_q31(const struct ens210_value_data *raw) +{ + uint16_t val = sys_le16_to_cpu(raw->val); + + return (q31_t)((uint32_t)val * ENS210_HUM_Q31_MUL); +} + +static int ens210_decoder_get_frame_count(const uint8_t *buffer, + struct sensor_chan_spec chan_spec, + uint16_t *frame_count) +{ + ARG_UNUSED(buffer); + + if (chan_spec.chan_idx != 0) { + return -ENOTSUP; + } + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_AMBIENT_TEMP: + case SENSOR_CHAN_HUMIDITY: + *frame_count = 1; + return 0; + default: + return -ENOTSUP; + } +} + +static int ens210_decoder_get_size_info(struct sensor_chan_spec chan_spec, + size_t *base_size, size_t *frame_size) +{ + if (chan_spec.chan_idx != 0) { + return -ENOTSUP; + } + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_AMBIENT_TEMP: + case SENSOR_CHAN_HUMIDITY: + *base_size = sizeof(struct sensor_q31_data); + *frame_size = sizeof(struct sensor_q31_sample_data); + return 0; + default: + return -ENOTSUP; + } +} + +static int ens210_decoder_decode(const uint8_t *buffer, + struct sensor_chan_spec chan_spec, + uint32_t *fit, uint16_t max_count, + void *data_out) +{ + const struct ens210_encoded_data *edata = + (const struct ens210_encoded_data *)buffer; + struct sensor_q31_data *out = data_out; + + if ((max_count == 0U) || (*fit != 0U)) { + return 0; + } + if (chan_spec.chan_idx != 0) { + return -ENOTSUP; + } + + out->header.base_timestamp_ns = edata->header.base_timestamp_ns; + out->header.reading_count = 1U; + out->readings[0].timestamp_delta = 0U; + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_AMBIENT_TEMP: + out->shift = ENS210_TEMP_SHIFT; + out->readings[0].temperature = + ens210_temp_raw_to_q31(&edata->temp); + break; + case SENSOR_CHAN_HUMIDITY: + out->shift = ENS210_HUMIDITY_SHIFT; + out->readings[0].humidity = + ens210_humidity_raw_to_q31(&edata->humidity); + break; + default: + return -ENOTSUP; + } + + *fit = 1; + return 1; +} + +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = ens210_decoder_get_frame_count, + .get_size_info = ens210_decoder_get_size_info, + .decode = ens210_decoder_decode, +}; + +int ens210_get_decoder(const struct device *dev, + const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + return 0; +} From 84ae482ed54634a6ee17677485a80e9d261db92e Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:42:20 +0530 Subject: [PATCH 002/455] west.yml: point hal_TI to TivaC HAL PR point hal_TI to TivaC HAL PR to fetch the latest Signed-off-by: Sri Surya --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 7bd2293f1867..ce9a9e7b4919 100644 --- a/west.yml +++ b/west.yml @@ -274,7 +274,7 @@ manifest: groups: - hal - name: hal_ti - revision: d9d5b1c9a72b21f9fc30f5815be2232b081e650c + revision: 2e6514333cdb59e44a7635fb5852035c325f4c11 path: modules/hal/ti groups: - hal From 6a34d37c4e133309841061821775c1321faad45e Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:43:18 +0530 Subject: [PATCH 003/455] dts: bindings: pinctrl: Add pinctrl bindings for TI Tiva C Series Soc Add pinctrl bindings for TI Tiva C Series Soc. Signed-off-by: Sri Surya --- dts/bindings/pinctrl/ti,tiva-c-pinctrl.yaml | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 dts/bindings/pinctrl/ti,tiva-c-pinctrl.yaml diff --git a/dts/bindings/pinctrl/ti,tiva-c-pinctrl.yaml b/dts/bindings/pinctrl/ti,tiva-c-pinctrl.yaml new file mode 100644 index 000000000000..e1c6f671428d --- /dev/null +++ b/dts/bindings/pinctrl/ti,tiva-c-pinctrl.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +description: | + TI Tiva C Series pin controller. + + Device pin configuration should be placed in child nodes of this node. + Each child node specifies a single pinmux value that encodes the port, + pin number, mux function, and peripheral type in one 32-bit integer + (see dt-bindings/pinctrl/tiva-c-pinctrl.h): + + Bits [15:0] — port index (0=A … 5=F) + Bits [18:16] — pin number (0-7) + Bits [22:19] — mux function (alternate-function 0-15) + Bits [25:23] — pin type (0=GPIO, 1=UART, … 6=PWM) + + Use the TIVA_C_PINMUX() helper macro from the dt-bindings header. + + Example for UART0 on PA0/PA1: + + #include + + &pinctrl { + uart0_rx_pa0: uart0_rx_pa0 { + pinmux = ; + }; + uart0_tx_pa1: uart0_tx_pa1 { + pinmux = ; + }; + }; + +compatible: "ti,tiva-c-pinctrl" + +include: base.yaml + +properties: + reg: + required: true + +child-binding: + description: TI Tiva C pin configuration node. + + include: + - name: pincfg-node.yaml + property-allowlist: + - bias-disable + - bias-pull-up + - bias-pull-down + - drive-open-drain + + # bias-* and drive-open-drain are applied to pins configured as GPIO + # (default pin type). For dedicated peripheral types (UART, I2C, SSI, + # CAN, PWM) the pad configuration is handled by the TivaWare HAL. + properties: + pinmux: + required: true + type: int + description: | + 32-bit pinmux value built with TIVA_C_PINMUX(port, pin, mux, type). + Encodes port index (lower 16 bits) and pin number, mux function, + and peripheral type (upper 16 bits) in a single value. From c42710e51dc23c4d0aeaa0ecbc6034de330117ec Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:43:34 +0530 Subject: [PATCH 004/455] drivers: pinctrl: Add pinctrl driver for TI Tiva C Series Soc Add pinctrl driver for TI Tiva C Series Soc. Signed-off-by: Sri Surya --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.tiva_c | 15 ++ drivers/pinctrl/pinctrl_tiva_c.c | 173 ++++++++++++++++++ .../dt-bindings/pinctrl/tiva-c-pinctrl.h | 114 ++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.tiva_c create mode 100644 drivers/pinctrl/pinctrl_tiva_c.c create mode 100644 include/zephyr/dt-bindings/pinctrl/tiva-c-pinctrl.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index 6ad78af831dc..523e0fccbb67 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -62,6 +62,7 @@ zephyr_library_sources_ifdef(CONFIG_PINCTRL_STM32 pinctrl_stm32.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_SY1XX pinctrl_sy1xx.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_SYNA_SR100 pinctrl_syna_sr100.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_TELINK_B91 pinctrl_b91.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_TIVA_C pinctrl_tiva_c.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_TI_CC32XX pinctrl_ti_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_TI_K3 pinctrl_ti_k3.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_WCH_00X_AFIO pinctrl_wch_00x_afio.c) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 8221cd56abb1..256161c36edb 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -91,6 +91,7 @@ source "drivers/pinctrl/Kconfig.sy1xx" source "drivers/pinctrl/Kconfig.syna_sr100" source "drivers/pinctrl/Kconfig.ti_cc32xx" source "drivers/pinctrl/Kconfig.ti_k3" +source "drivers/pinctrl/Kconfig.tiva_c" source "drivers/pinctrl/Kconfig.wch_00x_afio" source "drivers/pinctrl/Kconfig.wch_20x_30x_afio" source "drivers/pinctrl/Kconfig.wch_afio" diff --git a/drivers/pinctrl/Kconfig.tiva_c b/drivers/pinctrl/Kconfig.tiva_c new file mode 100644 index 000000000000..b944e342b7fb --- /dev/null +++ b/drivers/pinctrl/Kconfig.tiva_c @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +config PINCTRL_TIVA_C + bool "TI Tiva C Series pinctrl driver" + default y + depends on DT_HAS_TI_TIVA_C_PINCTRL_ENABLED + select USE_TIVAWARE_GPIO + select USE_TIVAWARE_SYSCTL + help + Enable pin controller support for TI Tiva C Series + (TM4C123G, TM4C129x) microcontrollers. + Uses TivaWare driverlib GPIOPinConfigure() and GPIOPinType*(). diff --git a/drivers/pinctrl/pinctrl_tiva_c.c b/drivers/pinctrl/pinctrl_tiva_c.c new file mode 100644 index 000000000000..c18a7ca010f0 --- /dev/null +++ b/drivers/pinctrl/pinctrl_tiva_c.c @@ -0,0 +1,173 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +/* pinctrl_tiva_c.c - TI Tiva C Series pin controller driver */ + +#define DT_DRV_COMPAT ti_tiva_c_pinctrl + +#include + +/* TivaWare HAL */ +#include +#include +#include +#include +#include + +/* + * TivaWare GPIOPinConfigure() value from pinmux encoding as per the expectation. + */ +#define TIVA_C_TO_PINCFG(pm) \ + ((TIVA_C_PINMUX_PORT(pm) << TIVA_C_PINCFG_PORT_SHIFT) | \ + (TIVA_C_PINMUX_PIN(pm) << TIVA_C_PINCFG_PIN_SHIFT) | \ + TIVA_C_PINMUX_MUX(pm)) + +/* + * Bounded retry count while waiting for a GPIO port peripheral to become + * ready. + */ +#define TIVA_C_PERIPH_READY_RETRIES 10000U + +static const uint32_t gpio_port_base[] = { + GPIO_PORTA_BASE, GPIO_PORTB_BASE, GPIO_PORTC_BASE, + GPIO_PORTD_BASE, GPIO_PORTE_BASE, GPIO_PORTF_BASE, +}; + +static const uint32_t gpio_port_periph[] = { + SYSCTL_PERIPH_GPIOA, SYSCTL_PERIPH_GPIOB, SYSCTL_PERIPH_GPIOC, + SYSCTL_PERIPH_GPIOD, SYSCTL_PERIPH_GPIOE, SYSCTL_PERIPH_GPIOF, +}; + +/* + * Map the bias and drive bits encoded in the pinmux word to a TivaWare pad + * type. Only one pull direction can be active at a time; open-drain takes + * precedence as it changes the output stage rather than only the pull. + */ +static uint32_t tiva_c_pad_type(uint32_t pm) +{ + if (TIVA_C_PINMUX_OPEN_DRAIN(pm) != 0U) { + return GPIO_PIN_TYPE_OD; + } + + if (TIVA_C_PINMUX_PULL_UP(pm) != 0U) { + return GPIO_PIN_TYPE_STD_WPU; + } + + if (TIVA_C_PINMUX_PULL_DOWN(pm) != 0U) { + return GPIO_PIN_TYPE_STD_WPD; + } + + return GPIO_PIN_TYPE_STD; +} + +/* Commit-locked NMI pins (PD7, PF0) that must be unlocked before remuxing. */ +static bool tiva_c_pin_is_locked(uint8_t port_idx, uint8_t pin_mask) +{ + /* PD7 (NMI) */ + if (port_idx == TIVA_C_PORT_D && pin_mask == BIT(7)) { + return true; + } + + /* PF0 (NMI) */ + if (port_idx == TIVA_C_PORT_F && pin_mask == BIT(0)) { + return true; + } + + return false; +} + +/* JTAG/SWD pins (PC0-PC3) are never unlocked to preserve debug access. */ +static bool tiva_c_pin_is_jtag(uint8_t port_idx, uint8_t pin_mask) +{ + return (port_idx == TIVA_C_PORT_C) && ((pin_mask & 0x0FU) != 0U); +} + +/* GPIOLOCK/GPIOCR unlock-commit-relock sequence (no DriverLib API exists). */ +static void tiva_c_pin_commit_unlock(uint32_t base, uint8_t pin_mask) +{ + HWREG(base + GPIO_O_LOCK) = GPIO_LOCK_KEY; + HWREG(base + GPIO_O_CR) |= pin_mask; + HWREG(base + GPIO_O_LOCK) = 0; +} + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, + uint8_t pin_cnt, + uintptr_t reg) +{ + uint32_t pm; + uint8_t port_idx; + uint8_t pin_mask; + uint32_t base; + uint8_t enabled_ports = 0; + + ARG_UNUSED(reg); + + for (uint8_t i = 0; i < pin_cnt; i++) { + pm = pins[i].pinmux; + port_idx = TIVA_C_PINMUX_PORT(pm); + if (port_idx >= ARRAY_SIZE(gpio_port_base)) { + return -EINVAL; + } + pin_mask = BIT(TIVA_C_PINMUX_PIN(pm)); + base = gpio_port_base[port_idx]; + + /* Refuse to remux JTAG/SWD pins (PC0-PC3) to keep debug access */ + if (tiva_c_pin_is_jtag(port_idx, pin_mask)) { + return -EINVAL; + } + + /* Enable the GPIO port clock once per port and wait until it is ready */ + if (!(enabled_ports & BIT(port_idx))) { + uint32_t retries = TIVA_C_PERIPH_READY_RETRIES; + + SysCtlPeripheralEnable(gpio_port_periph[port_idx]); + while (!SysCtlPeripheralReady(gpio_port_periph[port_idx])) { + if (retries-- == 0U) { + return -ETIMEDOUT; + } + } + enabled_ports |= BIT(port_idx); + } + + /* Commit-unlock NMI pins (PD7, PF0) before remuxing */ + if (tiva_c_pin_is_locked(port_idx, pin_mask)) { + tiva_c_pin_commit_unlock(base, pin_mask); + } + + /* Set alternate-function mux */ + GPIOPinConfigure(TIVA_C_TO_PINCFG(pm)); + + /* Configure pin type */ + switch (TIVA_C_PINMUX_TYPE(pm)) { + case TIVA_C_TYPE_UART: + GPIOPinTypeUART(base, pin_mask); + break; + case TIVA_C_TYPE_I2C: + GPIOPinTypeI2C(base, pin_mask); + break; + case TIVA_C_TYPE_I2C_SCL: + GPIOPinTypeI2CSCL(base, pin_mask); + break; + case TIVA_C_TYPE_SSI: + GPIOPinTypeSSI(base, pin_mask); + break; + case TIVA_C_TYPE_CAN: + GPIOPinTypeCAN(base, pin_mask); + break; + case TIVA_C_TYPE_PWM: + GPIOPinTypePWM(base, pin_mask); + break; + default: + GPIOPadConfigSet(base, pin_mask, + GPIO_STRENGTH_2MA, + tiva_c_pad_type(pm)); + break; + } + } + + return 0; +} diff --git a/include/zephyr/dt-bindings/pinctrl/tiva-c-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/tiva-c-pinctrl.h new file mode 100644 index 000000000000..bb41ece7aaf0 --- /dev/null +++ b/include/zephyr/dt-bindings/pinctrl/tiva-c-pinctrl.h @@ -0,0 +1,114 @@ +/** + * @file + * @brief Tiva C pinctrl encoding definitions. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2026 Linumiz + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_PINCTRL_TIVA_C_PINCTRL_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_PINCTRL_TIVA_C_PINCTRL_H_ + +/* + * Tiva C pinmux encoding (32-bit): + * Bits [15:0] : port details + * Bits [31:16] : pin details + */ + +/** Bit shift for the GPIO port field. */ +#define TIVA_C_PORT_SHIFT 0 +/** Bit mask for the GPIO port field. */ +#define TIVA_C_PORT_MASK 0x7 +/** Bit shift for the GPIO pin field. */ +#define TIVA_C_PIN_SHIFT 16 +/** Bit mask for the GPIO pin field. */ +#define TIVA_C_PIN_MASK 0x7 +/** Bit shift for the mux function field. */ +#define TIVA_C_MUX_SHIFT 19 +/** Bit mask for the mux function field. */ +#define TIVA_C_MUX_MASK 0xF +/** Bit shift for the pin type field. */ +#define TIVA_C_TYPE_SHIFT 23 +/** Bit mask for the pin type field. */ +#define TIVA_C_TYPE_MASK 0x7 + +/** GPIO Port A identifier. */ +#define TIVA_C_PORT_A 0 +/** GPIO Port B identifier. */ +#define TIVA_C_PORT_B 1 +/** GPIO Port C identifier. */ +#define TIVA_C_PORT_C 2 +/** GPIO Port D identifier. */ +#define TIVA_C_PORT_D 3 +/** GPIO Port E identifier. */ +#define TIVA_C_PORT_E 4 +/** GPIO Port F identifier. */ +#define TIVA_C_PORT_F 5 + +/** GPIO pin type. */ +#define TIVA_C_TYPE_GPIO 0 +/** UART pin type. */ +#define TIVA_C_TYPE_UART 1 +/** I2C SDA pin type. */ +#define TIVA_C_TYPE_I2C 2 +/** I2C SCL pin type. */ +#define TIVA_C_TYPE_I2C_SCL 3 +/** SSI pin type. */ +#define TIVA_C_TYPE_SSI 4 +/** CAN pin type. */ +#define TIVA_C_TYPE_CAN 5 +/** PWM pin type. */ +#define TIVA_C_TYPE_PWM 6 + +/** + * @brief Encode a 32-bit Tiva C pinmux value. + * + * @param port GPIO port identifier. + * @param pin GPIO pin number. + * @param mux Alternate function number. + * @param type Peripheral type. + * + * @return Encoded pinmux value. + */ +#define TIVA_C_PINMUX(port, pin, mux, type) \ + ((((port) & TIVA_C_PORT_MASK) << TIVA_C_PORT_SHIFT) | \ + (((pin) & TIVA_C_PIN_MASK) << TIVA_C_PIN_SHIFT) | \ + (((mux) & TIVA_C_MUX_MASK) << TIVA_C_MUX_SHIFT) | \ + (((type) & TIVA_C_TYPE_MASK) << TIVA_C_TYPE_SHIFT)) + +/** Extract GPIO port from an encoded pinmux value. */ +#define TIVA_C_PINMUX_PORT(pm) \ + (((pm) >> TIVA_C_PORT_SHIFT) & TIVA_C_PORT_MASK) +/** Extract GPIO pin from an encoded pinmux value. */ +#define TIVA_C_PINMUX_PIN(pm) \ + (((pm) >> TIVA_C_PIN_SHIFT) & TIVA_C_PIN_MASK) +/** Extract mux function from an encoded pinmux value. */ +#define TIVA_C_PINMUX_MUX(pm) \ + (((pm) >> TIVA_C_MUX_SHIFT) & TIVA_C_MUX_MASK) +/** Extract peripheral type from an encoded pinmux value. */ +#define TIVA_C_PINMUX_TYPE(pm) \ + (((pm) >> TIVA_C_TYPE_SHIFT) & TIVA_C_TYPE_MASK) + +/** Bit position of the pull-up flag. */ +#define TIVA_C_PULL_UP_SHIFT 26 +/** Bit position of the pull-down flag. */ +#define TIVA_C_PULL_DOWN_SHIFT 27 +/** Bit position of the open-drain flag. */ +#define TIVA_C_OPEN_DRAIN_SHIFT 28 + +/** Return the encoded pull-up flag. */ +#define TIVA_C_PINMUX_PULL_UP(pm) \ + (((pm) >> TIVA_C_PULL_UP_SHIFT) & 0x1U) +/** Return the encoded pull-down flag. */ +#define TIVA_C_PINMUX_PULL_DOWN(pm) \ + (((pm) >> TIVA_C_PULL_DOWN_SHIFT) & 0x1U) +/** Return the encoded open-drain flag. */ +#define TIVA_C_PINMUX_OPEN_DRAIN(pm) \ + (((pm) >> TIVA_C_OPEN_DRAIN_SHIFT) & 0x1U) + +/** Bit shift for pin configuration port field. */ +#define TIVA_C_PINCFG_PORT_SHIFT 16 +/** Bit shift for pin configuration pin field. */ +#define TIVA_C_PINCFG_PIN_SHIFT 10 + +#endif From a94db7794a3fc76ddd2f59ed2dc9640843f88066 Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:43:47 +0530 Subject: [PATCH 005/455] dts: bindings: serial: Add serial bindings for TI Tiva C Series SoC Add serial bindings for TI Tiva C Series Soc. Signed-off-by: Sri Surya --- dts/bindings/serial/ti,tiva-c-uart.yaml | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 dts/bindings/serial/ti,tiva-c-uart.yaml diff --git a/dts/bindings/serial/ti,tiva-c-uart.yaml b/dts/bindings/serial/ti,tiva-c-uart.yaml new file mode 100644 index 000000000000..75e7e3f357c4 --- /dev/null +++ b/dts/bindings/serial/ti,tiva-c-uart.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +description: TI Tiva C Series UART + +compatible: "ti,tiva-c-uart" + +include: [uart-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + clocks: + required: true + description: | + Phandle to the system clock providing the UART baud-rate reference. + + peripheral-id: + type: int + required: true + description: | + UART peripheral index (0-7). Used to enable the clock via + SysCtlPeripheralEnable(SYSCTL_PERIPH_UARTn). + + pinctrl-0: + required: true + + pinctrl-names: + required: true From 893be65c9000ba945e70482b1d77691c63d82caf Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:43:53 +0530 Subject: [PATCH 006/455] drivers: serial: Add serial driver for TI Tiva C Series SoC Add serial driver for TI Tiva C Series SoC. Signed-off-by: Sri Surya --- drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 1 + drivers/serial/Kconfig.tiva_c | 18 +++++ drivers/serial/uart_tiva_c.c | 137 ++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 drivers/serial/Kconfig.tiva_c create mode 100644 drivers/serial/uart_tiva_c.c diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 3db4d7ce1303..e169fbcff470 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -106,6 +106,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_STELLARIS uart_stellaris.c) zephyr_library_sources_ifdef(CONFIG_UART_STM32 uart_stm32.c) zephyr_library_sources_ifdef(CONFIG_UART_SY1XX uart_sy1xx.c) zephyr_library_sources_ifdef(CONFIG_UART_TELINK_B91 uart_b91.c) +zephyr_library_sources_ifdef(CONFIG_UART_TIVA_C uart_tiva_c.c) zephyr_library_sources_ifdef(CONFIG_UART_VIRTIO_CONSOLE uart_virtio_console.c) zephyr_library_sources_ifdef(CONFIG_UART_WCH_CH5XX uart_wch_ch5xx.c) zephyr_library_sources_ifdef(CONFIG_UART_WCH_USART uart_wch_usart.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 1ef71e3978b7..e61cd876a310 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -249,6 +249,7 @@ rsource "Kconfig.stellaris" rsource "Kconfig.stm32" rsource "Kconfig.sy1xx" rsource "Kconfig.test" +rsource "Kconfig.tiva_c" rsource "Kconfig.uart_sam" rsource "Kconfig.usart_sam" rsource "Kconfig.virtio_console" diff --git a/drivers/serial/Kconfig.tiva_c b/drivers/serial/Kconfig.tiva_c new file mode 100644 index 000000000000..df8a559c3d41 --- /dev/null +++ b/drivers/serial/Kconfig.tiva_c @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +config UART_TIVA_C + bool "TI Tiva C Series UART driver" + default y + depends on DT_HAS_TI_TIVA_C_UART_ENABLED + select SERIAL_HAS_DRIVER + select USE_TIVAWARE_UART + select USE_TIVAWARE_SYSCTL + select USE_TIVAWARE_GPIO + select PINCTRL + help + Enable the UART driver for the TI Tiva C Series + (TM4C123G, TM4C129x) microcontrollers. + Uses TivaWare HAL (driverlib/uart.c) for configuration. diff --git a/drivers/serial/uart_tiva_c.c b/drivers/serial/uart_tiva_c.c new file mode 100644 index 000000000000..4ba63c2367c0 --- /dev/null +++ b/drivers/serial/uart_tiva_c.c @@ -0,0 +1,137 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +/* uart_tiva_c.c - TI Tiva C Series UART driver */ + +#define DT_DRV_COMPAT ti_tiva_c_uart + +/** + * UART driver for TI Tiva C Series (TM4C123G / TM4C129x) + * + * This driver consumes the TivaWare HAL (driverlib/uart.c) for + * configuration, baud-rate setup, and data transfer. Clock gating + * is performed via TivaWare SysCtlPeripheralEnable(). + */ + +#include +#include +#include +#include + +/* TivaWare HAL headers */ +#include +#include + +/* + * SYSCTL_PERIPH_UARTn values from sysctl.h follow the pattern: + * SYSCTL_PERIPH_UART0 = 0xf0001800, UART1 = 0xf0001801, etc. + * We derive the peripheral ID from the UART index. + */ +#define TIVA_C_SYSCTL_PERIPH_UART(idx) (SYSCTL_PERIPH_UART0 + (idx)) + +/* + * Bounded retry count while waiting for a UART peripheral to become ready. + */ +#define TIVA_C_UART_READY_RETRIES 10000U + + +struct uart_tiva_c_config { + uint32_t base; /* UART base address (e.g. UART0_BASE) */ + uint32_t sys_clk_freq; + uint8_t uart_index; /* 0-7 */ + const struct pinctrl_dev_config *pcfg; +}; + +struct uart_tiva_c_data { + uint32_t baud_rate; +}; + +static int uart_tiva_c_init(const struct device *dev) +{ + const struct uart_tiva_c_config *cfg = dev->config; + struct uart_tiva_c_data *data = dev->data; + uint32_t retries = TIVA_C_UART_READY_RETRIES; + int ret; + + /* Enable the UART peripheral clock via TivaWare */ + SysCtlPeripheralEnable(TIVA_C_SYSCTL_PERIPH_UART(cfg->uart_index)); + + /* Wait for peripheral to be ready */ + while (!SysCtlPeripheralReady(TIVA_C_SYSCTL_PERIPH_UART(cfg->uart_index))) { + if (retries-- == 0U) { + return -ETIMEDOUT; + } + } + + /* Apply pin configuration from device tree */ + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; + } + + /* Configure UART: baud rate, 8-N-1 */ + UARTConfigSetExpClk(cfg->base, cfg->sys_clk_freq, + data->baud_rate, + (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | + UART_CONFIG_PAR_NONE)); + + UARTEnable(cfg->base); + + return 0; +} + +static int uart_tiva_c_poll_in(const struct device *dev, unsigned char *c) +{ + const struct uart_tiva_c_config *cfg = dev->config; + + if (!UARTCharsAvail(cfg->base)) { + return -1; + } + + *c = (unsigned char)UARTCharGetNonBlocking(cfg->base); + return 0; +} + +static void uart_tiva_c_poll_out(const struct device *dev, unsigned char c) +{ + const struct uart_tiva_c_config *cfg = dev->config; + + UARTCharPut(cfg->base, c); +} + + +static DEVICE_API(uart, uart_tiva_c_driver_api) = { + .poll_in = uart_tiva_c_poll_in, + .poll_out = uart_tiva_c_poll_out, +}; + +/* Device instantiation macros */ + +#define TIVA_C_UART_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static const struct uart_tiva_c_config uart_tiva_c_cfg_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .sys_clk_freq = DT_PROP(DT_INST_CLOCKS_CTLR(n), clock_frequency),\ + .uart_index = DT_INST_PROP(n, peripheral_id), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + \ + static struct uart_tiva_c_data uart_tiva_c_data_##n = { \ + .baud_rate = DT_INST_PROP(n, current_speed), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + uart_tiva_c_init, \ + NULL, \ + &uart_tiva_c_data_##n, \ + &uart_tiva_c_cfg_##n, \ + PRE_KERNEL_1, \ + CONFIG_SERIAL_INIT_PRIORITY, \ + &uart_tiva_c_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(TIVA_C_UART_INIT) From 2fcf97d1a364f6106c7bf0d877a051f4982b54b9 Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:44:02 +0530 Subject: [PATCH 007/455] dts: arm: ti: Add DTSI for TI Tiva C Series SoC Add DTSI for TI Tiva C Series SoC. Signed-off-by: Sri Surya --- dts/arm/ti/tm4c123gh6pm.dtsi | 140 +++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 dts/arm/ti/tm4c123gh6pm.dtsi diff --git a/dts/arm/ti/tm4c123gh6pm.dtsi b/dts/arm/ti/tm4c123gh6pm.dtsi new file mode 100644 index 000000000000..7b68a698bf84 --- /dev/null +++ b/dts/arm/ti/tm4c123gh6pm.dtsi @@ -0,0 +1,140 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +/* + * Device Tree Source include for TI TM4C123GH6PM (Tiva C Series) + * + * ARM Cortex-M4F, 80 MHz, 256 KB Flash, 32 KB SRAM + * 8x UART, 4x SSI, 4x I2C, 2x CAN, USB, 12-bit ADC, etc. + */ + +#include + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-m4f"; + reg = <0>; + }; + }; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 (32 * 1024)>; + }; + + sysclk: system-clock { + compatible = "fixed-clock"; + clock-frequency = <80000000>; + #clock-cells = <0>; + }; + + soc { + pinctrl: pin-controller@400fe000 { + compatible = "ti,tiva-c-pinctrl"; + reg = <0x400fe000 0x1000>; + }; + + flash-controller@400fd000 { + compatible = "ti,stellaris-flash-controller"; + reg = <0x400fd000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x00000000 (256 * 1024)>; + erase-block-size = <1024>; + write-block-size = <4>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 (256 * 1024)>; + }; + }; + + uart0: uart@4000c000 { + compatible = "ti,tiva-c-uart"; + reg = <0x4000c000 0x1000>; + clocks = <&sysclk>; + interrupts = <5 3>; + peripheral-id = <0>; + status = "disabled"; + }; + + uart1: uart@4000d000 { + compatible = "ti,tiva-c-uart"; + reg = <0x4000d000 0x1000>; + clocks = <&sysclk>; + interrupts = <6 3>; + peripheral-id = <1>; + status = "disabled"; + }; + + uart2: uart@4000e000 { + compatible = "ti,tiva-c-uart"; + reg = <0x4000e000 0x1000>; + clocks = <&sysclk>; + interrupts = <33 3>; + peripheral-id = <2>; + status = "disabled"; + }; + + uart3: uart@4000f000 { + compatible = "ti,tiva-c-uart"; + reg = <0x4000f000 0x1000>; + clocks = <&sysclk>; + interrupts = <59 3>; + peripheral-id = <3>; + status = "disabled"; + }; + + uart4: uart@40010000 { + compatible = "ti,tiva-c-uart"; + reg = <0x40010000 0x1000>; + clocks = <&sysclk>; + interrupts = <60 3>; + peripheral-id = <4>; + status = "disabled"; + }; + + uart5: uart@40011000 { + compatible = "ti,tiva-c-uart"; + reg = <0x40011000 0x1000>; + clocks = <&sysclk>; + interrupts = <61 3>; + peripheral-id = <5>; + status = "disabled"; + }; + + uart6: uart@40012000 { + compatible = "ti,tiva-c-uart"; + reg = <0x40012000 0x1000>; + clocks = <&sysclk>; + interrupts = <62 3>; + peripheral-id = <6>; + status = "disabled"; + }; + + uart7: uart@40013000 { + compatible = "ti,tiva-c-uart"; + reg = <0x40013000 0x1000>; + clocks = <&sysclk>; + interrupts = <63 3>; + peripheral-id = <7>; + status = "disabled"; + }; + }; +}; + +&nvic { + arm,num-irq-priority-bits = <3>; +}; From 594a038808cd65206e1526499fa719ae1a8597a8 Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:44:10 +0530 Subject: [PATCH 008/455] soc: ti: Add support for TI Tiva C Series Soc Add support for TI Tiva C Series Soc. Signed-off-by: Sri Surya --- modules/Kconfig | 1 + modules/Kconfig.tiva_c | 103 +++++++++++++++++++++++ soc/ti/tiva_c/CMakeLists.txt | 9 ++ soc/ti/tiva_c/Kconfig | 10 +++ soc/ti/tiva_c/Kconfig.defconfig | 12 +++ soc/ti/tiva_c/Kconfig.soc | 12 +++ soc/ti/tiva_c/soc.yml | 6 ++ soc/ti/tiva_c/tm4c123x/CMakeLists.txt | 8 ++ soc/ti/tiva_c/tm4c123x/Kconfig | 17 ++++ soc/ti/tiva_c/tm4c123x/Kconfig.defconfig | 16 ++++ soc/ti/tiva_c/tm4c123x/Kconfig.soc | 23 +++++ soc/ti/tiva_c/tm4c123x/pinctrl_soc.h | 33 ++++++++ soc/ti/tiva_c/tm4c123x/soc.c | 23 +++++ soc/ti/tiva_c/tm4c123x/soc.h | 20 +++++ 14 files changed, 293 insertions(+) create mode 100644 modules/Kconfig.tiva_c create mode 100644 soc/ti/tiva_c/CMakeLists.txt create mode 100644 soc/ti/tiva_c/Kconfig create mode 100644 soc/ti/tiva_c/Kconfig.defconfig create mode 100644 soc/ti/tiva_c/Kconfig.soc create mode 100644 soc/ti/tiva_c/soc.yml create mode 100644 soc/ti/tiva_c/tm4c123x/CMakeLists.txt create mode 100644 soc/ti/tiva_c/tm4c123x/Kconfig create mode 100644 soc/ti/tiva_c/tm4c123x/Kconfig.defconfig create mode 100644 soc/ti/tiva_c/tm4c123x/Kconfig.soc create mode 100644 soc/ti/tiva_c/tm4c123x/pinctrl_soc.h create mode 100644 soc/ti/tiva_c/tm4c123x/soc.c create mode 100644 soc/ti/tiva_c/tm4c123x/soc.h diff --git a/modules/Kconfig b/modules/Kconfig index c8f62107b140..0648e533ece5 100644 --- a/modules/Kconfig +++ b/modules/Kconfig @@ -40,6 +40,7 @@ source "modules/Kconfig.simplelink" source "modules/Kconfig.stm32" source "modules/Kconfig.syst" source "modules/Kconfig.telink" +source "modules/Kconfig.tiva_c" source "modules/Kconfig.vega" source "modules/Kconfig.wurthelektronik" source "modules/Kconfig.xtensa" diff --git a/modules/Kconfig.tiva_c b/modules/Kconfig.tiva_c new file mode 100644 index 000000000000..59892fe06394 --- /dev/null +++ b/modules/Kconfig.tiva_c @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +config HAS_TIVAWARE + bool + +config USE_TIVAWARE_ADC + bool + +config USE_TIVAWARE_AES + bool + +config USE_TIVAWARE_CAN + bool + +config USE_TIVAWARE_COMP + bool + +config USE_TIVAWARE_CPU + bool + +config USE_TIVAWARE_CRC + bool + +config USE_TIVAWARE_DES + bool + +config USE_TIVAWARE_EEPROM + bool + +config USE_TIVAWARE_EMAC + bool + +config USE_TIVAWARE_EPI + bool + +config USE_TIVAWARE_FLASH + bool + +config USE_TIVAWARE_FPU + bool + +config USE_TIVAWARE_GPIO + bool + +config USE_TIVAWARE_HIBERNATE + bool + +config USE_TIVAWARE_I2C + bool + +config USE_TIVAWARE_INTERRUPT + bool + +config USE_TIVAWARE_LCD + bool + +config USE_TIVAWARE_MPU + bool + +config USE_TIVAWARE_ONEWIRE + bool + +config USE_TIVAWARE_PWM + bool + +config USE_TIVAWARE_QEI + bool + +config USE_TIVAWARE_SHAMD5 + bool + +config USE_TIVAWARE_SSI + bool + +config USE_TIVAWARE_SW_CRC + bool + +config USE_TIVAWARE_SYSCTL + bool + +config USE_TIVAWARE_SYSEXC + bool + +config USE_TIVAWARE_SYSTICK + bool + +config USE_TIVAWARE_TIMER + bool + +config USE_TIVAWARE_UART + bool + +config USE_TIVAWARE_UDMA + bool + +config USE_TIVAWARE_USB + bool + +config USE_TIVAWARE_WATCHDOG + bool diff --git a/soc/ti/tiva_c/CMakeLists.txt b/soc/ti/tiva_c/CMakeLists.txt new file mode 100644 index 000000000000..6a5608cd0354 --- /dev/null +++ b/soc/ti/tiva_c/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + + +add_subdirectory(${SOC_SERIES}) + +set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/ti/tiva_c/Kconfig b/soc/ti/tiva_c/Kconfig new file mode 100644 index 000000000000..d69279449782 --- /dev/null +++ b/soc/ti/tiva_c/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +if SOC_FAMILY_TI_TIVA_C + +rsource "*/Kconfig" + +endif # SOC_FAMILY_TI_TIVA_C diff --git a/soc/ti/tiva_c/Kconfig.defconfig b/soc/ti/tiva_c/Kconfig.defconfig new file mode 100644 index 000000000000..56ecad6d674a --- /dev/null +++ b/soc/ti/tiva_c/Kconfig.defconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +# TI Tiva C Series platform configuration defaults + +if SOC_FAMILY_TI_TIVA_C + +rsource "*/Kconfig.defconfig" + +endif # SOC_FAMILY_TI_TIVA_C diff --git a/soc/ti/tiva_c/Kconfig.soc b/soc/ti/tiva_c/Kconfig.soc new file mode 100644 index 000000000000..a3c0a891554d --- /dev/null +++ b/soc/ti/tiva_c/Kconfig.soc @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +config SOC_FAMILY_TI_TIVA_C + bool + +config SOC_FAMILY + default "ti_tiva_c" if SOC_FAMILY_TI_TIVA_C + +rsource "*/Kconfig.soc" diff --git a/soc/ti/tiva_c/soc.yml b/soc/ti/tiva_c/soc.yml new file mode 100644 index 000000000000..2bdbd9204512 --- /dev/null +++ b/soc/ti/tiva_c/soc.yml @@ -0,0 +1,6 @@ +family: +- name: ti_tiva_c + series: + - name: tm4c123x + socs: + - name: ti_tm4c123gh6pm diff --git a/soc/ti/tiva_c/tm4c123x/CMakeLists.txt b/soc/ti/tiva_c/tm4c123x/CMakeLists.txt new file mode 100644 index 000000000000..a5083aa25472 --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +zephyr_library_sources(soc.c) + +zephyr_include_directories(.) diff --git a/soc/ti/tiva_c/tm4c123x/Kconfig b/soc/ti/tiva_c/tm4c123x/Kconfig new file mode 100644 index 000000000000..7d26308d987c --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +# TI Tiva C TM4C123x Series + +config SOC_SERIES_TM4C123X + select ARM + select CPU_CORTEX_M4 + select CPU_CORTEX_M_HAS_DWT + select CPU_HAS_ARM_MPU + select CPU_HAS_FPU + select PINCTRL + select HAS_TIVAWARE + select BUILD_OUTPUT_HEX + select SOC_EARLY_INIT_HOOK diff --git a/soc/ti/tiva_c/tm4c123x/Kconfig.defconfig b/soc/ti/tiva_c/tm4c123x/Kconfig.defconfig new file mode 100644 index 000000000000..554514e46d83 --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/Kconfig.defconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +# TI Tiva C TM4C123x Series defaults + +if SOC_SERIES_TM4C123X + +config NUM_IRQS + default 139 + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default $(dt_node_int_prop_int,$(dt_nodelabel_path,sysclk),clock-frequency) + +endif # SOC_SERIES_TM4C123X diff --git a/soc/ti/tiva_c/tm4c123x/Kconfig.soc b/soc/ti/tiva_c/tm4c123x/Kconfig.soc new file mode 100644 index 000000000000..03226ca85380 --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/Kconfig.soc @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +# TI Tiva C TM4C123x Series + +config SOC_SERIES_TM4C123X + bool + select SOC_FAMILY_TI_TIVA_C + +config SOC_SERIES + default "tm4c123x" if SOC_SERIES_TM4C123X + +config SOC_TI_TM4C123GH6PM + bool + select SOC_SERIES_TM4C123X + help + TI Tiva C Series TM4C123GH6PM (ARM Cortex-M4F, 80 MHz, + 256 KB Flash, 32 KB SRAM) + +config SOC + default "ti_tm4c123gh6pm" if SOC_TI_TM4C123GH6PM diff --git a/soc/ti/tiva_c/tm4c123x/pinctrl_soc.h b/soc/ti/tiva_c/tm4c123x/pinctrl_soc.h new file mode 100644 index 000000000000..0a3b57783dea --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/pinctrl_soc.h @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +#ifndef _SOC_TIVA_C_PINCTRL_SOC_H_ +#define _SOC_TIVA_C_PINCTRL_SOC_H_ + +#include +#include +#include + +typedef struct pinctrl_soc_pin { + uint32_t pinmux; +} pinctrl_soc_pin_t; + +#define Z_PINCTRL_STATE_PIN_INIT(node_id, prop, idx) \ + {.pinmux = (DT_PROP(DT_PROP_BY_IDX(node_id, prop, idx), pinmux) | \ + (DT_PROP(DT_PROP_BY_IDX(node_id, prop, idx), bias_pull_up) \ + << TIVA_C_PULL_UP_SHIFT) | \ + (DT_PROP(DT_PROP_BY_IDX(node_id, prop, idx), bias_pull_down) \ + << TIVA_C_PULL_DOWN_SHIFT) | \ + (DT_PROP(DT_PROP_BY_IDX(node_id, prop, idx), drive_open_drain) \ + << TIVA_C_OPEN_DRAIN_SHIFT))}, + +#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \ + { \ + DT_FOREACH_PROP_ELEM(node_id, prop, Z_PINCTRL_STATE_PIN_INIT) \ + } + +#endif /* _SOC_TIVA_C_PINCTRL_SOC_H_ */ diff --git a/soc/ti/tiva_c/tm4c123x/soc.c b/soc/ti/tiva_c/tm4c123x/soc.c new file mode 100644 index 000000000000..9372bcbc0fa4 --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/soc.c @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +/* + * SoC initialization for the TI Tiva C TM4C123x Series + * + * Configures the PLL to run at 80 MHz from the 16 MHz main oscillator. + */ + +#include + +#include "soc.h" + +void soc_early_init_hook(void) +{ + /* Configure PLL: 80 MHz from 16 MHz crystal */ + SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | + SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); +} diff --git a/soc/ti/tiva_c/tm4c123x/soc.h b/soc/ti/tiva_c/tm4c123x/soc.h new file mode 100644 index 000000000000..f0eb0a5e7774 --- /dev/null +++ b/soc/ti/tiva_c/tm4c123x/soc.h @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +/** + * SoC header for the TI Tiva C TM4C123x Series + * + * IRQ numbers and system control register addresses for TM4C123x. + */ + +#ifndef _SOC_TM4C123X_H_ +#define _SOC_TM4C123X_H_ + +#include +#include + +#endif /* _SOC_TM4C123X_H_ */ From 9e8b6a8ac8ace763c35b2d4886ffc5d1a447a85e Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:44:16 +0530 Subject: [PATCH 009/455] boards: ti: Add support for TI Tiva C tm4c123gxl board Add support for TI Tiva C tm4c123gxl board. Signed-off-by: Sri Surya --- boards/ti/tm4c123gxl/Kconfig.tm4c123gxl | 7 + boards/ti/tm4c123gxl/board.cmake | 10 ++ boards/ti/tm4c123gxl/board.yml | 6 + boards/ti/tm4c123gxl/doc/img/tm4c123gxl.webp | Bin 0 -> 36918 bytes boards/ti/tm4c123gxl/doc/index.rst | 148 +++++++++++++++++++ boards/ti/tm4c123gxl/support/openocd.cfg | 6 + boards/ti/tm4c123gxl/tm4c123gxl.dts | 61 ++++++++ boards/ti/tm4c123gxl/tm4c123gxl.yaml | 14 ++ boards/ti/tm4c123gxl/tm4c123gxl_defconfig | 9 ++ 9 files changed, 261 insertions(+) create mode 100644 boards/ti/tm4c123gxl/Kconfig.tm4c123gxl create mode 100644 boards/ti/tm4c123gxl/board.cmake create mode 100644 boards/ti/tm4c123gxl/board.yml create mode 100644 boards/ti/tm4c123gxl/doc/img/tm4c123gxl.webp create mode 100644 boards/ti/tm4c123gxl/doc/index.rst create mode 100644 boards/ti/tm4c123gxl/support/openocd.cfg create mode 100644 boards/ti/tm4c123gxl/tm4c123gxl.dts create mode 100644 boards/ti/tm4c123gxl/tm4c123gxl.yaml create mode 100644 boards/ti/tm4c123gxl/tm4c123gxl_defconfig diff --git a/boards/ti/tm4c123gxl/Kconfig.tm4c123gxl b/boards/ti/tm4c123gxl/Kconfig.tm4c123gxl new file mode 100644 index 000000000000..6e87667e471e --- /dev/null +++ b/boards/ti/tm4c123gxl/Kconfig.tm4c123gxl @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +config BOARD_TM4C123GXL + select SOC_TI_TM4C123GH6PM diff --git a/boards/ti/tm4c123gxl/board.cmake b/boards/ti/tm4c123gxl/board.cmake new file mode 100644 index 000000000000..2271bf751687 --- /dev/null +++ b/boards/ti/tm4c123gxl/board.cmake @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +# TI Tiva C Series TM4C123GXL LaunchPad board configuration + +board_runner_args(openocd "--config=${BOARD_DIR}/support/openocd.cfg") + +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) diff --git a/boards/ti/tm4c123gxl/board.yml b/boards/ti/tm4c123gxl/board.yml new file mode 100644 index 000000000000..268c4407ebae --- /dev/null +++ b/boards/ti/tm4c123gxl/board.yml @@ -0,0 +1,6 @@ +board: + name: tm4c123gxl + full_name: TI Tiva C Series TM4C123G LaunchPad + vendor: ti + socs: + - name: ti_tm4c123gh6pm diff --git a/boards/ti/tm4c123gxl/doc/img/tm4c123gxl.webp b/boards/ti/tm4c123gxl/doc/img/tm4c123gxl.webp new file mode 100644 index 0000000000000000000000000000000000000000..35f8e1613d958a643face1f48dab045131d99ee9 GIT binary patch literal 36918 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 Sz!&_4rMBExdYW>!pa1|fG|5i@ literal 0 HcmV?d00001 diff --git a/boards/ti/tm4c123gxl/doc/index.rst b/boards/ti/tm4c123gxl/doc/index.rst new file mode 100644 index 000000000000..3e1a6bafd518 --- /dev/null +++ b/boards/ti/tm4c123gxl/doc/index.rst @@ -0,0 +1,148 @@ +.. zephyr:board:: tm4c123gxl + +Overview +******** + +The TM4C123GXL LaunchPad Evaluation Kit is a low-cost development platform for +TI Tiva C Series TM4C123GH6PM microcontrollers. It features a high-performance +ARM Cortex-M4F core running at 80 MHz with 256 KB flash and 32 KB SRAM. + +The TM4C123GH6PM microcontroller includes: + +* Core. + + * ARM Cortex-M4F with FPU, 80 MHz. + + * Nested Vectored Interrupt Controller (NVIC). + + * Memory Protection Unit (MPU). + +* Memory. + + * 256 KB single-cycle flash (up to 40 MHz). + + * 32 KB single-cycle SRAM. + + * 2 KB EEPROM. + +* Communication. + + * Eight UARTs. + + * Four SSI/SPI modules. + + * Four I2C modules. + + * Two CAN 2.0 A/B controllers. + + * USB 2.0 OTG/Host/Device (Full-Speed). + +* Timers. + + * Six 64-bit general-purpose timers (twelve 32-bit). + + * Six wide timers. + + * Two watchdog timers. + +* Other. + + * 16 PWM outputs. + + * System control and clocks with PLL. + +.. image:: img/tm4c123gxl.webp + :align: center + :alt: TM4C123GXL LaunchPad development board + +Hardware +******** + +The TM4C123GXL LaunchPad features: + +- On-board In-Circuit Debug Interface (ICDI) for programming and debugging +- USB Micro-B connector for debug and power +- Two user switches (SW1 on PF4, SW2 on PF0) +- RGB LED (Red on PF1, Blue on PF2, Green on PF3) +- Reset switch +- Boosterpack-compatible headers exposing most MCU pins + +Details on the TM4C123GXL LaunchPad can be found on the +`TI TM4C123GXL Product Page`_. + +.. _TI TM4C123GXL Product Page: + https://www.ti.com/tool/EK-TM4C123GXL + +Supported Features +================== + +.. zephyr:board-supported-hw:: + +Connections and IOs +=================== + +UART +---- + +The TM4C123GXL has 8 UART modules. UART0 is connected to the on-board ICDI +virtual COM port via PA0 (RX) and PA1 (TX). + ++-------+----------+----------+ +| UART | RX Pin | TX Pin | ++=======+==========+==========+ +| UART0 | PA0 | PA1 | ++-------+----------+----------+ +| UART1 | PB0 | PB1 | ++-------+----------+----------+ + +Building and Flashing +********************* + +Building +======== + +Follow the :ref:`getting_started` instructions for Zephyr application development. + +For example, to build the Hello World application for the TM4C123GXL LaunchPad: + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: tm4c123gxl + :goals: build + +Flashing +======== + +The TM4C123GXL uses the on-board TI ICDI interface for flashing via OpenOCD. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: tm4c123gxl + :goals: flash + +Debugging +========= + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: tm4c123gxl + :goals: debug + +Console +======= + +The UART0 console is available through the on-board ICDI virtual COM port. +Connect to the serial port at **115200 8N1**. + +On Linux: + +.. code-block:: console + + $ screen /dev/ttyACM0 115200 + +References +********** + +- `TM4C123GH6PM Datasheet `_ +- `TM4C123GXL LaunchPad User Guide `_ +- `TivaWare Peripheral Driver Library `_ diff --git a/boards/ti/tm4c123gxl/support/openocd.cfg b/boards/ti/tm4c123gxl/support/openocd.cfg new file mode 100644 index 000000000000..eebd6f2a1caf --- /dev/null +++ b/boards/ti/tm4c123gxl/support/openocd.cfg @@ -0,0 +1,6 @@ +# OpenOCD configuration for TI TM4C123GXL LaunchPad +# Uses the on-board TI ICDI (In-Circuit Debug Interface) + +source [find interface/ti-icdi.cfg] +transport select hla_jtag +source [find target/stellaris.cfg] diff --git a/boards/ti/tm4c123gxl/tm4c123gxl.dts b/boards/ti/tm4c123gxl/tm4c123gxl.dts new file mode 100644 index 000000000000..0c272f55716e --- /dev/null +++ b/boards/ti/tm4c123gxl/tm4c123gxl.dts @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 Linumiz + * Author: Sri Surya + */ + +/dts-v1/; + +#include +#include + +/* + * Pin configuration nodes for UART0 and UART1. + * Use TIVA_C_PINMUX(port, pin, mux, type) macro. + */ +&pinctrl { + uart0_rx_pa0: uart0_rx_pa0 { + pinmux = ; + }; + + uart0_tx_pa1: uart0_tx_pa1 { + pinmux = ; + }; + + uart1_rx_pb0: uart1_rx_pb0 { + pinmux = ; + }; + + uart1_tx_pb1: uart1_tx_pb1 { + pinmux = ; + }; +}; + +/ { + model = "TI Tiva C Series TM4C123GXL LaunchPad"; + compatible = "ti,tm4c123gxl"; + + chosen { + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + }; +}; + +/* UART0 is connected to the on-board ICDI virtual COM port (PA0/PA1) */ +&uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_rx_pa0 &uart0_tx_pa1>; + pinctrl-names = "default"; +}; + +/* UART1 on header pins PB0/PB1 */ +&uart1 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart1_rx_pb0 &uart1_tx_pb1>; + pinctrl-names = "default"; +}; diff --git a/boards/ti/tm4c123gxl/tm4c123gxl.yaml b/boards/ti/tm4c123gxl/tm4c123gxl.yaml new file mode 100644 index 000000000000..6d65f4081c2f --- /dev/null +++ b/boards/ti/tm4c123gxl/tm4c123gxl.yaml @@ -0,0 +1,14 @@ +identifier: tm4c123gxl +name: TI Tiva C Series TM4C123GXL LaunchPad +type: mcu +arch: arm +ram: 32 +flash: 256 +toolchain: + - zephyr + - gnuarmemb + - xtools +supported: + - pinctrl + - uart +vendor: ti diff --git a/boards/ti/tm4c123gxl/tm4c123gxl_defconfig b/boards/ti/tm4c123gxl/tm4c123gxl_defconfig new file mode 100644 index 000000000000..7c2a9ebce350 --- /dev/null +++ b/boards/ti/tm4c123gxl/tm4c123gxl_defconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2026 Linumiz +# Author: Sri Surya + +# Default configuration for TI TM4C123GXL LaunchPad +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y From 9c9b34021e32f8de02b7bcb77d89f0c6fa4d81b4 Mon Sep 17 00:00:00 2001 From: Sri Surya Date: Mon, 24 Aug 2026 12:44:23 +0530 Subject: [PATCH 010/455] MAINTAINERS: Add maintainers for TI Tiva C Platforms Add maintainers for TI Tiva C Platforms Signed-off-by: Sri Surya --- MAINTAINERS.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index c79397e76053..2de5d7dddd3b 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -6218,6 +6218,21 @@ TI Stellaris Platforms: labels: - "platform: TI Stellaris" +TI Tiva C Platforms: + status: maintained + maintainers: + - srisurya1 + files: + - soc/ti/tiva_c/ + - boards/ti/tm4c123gxl/ + - dts/arm/ti/tm4c* + - dts/bindings/*/*tiva-c* + - drivers/*/*tiva_c* + - include/zephyr/dt-bindings/pinctrl/tiva-c* + - modules/Kconfig.tiva_c + labels: + - "platform: TI Tiva C" + Task Watchdog: status: maintained maintainers: From 17164c160eaf8f24b0a4a09bdf4166500090615b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 18 Jul 2026 08:20:30 +0000 Subject: [PATCH 011/455] drivers: tee: adopt driver_ops Doxygen convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group the TEE driver backend definitions in a dedicated backend API group and document the driver API structure with the driver_ops Doxygen commands, tagging each operation as optional. Also fix a stale reference to tee_version_get() in the callback typedef doc. Signed-off-by: Benjamin Cabé --- include/zephyr/drivers/tee.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/include/zephyr/drivers/tee.h b/include/zephyr/drivers/tee.h index 4bfc83d1c55c..742aee0bf91a 100644 --- a/include/zephyr/drivers/tee.h +++ b/include/zephyr/drivers/tee.h @@ -256,11 +256,16 @@ struct tee_shm { uint32_t flags; /**< [out] shared buffer flags */ }; +/** + * @def_driverbackendgroup{TEE,tee_interface} + * @{ + */ + /** * * @brief Callback API to get current tee version * - * See @a tee_version_get() for argument definitions. + * See @a tee_get_version() for argument definitions. */ typedef int (*tee_get_version_t)(const struct device *dev, struct tee_version_info *info); @@ -331,18 +336,32 @@ typedef int (*tee_suppl_recv_t)(const struct device *dev, uint32_t *func, unsign typedef int (*tee_suppl_send_t)(const struct device *dev, unsigned int ret, unsigned int num_params, struct tee_param *param); +/** + * @driver_ops{TEE} + */ __subsystem struct tee_driver_api { + /** @driver_ops_optional @copybrief tee_get_version */ tee_get_version_t get_version; + /** @driver_ops_optional @copybrief tee_open_session */ tee_open_session_t open_session; + /** @driver_ops_optional @copybrief tee_close_session */ tee_close_session_t close_session; + /** @driver_ops_optional @copybrief tee_cancel */ tee_cancel_t cancel; + /** @driver_ops_optional @copybrief tee_invoke_func */ tee_invoke_func_t invoke_func; + /** @driver_ops_optional @copybrief tee_shm_register */ tee_shm_register_t shm_register; + /** @driver_ops_optional @copybrief tee_shm_unregister */ tee_shm_unregister_t shm_unregister; + /** @driver_ops_optional @copybrief tee_suppl_recv */ tee_suppl_recv_t suppl_recv; + /** @driver_ops_optional @copybrief tee_suppl_send */ tee_suppl_send_t suppl_send; }; +/** @} */ + /** * @brief Get the current TEE version info * From 13c6f3432012f48dbe90bf8be64954e6f226709e Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Mon, 3 Aug 2026 22:50:43 +0600 Subject: [PATCH 012/455] authentication: fido2: implement changePIN Implement changePIN subprotocol for clientPIN Signed-off-by: Siratul Islam --- .../zephyr/authentication/fido2/fido2_types.h | 21 +++ subsys/authentication/fido2/fido2_cbor.h | 19 +-- subsys/authentication/fido2/fido2_clientpin.c | 133 ++++++++++++++++++ subsys/authentication/fido2/fido2_clientpin.h | 31 ++-- subsys/authentication/fido2/fido2_core.c | 12 +- 5 files changed, 189 insertions(+), 27 deletions(-) diff --git a/include/zephyr/authentication/fido2/fido2_types.h b/include/zephyr/authentication/fido2/fido2_types.h index de1359c84e5f..cfd17952a6b1 100644 --- a/include/zephyr/authentication/fido2/fido2_types.h +++ b/include/zephyr/authentication/fido2/fido2_types.h @@ -55,6 +55,27 @@ extern "C" { /** @brief PIN hash size */ #define FIDO2_PIN_HASH_SIZE 16 +/** @brief Maximum encrypted PIN hash size */ +#define FIDO2_PIN_HASH_ENC_MAX_SIZE 32 + +/** @brief Maximum encrypted PIN size */ +#define FIDO2_PIN_ENC_MAX_SIZE 80 + +/** @brief PIN Protocol 1 auth param size */ +#define FIDO2_PIN_AUTH_SIZE_P1 16 + +/** @brief PIN Protocol 2 auth param size */ +#define FIDO2_PIN_AUTH_SIZE_P2 32 + +/** @brief Maximum PIN auth param size */ +#define FIDO2_PIN_AUTH_MAX_SIZE 32 + +/** @brief Padded PIN size */ +#define FIDO2_PIN_PADDED_SIZE 64 + +/** @brief Encrypted PIN token size */ +#define FIDO2_PIN_TOKEN_ENC_MAX_SIZE 48 + /** @brief Size of a discoverable credential ID */ #define FIDO2_DISCOVERABLE_CRED_ID_SIZE 32 diff --git a/subsys/authentication/fido2/fido2_cbor.h b/subsys/authentication/fido2/fido2_cbor.h index 87994eb765d2..313dfae6ec91 100644 --- a/subsys/authentication/fido2/fido2_cbor.h +++ b/subsys/authentication/fido2/fido2_cbor.h @@ -13,15 +13,6 @@ /** Maximum number of algorithms in pubKeyCredParams */ #define FIDO2_MAX_ALGORITHMS 8 -/** Maximum PIN auth param size across all supported protocols */ -#define FIDO2_CBOR_PIN_AUTH_MAX_SIZE 32 - -/** Maximum encrypted PIN size */ -#define FIDO2_CBOR_PIN_ENC_MAX_SIZE 80 - -/** Encrypted PIN hash size */ -#define FIDO2_CBOR_PIN_HASH_ENC_SIZE 32 - /** Number of attestation statement format identifiers */ #define FIDO2_CBOR_MAX_ATTESTATION_FORMATS 4 @@ -77,7 +68,7 @@ struct fido2_make_credential_params { /** Whether options.uv was present */ bool has_uv_option; /** 0x08: pinUvAuthParam */ - uint8_t pin_uv_auth_param[FIDO2_CBOR_PIN_AUTH_MAX_SIZE]; + uint8_t pin_uv_auth_param[FIDO2_PIN_AUTH_MAX_SIZE]; /** Length of pinUvAuthParam (0 = probe, 16 = P1, 32 = P2) */ size_t pin_uv_auth_param_len; /** Whether pinUvAuthParam key was present */ @@ -120,7 +111,7 @@ struct fido2_get_assertion_params { /** Whether options.uv was present */ bool has_uv_option; /** 0x06: pinUvAuthParam */ - uint8_t pin_uv_auth_param[FIDO2_CBOR_PIN_AUTH_MAX_SIZE]; + uint8_t pin_uv_auth_param[FIDO2_PIN_AUTH_MAX_SIZE]; /** Length of pinUvAuthParam (0 = probe, 16 = P1, 32 = P2) */ size_t pin_uv_auth_param_len; /** Whether pinUvAuthParam key was present */ @@ -157,19 +148,19 @@ struct fido2_client_pin_params { /** Whether keyAgreement was present */ bool has_key_agreement; /** 0x04: pinUvAuthParam */ - uint8_t pin_uv_auth_param[FIDO2_CBOR_PIN_AUTH_MAX_SIZE]; + uint8_t pin_uv_auth_param[FIDO2_PIN_AUTH_MAX_SIZE]; /** Length of pinUvAuthParam */ size_t pin_uv_auth_param_len; /** Whether pinUvAuthParam was present */ bool has_pin_uv_auth_param; /** 0x05: newPinEnc */ - uint8_t new_pin_enc[FIDO2_CBOR_PIN_ENC_MAX_SIZE]; + uint8_t new_pin_enc[FIDO2_PIN_ENC_MAX_SIZE]; /** newPinEnc len */ size_t new_pin_enc_len; /** Whether newPinEnc was present */ bool has_new_pin_enc; /** 0x06: pinHashEnc */ - uint8_t pin_hash_enc[FIDO2_CBOR_PIN_HASH_ENC_SIZE]; + uint8_t pin_hash_enc[FIDO2_PIN_HASH_ENC_MAX_SIZE]; /** pinHashEnc len */ size_t pin_hash_enc_len; /** Whether pinHashEnc was present */ diff --git a/subsys/authentication/fido2/fido2_clientpin.c b/subsys/authentication/fido2/fido2_clientpin.c index e33c32eacff6..02a02655a079 100644 --- a/subsys/authentication/fido2/fido2_clientpin.c +++ b/subsys/authentication/fido2/fido2_clientpin.c @@ -312,6 +312,139 @@ enum fido2_status fido2_clientpin_cmd_set_pin(uint8_t protocol, const uint8_t *p return FIDO2_OK; } +enum fido2_status fido2_clientpin_cmd_change_pin(uint8_t protocol, const uint8_t *platform_key, + size_t platform_key_len, + const uint8_t *pin_hash_enc, + size_t pin_hash_enc_len, + const uint8_t *new_pin_enc, size_t new_pin_enc_len, + const uint8_t *pin_uv_auth_param) +{ + uint8_t pin_enc_w_hash_enc[FIDO2_PIN_ENC_MAX_SIZE + FIDO2_PIN_HASH_ENC_MAX_SIZE]; + uint8_t pin_hash[FIDO2_PIN_HASH_SIZE]; + size_t pin_hash_len; + uint8_t stored_pin_hash[FIDO2_PIN_HASH_SIZE]; + uint8_t padded_pin[FIDO2_PIN_PADDED_SIZE]; + size_t padded_pin_len; + size_t new_pin_len; + uint8_t new_pin_hash[FIDO2_SHA256_SIZE]; + uint8_t retries; + int ret; + + if (!fido2_clientpin_pin_is_set()) { + return FIDO2_ERR_PIN_AUTH_INVALID; + } + + ret = fido2_storage_pin_retries_get(&retries); + if (ret) { + return FIDO2_ERR_OTHER; + } + if (retries == 0) { + return FIDO2_ERR_PIN_BLOCKED; + } + + ret = decapsulate(platform_key, platform_key_len, protocol); + if (ret) { + return FIDO2_ERR_INVALID_PARAMETER; + } + + if (new_pin_enc_len + pin_hash_enc_len > sizeof(pin_enc_w_hash_enc)) { + return FIDO2_ERR_INVALID_PARAMETER; + } + memcpy(pin_enc_w_hash_enc, new_pin_enc, new_pin_enc_len); + memcpy(pin_enc_w_hash_enc + new_pin_enc_len, pin_hash_enc, pin_hash_enc_len); + + ret = verify(shared_secret, pin_enc_w_hash_enc, new_pin_enc_len + pin_hash_enc_len, + pin_uv_auth_param, + protocol == FIDO2_PIN_PROTOCOL_V1 ? FIDO2_PIN_AUTH_SIZE_P1 + : FIDO2_PIN_AUTH_SIZE_P2); + if (ret) { + return FIDO2_ERR_PIN_AUTH_INVALID; + } + + ret = fido2_storage_pin_retries_decrement(); + if (ret) { + return FIDO2_ERR_OTHER; + } + + ret = decrypt(protocol, pin_hash_enc, pin_hash_enc_len, pin_hash, sizeof(pin_hash), + &pin_hash_len); + if (ret) { + return FIDO2_ERR_PIN_AUTH_INVALID; + } + + ret = fido2_storage_pin_get(stored_pin_hash); + if (ret) { + return FIDO2_ERR_OTHER; + } + + if (pin_hash_len != FIDO2_PIN_HASH_SIZE || + memcmp(pin_hash, stored_pin_hash, FIDO2_PIN_HASH_SIZE) != 0) { + ret = generate_key_agreement(); + if (ret) { + return FIDO2_ERR_OTHER; + } + + ret = fido2_storage_pin_retries_get(&retries); + if (ret) { + return FIDO2_ERR_OTHER; + } + + if (retries == 0) { + return FIDO2_ERR_PIN_BLOCKED; + } + + ++consecutive_pin_mismatches; + if (consecutive_pin_mismatches >= 3) { + return FIDO2_ERR_PIN_AUTH_BLOCKED; + } + + return FIDO2_ERR_PIN_INVALID; + } + + consecutive_pin_mismatches = 0; + + ret = fido2_storage_pin_retries_reset(); + if (ret) { + return FIDO2_ERR_OTHER; + } + + ret = decrypt(protocol, new_pin_enc, new_pin_enc_len, padded_pin, sizeof(padded_pin), + &padded_pin_len); + if (ret) { + return FIDO2_ERR_PIN_AUTH_INVALID; + } + + if (padded_pin_len != FIDO2_PIN_PADDED_SIZE) { + return FIDO2_ERR_INVALID_PARAMETER; + } + + /* Remove trailng 0s */ + new_pin_len = strnlen((const char *)padded_pin, FIDO2_PIN_PADDED_SIZE); + if (new_pin_len < CONFIG_FIDO2_MIN_PIN_LENGTH) { + return FIDO2_ERR_PIN_POLICY_VIOLATION; + } + + ret = fido2_crypto_sha256(padded_pin, new_pin_len, new_pin_hash); + if (ret) { + return FIDO2_ERR_OTHER; + } + + ret = fido2_storage_pin_set(new_pin_hash); + if (ret) { + return FIDO2_ERR_OTHER; + } + + /* Reset again because spec says so. */ + ret = fido2_storage_pin_retries_reset(); + if (ret) { + return FIDO2_ERR_OTHER; + } + + reset_pin_uv_auth_token(); + + return FIDO2_OK; +} + enum fido2_status fido2_clientpin_cmd_get_pin_token_pin_w_perms( uint8_t protocol, const uint8_t *platform_key, size_t platform_key_len, const uint8_t *pin_hash_enc, size_t pin_hash_enc_len, uint8_t permissions, diff --git a/subsys/authentication/fido2/fido2_clientpin.h b/subsys/authentication/fido2/fido2_clientpin.h index 6d208d499961..8f4e361c7fef 100644 --- a/subsys/authentication/fido2/fido2_clientpin.h +++ b/subsys/authentication/fido2/fido2_clientpin.h @@ -9,17 +9,6 @@ #include -/** PIN Protocol 1 auth param size */ -#define FIDO2_PIN_AUTH_SIZE_P1 16 -/** PIN Protocol 2 auth param size */ -#define FIDO2_PIN_AUTH_SIZE_P2 32 -/** Maximum PIN auth param size */ -#define FIDO2_PIN_AUTH_MAX_SIZE 32 -/** Padded PIN size */ -#define FIDO2_PIN_PADDED_SIZE 64 -/** Encrypted PIN token size */ -#define FIDO2_PIN_TOKEN_ENC_MAX_SIZE 48 - #define FIDO2_CLIENTPIN_GET_PIN_RETRIES 0x01 /**< getPINRetries */ #define FIDO2_CLIENTPIN_GET_KEY_AGREEMENT 0x02 /**< getKeyAgreement */ #define FIDO2_CLIENTPIN_SET_PIN 0x03 /**< setPIN */ @@ -109,6 +98,26 @@ enum fido2_status fido2_clientpin_cmd_set_pin(uint8_t protocol, const uint8_t *p size_t new_pin_enc_len, const uint8_t *pin_uv_auth_param); +/** + * Handle changePIN. + * + * @param protocol PIN/UV auth protocol version. + * @param platform_key Platform's ECDH public key. + * @param platform_key_len Length of @p platform_key in bytes. + * @param pin_hash_enc Encrypted PIN hash. + * @param pin_hash_enc_len Length of @p pin_hash_enc in bytes. + * @param new_pin_enc Encrypted new pin. + * @param new_pin_enc_len Length of @p new_pin_enc in bytes. + * @param pin_uv_auth_param Result of calling authenticate(shared secret, newPinEnc). + * @return FIDO2_OK on success, FIDO2_ERR_* on failure. + */ +enum fido2_status fido2_clientpin_cmd_change_pin(uint8_t protocol, const uint8_t *platform_key, + size_t platform_key_len, + const uint8_t *pin_hash_enc, + size_t pin_hash_enc_len, + const uint8_t *new_pin_enc, size_t new_pin_enc_len, + const uint8_t *pin_uv_auth_param); + /** * Handle getPinToken / getPinUvAuthTokenUsingPinWithPermissions. * diff --git a/subsys/authentication/fido2/fido2_core.c b/subsys/authentication/fido2/fido2_core.c index 5bdc8101d1e4..5d1ac5e7ec0f 100644 --- a/subsys/authentication/fido2/fido2_core.c +++ b/subsys/authentication/fido2/fido2_core.c @@ -803,8 +803,16 @@ static enum fido2_status handle_client_pin(uint8_t *cbor_in, size_t cbor_in_len, cp_params.key_agreement_len, cp_params.new_pin_enc, cp_params.new_pin_enc_len, cp_params.pin_uv_auth_param); case FIDO2_CLIENTPIN_CHANGE_PIN: - /* TODO */ - return FIDO2_ERR_INVALID_SUBCOMMAND; + if (!cp_params.has_pin_uv_auth_protocol || !cp_params.has_pin_uv_auth_param || + !cp_params.has_new_pin_enc || !cp_params.has_pin_hash_enc || + !cp_params.has_key_agreement) { + return FIDO2_ERR_MISSING_PARAMETER; + } + return fido2_clientpin_cmd_change_pin( + cp_params.pin_uv_auth_protocol, cp_params.key_agreement, + cp_params.key_agreement_len, cp_params.pin_hash_enc, + cp_params.pin_hash_enc_len, cp_params.new_pin_enc, + cp_params.new_pin_enc_len, cp_params.pin_uv_auth_param); case FIDO2_CLIENTPIN_GET_PIN_TOKEN: /* Backwards compatibility */ if (!cp_params.has_pin_uv_auth_protocol || !cp_params.has_key_agreement || !cp_params.has_pin_hash_enc) { From ed8a7182a2f80288c4e3af999a7e22d18bb29303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 1 Aug 2026 00:46:33 +0000 Subject: [PATCH 013/455] drivers: pm_cpu_ops: add @file Doxygen block to psci.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a missing @file block so the header shows up properly in the generated API documentation, and attach it to the CPU power management Doxygen group defined by pm_cpu_ops.h. Assisted-by: Claude:fable-5 Signed-off-by: Benjamin Cabé --- include/zephyr/drivers/pm_cpu_ops/psci.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/zephyr/drivers/pm_cpu_ops/psci.h b/include/zephyr/drivers/pm_cpu_ops/psci.h index 7356adca1258..8fe725072f00 100644 --- a/include/zephyr/drivers/pm_cpu_ops/psci.h +++ b/include/zephyr/drivers/pm_cpu_ops/psci.h @@ -4,6 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** + * @file + * @brief ARM PSCI specific APIs for CPU power management. + * @ingroup power_management_cpu_api + */ + #ifndef ZEPHYR_INCLUDE_DRIVERS_PM_CPU_OPS_PSCI_H_ #define ZEPHYR_INCLUDE_DRIVERS_PM_CPU_OPS_PSCI_H_ From a85a9faf736aeed94accc1456579cb7986dbe39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 1 Aug 2026 02:37:30 +0000 Subject: [PATCH 014/455] drivers: pm_cpu_ops: document PSCI version helpers, hide field masks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document PSCI_VERSION_MAJOR(), PSCI_VERSION_MINOR() and psci_version(), including how the major and minor fields are packed into the returned value. The shift and mask macros those helpers are built from are not used anywhere outside this header, so exclude them from the generated documentation rather than documenting them as API. Assisted-by: Claude:fable-5 Signed-off-by: Benjamin Cabé --- include/zephyr/drivers/pm_cpu_ops/psci.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/zephyr/drivers/pm_cpu_ops/psci.h b/include/zephyr/drivers/pm_cpu_ops/psci.h index 8fe725072f00..c2c1bf4c3e2a 100644 --- a/include/zephyr/drivers/pm_cpu_ops/psci.h +++ b/include/zephyr/drivers/pm_cpu_ops/psci.h @@ -23,16 +23,38 @@ extern "C" { #endif /* PSCI version decoding (independent of PSCI version) */ + +/** @cond INTERNAL_HIDDEN */ #define PSCI_VERSION_MAJOR_SHIFT 16 #define PSCI_VERSION_MINOR_MASK \ ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1) #define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK +/** @endcond */ +/** + * @brief Extract the major version field from a PSCI version value + * + * @param ver PSCI version value + */ #define PSCI_VERSION_MAJOR(ver) \ (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) +/** + * @brief Extract the minor version field from a PSCI version value + * + * @param ver PSCI version value + */ #define PSCI_VERSION_MINOR(ver) \ ((ver) & PSCI_VERSION_MINOR_MASK) +/** + * @brief Get the PSCI firmware version + * + * Returns the version of the detected PSCI firmware, with the major version + * in the upper 16 bits and the minor version in the lower 16 bits. Use + * PSCI_VERSION_MAJOR() and PSCI_VERSION_MINOR() to decode the fields. + * + * @return PSCI firmware version + */ uint32_t psci_version(void); /** From b00f07ca226374c8fb70ffc2eefd9c9837989043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 1 Aug 2026 00:46:45 +0000 Subject: [PATCH 015/455] pm_cpu_ops: attach @file block to Doxygen group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the missing @ingroup line to the existing @file block so the header is attached to the power_management_cpu_api Doxygen group as required by the documentation guidelines. Assisted-by: Claude:fable-5 Signed-off-by: Benjamin Cabé --- include/zephyr/drivers/pm_cpu_ops.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/zephyr/drivers/pm_cpu_ops.h b/include/zephyr/drivers/pm_cpu_ops.h index fa7f11734c59..5bc09d77d122 100644 --- a/include/zephyr/drivers/pm_cpu_ops.h +++ b/include/zephyr/drivers/pm_cpu_ops.h @@ -10,6 +10,7 @@ /** * @file * @brief Public API for CPU Power Management + * @ingroup power_management_cpu_api */ #include From 2bb8c7f2f292e5368cc2a362df7aa3571ad37f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 1 Aug 2026 02:37:30 +0000 Subject: [PATCH 016/455] drivers: pm_cpu_ops: document system reset type macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give SYS_WARM_RESET and SYS_COLD_RESET individual documentation comments referencing pm_system_reset(), replacing the shared plain comment. Assisted-by: Claude:fable-5 Signed-off-by: Benjamin Cabé --- include/zephyr/drivers/pm_cpu_ops.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/zephyr/drivers/pm_cpu_ops.h b/include/zephyr/drivers/pm_cpu_ops.h index 5bc09d77d122..357c225a3175 100644 --- a/include/zephyr/drivers/pm_cpu_ops.h +++ b/include/zephyr/drivers/pm_cpu_ops.h @@ -21,8 +21,10 @@ extern "C" { #endif -/* System reset types. */ +/** Warm system reset, used as argument to pm_system_reset(). */ #define SYS_WARM_RESET 0 + +/** Cold system reset, used as argument to pm_system_reset(). */ #define SYS_COLD_RESET 1 /** * @defgroup power_management_cpu_api CPU Power Management From 31079094b857486370b5e15b06460d6f9dcafcc3 Mon Sep 17 00:00:00 2001 From: Anu Biradar Date: Tue, 17 Mar 2026 16:29:25 -0500 Subject: [PATCH 017/455] drivers: clock_control: max32: add ERFO capacitance config via DTS Add support for configuring ERFO in/out capacitance through device tree on MAX32657. Co-authored-by: Okan Sahin Signed-off-by: Anu Biradar --- drivers/clock_control/clock_control_max32.c | 9 ++++++++- dts/arm/adi/max32/max32657_common.dtsi | 3 ++- dts/bindings/clock/adi,max32-erfo.yaml | 12 ++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 dts/bindings/clock/adi,max32-erfo.yaml diff --git a/drivers/clock_control/clock_control_max32.c b/drivers/clock_control/clock_control_max32.c index 32ddebb0c810..175b13f7bfc8 100644 --- a/drivers/clock_control/clock_control_max32.c +++ b/drivers/clock_control/clock_control_max32.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Analog Devices, Inc. + * Copyright (c) 2023-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -158,6 +158,13 @@ static int max32_clkctrl_init(const struct device *dev) Wrap_MXC_SYS_SetClockDiv(sysclk_prescaler(ADI_MAX32_SYSCLK_PRESCALER)); #endif +#if defined(CONFIG_SOC_MAX32657) && DT_NODE_HAS_PROP(DT_NODELABEL(clk_erfo), capacitance_erfoctrl) + /* Setup capacitance for ERFO */ + int erfo_cap = DT_PROP(DT_NODELABEL(clk_erfo), capacitance_erfoctrl); + + MXC_FCR->erfoctrl = erfo_cap; +#endif + return 0; } diff --git a/dts/arm/adi/max32/max32657_common.dtsi b/dts/arm/adi/max32/max32657_common.dtsi index f149492d54ca..dbe5593020c1 100644 --- a/dts/arm/adi/max32/max32657_common.dtsi +++ b/dts/arm/adi/max32/max32657_common.dtsi @@ -99,9 +99,10 @@ }; clk_erfo: clk_erfo { - compatible = "fixed-clock"; + compatible = "adi,max32-erfo"; #clock-cells = <0>; clock-frequency = ; + capacitance-erfoctrl = <0>; status = "disabled"; }; }; diff --git a/dts/bindings/clock/adi,max32-erfo.yaml b/dts/bindings/clock/adi,max32-erfo.yaml new file mode 100644 index 000000000000..04c18347de00 --- /dev/null +++ b/dts/bindings/clock/adi,max32-erfo.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2026 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +compatible: "adi,max32-erfo" + +include: fixed-clock.yaml + +properties: + capacitance-erfoctrl: + type: int + description: This is only supported on MAX32657. Refer to documentation on the + ERFO Control register for guidance on how to set the register. From e200f33b4a9ab0845633c0782367024b8c3e11eb Mon Sep 17 00:00:00 2001 From: Lauren Murphy Date: Tue, 18 Aug 2026 09:39:12 -0700 Subject: [PATCH 018/455] llext: remove unused variable in llext struct Remove unused variable left in llext struct by #11409 due to CI error causing test failure. Signed-off-by: Lauren Murphy --- include/zephyr/llext/llext.h | 3 --- subsys/llext/llext_mem.c | 4 ---- 2 files changed, 7 deletions(-) diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 10ef10f018ba..dd7effc050f0 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -125,9 +125,6 @@ struct llext { /** Lookup table of memory regions */ void *mem[LLEXT_MEM_COUNT]; - /** Address of text region in ELF buffer */ - void *text_in_elf; - /** Is the memory for this region allocated on heap? */ bool mem_on_heap[LLEXT_MEM_COUNT]; diff --git a/subsys/llext/llext_mem.c b/subsys/llext/llext_mem.c index d7a02b0f5102..eecc87a3b281 100644 --- a/subsys/llext/llext_mem.c +++ b/subsys/llext/llext_mem.c @@ -135,10 +135,6 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext, /* Region has data in the file, check if peek() is supported */ ext->mem[mem_idx] = llext_peek(ldr, region->sh_offset); if (ext->mem[mem_idx]) { - if (mem_idx == LLEXT_MEM_TEXT) { - ext->text_in_elf = ext->mem[mem_idx]; - } - if ((IS_ALIGNED(ext->mem[mem_idx], region_align) || ldr_parm->pre_located) && ((mem_idx != LLEXT_MEM_TEXT) || From 3ac8ef05fc2662e3197aed767f3d537e267fa994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Str=C3=B8mme?= Date: Thu, 20 Aug 2026 15:24:38 +0200 Subject: [PATCH 019/455] kernel: queue: skip blocking trace for no-wait get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An empty k_queue_get() emits the blocking trace before checking K_NO_WAIT. This records a block that never occurs and invokes the tracing backend on a non-blocking fast path. Move the trace below the no-wait return so only calls that can pend emit it. Add CTF coverage for an empty no-wait get. Signed-off-by: Magnus Strømme --- kernel/queue.c | 4 ++-- tests/subsys/tracing/ctf_trace/pytest/test_ctf_trace.py | 2 ++ tests/subsys/tracing/ctf_trace/src/main.c | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/queue.c b/kernel/queue.c index 61c2f092a03b..939f518bdf2d 100644 --- a/kernel/queue.c +++ b/kernel/queue.c @@ -338,8 +338,6 @@ void *z_impl_k_queue_get(struct k_queue *queue, k_timeout_t timeout) return data; } - SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_queue, get, queue, timeout); - if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { k_spin_unlock(&queue->lock, key); @@ -348,6 +346,8 @@ void *z_impl_k_queue_get(struct k_queue *queue, k_timeout_t timeout) return NULL; } + SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_queue, get, queue, timeout); + int ret = z_pend_curr(&queue->lock, key, &queue->wait_q, timeout); SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_queue, get, queue, timeout, diff --git a/tests/subsys/tracing/ctf_trace/pytest/test_ctf_trace.py b/tests/subsys/tracing/ctf_trace/pytest/test_ctf_trace.py index 7073d89882ba..fab502af8e1c 100644 --- a/tests/subsys/tracing/ctf_trace/pytest/test_ctf_trace.py +++ b/tests/subsys/tracing/ctf_trace/pytest/test_ctf_trace.py @@ -75,6 +75,8 @@ def test_ctf_trace(dut): missing = [e for e in EXPECTED_EVENTS if e not in seen] assert not missing, f"missing expected CTF events {missing}; decoded types: {sorted(seen)}" + assert "queue_get_blocking" not in names, "K_NO_WAIT queue get emitted a blocking event" + # Field sanity: queue_get_exit must carry the object id, timeout and return value. get_exit = next(e for e in tr.events if e.name == "queue_get_exit") for field in ("id", "timeout", "ret"): diff --git a/tests/subsys/tracing/ctf_trace/src/main.c b/tests/subsys/tracing/ctf_trace/src/main.c index ac6a18623036..c832aea5ae16 100644 --- a/tests/subsys/tracing/ctf_trace/src/main.c +++ b/tests/subsys/tracing/ctf_trace/src/main.c @@ -57,6 +57,8 @@ int main(void) k_queue_init(&queue); k_queue_append(&queue, &item); (void)k_queue_get(&queue, K_NO_WAIT); + /* Exercise the empty no-wait path without emitting a blocking event. */ + (void)k_queue_get(&queue, K_NO_WAIT); k_fifo_init(&fifo); k_fifo_put(&fifo, &item); From 4fa63783b0956f3f6f11741d5792c9ba030214d2 Mon Sep 17 00:00:00 2001 From: Dimitri Varpusvuori Date: Sun, 16 Aug 2026 06:11:25 +0300 Subject: [PATCH 020/455] logging: align stack packages when stack alignment is insufficient The m68k port proposed in RFC #114672 permits 2-byte stack alignment while pointers are 4 bytes. This is ABI-compliant, but alloca() can consequently return an address at 2 mod 4. cbvprintf_package() explicitly comments that its buffer must be aligned at least to the size of a pointer, and returns -EFAULT otherwise. Runtime logging then asserts because packaging failed. Keep plain alloca() when ARCH_STACK_PTR_ALIGN guarantees pointer alignment. Otherwise reserve alignment slack and round the allocation to Z_LOG_MSG_ALIGNMENT. Apply this to the user, frontend fallback, and immediate paths. Although pointer alignment is enough to pass cbvprintf_package()'s initial buffer check, use Z_LOG_MSG_ALIGNMENT for the fallback to preserve the alignment assumed by logging's package-size calculation. The condition is constant at compile time, so generated code remains unchanged on architectures whose stack alignment is sufficient. This is a prerequisite for the m68k port proposed in: https://github.com/zephyrproject-rtos/zephyr/issues/114672 Assisted-by: OpenAI Codex Signed-off-by: Dimitri Varpusvuori --- subsys/logging/log_msg.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/subsys/logging/log_msg.c b/subsys/logging/log_msg.c index 1960ec7c28ba..a2bf2a875233 100644 --- a/subsys/logging/log_msg.c +++ b/subsys/logging/log_msg.c @@ -23,6 +23,20 @@ BUILD_ASSERT(sizeof(struct log_msg_desc) == sizeof(uint32_t), #define CBPRINTF_DESC_SIZE32 (sizeof(struct cbprintf_package_desc) / sizeof(uint32_t)) +/* + * cbvprintf_package() requires its buffer to be aligned at least to the size + * of a pointer. Avoid alignment arithmetic when the stack already provides + * that guarantee. + * + * Use the logging alignment for the fallback so the package address matches + * the alignment assumed when its size was calculated. + */ +#define LOG_MSG_ALIGNED_ALLOCA(_size) \ + (IS_ALIGNED(ARCH_STACK_PTR_ALIGN, sizeof(void *)) \ + ? alloca(_size) \ + : (void *)ROUND_UP((uintptr_t)alloca((_size) + Z_LOG_MSG_ALIGNMENT - 1U), \ + Z_LOG_MSG_ALIGNMENT)) + /* For simplified message handling cprintf package must have only 1 word. */ BUILD_ASSERT(!IS_ENABLED(CONFIG_LOG_SIMPLE_MSG_OPTIMIZE) || (IS_ENABLED(CONFIG_LOG_SIMPLE_MSG_OPTIMIZE) && (CBPRINTF_DESC_SIZE32 == 1))); @@ -398,19 +412,19 @@ void z_log_msg_runtime_vcreate(uint8_t domain_id, const void *source, Z_LOG_MSG_DESC_INITIALIZER(domain_id, level, plen, dlen); if (k_is_user_context()) { - pkg = alloca(plen); + pkg = LOG_MSG_ALIGNED_ALLOCA(plen); msg = NULL; } else if (IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) && BACKENDS_IN_USE()) { compiler_barrier(); msg = z_log_msg_alloc(msg_wlen); if (IS_ENABLED(CONFIG_LOG_FRONTEND) && msg == NULL) { - pkg = alloca(plen); + pkg = LOG_MSG_ALIGNED_ALLOCA(plen); } else { pkg = msg ? msg->data : NULL; } } else { compiler_barrier(); - msg = alloca(msg_wlen * sizeof(int)); + msg = LOG_MSG_ALIGNED_ALLOCA(msg_wlen * sizeof(int)); pkg = msg->data; } From 7b86cd90367987eeabd70dad622ba55e00ecfa70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 11 Aug 2026 14:33:15 +0200 Subject: [PATCH 021/455] net: sockets: handle interface down case in multicast group join MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when the iface is down, the group will be joined on up, so don't treat that as a error. Signed-off-by: Fin Maaß --- subsys/net/lib/sockets/sockets_inet.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/subsys/net/lib/sockets/sockets_inet.c b/subsys/net/lib/sockets/sockets_inet.c index cb67d2aef947..07eb1da2bc72 100644 --- a/subsys/net/lib/sockets/sockets_inet.c +++ b/subsys/net/lib/sockets/sockets_inet.c @@ -2555,6 +2555,13 @@ static int ipv4_multicast_group(struct net_context *ctx, const void *optval, ret = net_ipv4_igmp_leave(iface, &mreqn->imr_multiaddr); } + if (ret == -ENETDOWN) { + /* If the interface is down, we can still return success as the + * join will be performed when the interface comes up. + */ + return 0; + } + if (ret < 0) { errno = -ret; return -1; @@ -2607,6 +2614,13 @@ static int ipv6_multicast_group(struct net_context *ctx, const void *optval, ret = net_ipv6_mld_leave(iface, &mreq->ipv6mr_multiaddr); } + if (ret == -ENETDOWN) { + /* If the interface is down, we can still return success as the + * join will be performed when the interface comes up. + */ + return 0; + } + if (ret < 0) { errno = -ret; return -1; From 11bcb86d8f7da04472dc736671e4490a9feef00d Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Fri, 21 Aug 2026 12:02:49 +0200 Subject: [PATCH 022/455] native_simulator: Get latest from upstream Align with native_simulator's upstream main 0d3eafc13a7bf37ef146da540986f99356ac48bd Which includes: 0d3eafc native: hw_timer: split real time pacing from tick logic Signed-off-by: Alberto Escolar Piedras --- .../native/src/include/nsi_timer_model.h | 8 +- .../native_simulator/native/src/timer_model.c | 123 +++++++++++++----- 2 files changed, 98 insertions(+), 33 deletions(-) diff --git a/scripts/native_simulator/native/src/include/nsi_timer_model.h b/scripts/native_simulator/native/src/include/nsi_timer_model.h index aaa39e933fa8..2c59653b7744 100644 --- a/scripts/native_simulator/native/src/include/nsi_timer_model.h +++ b/scripts/native_simulator/native/src/include/nsi_timer_model.h @@ -16,7 +16,10 @@ extern "C" { #endif void hwtimer_set_real_time_mode(bool new_rt); -void hwtimer_timer_reached(void); +void hwtimer_set_rt_period(uint64_t period); +void hwtimer_set_rt_ratio(double ratio); +void hwtimer_adjust_rt_ratio(double ratio_correction); + void hwtimer_wake_in_time(uint64_t time); void hwtimer_set_silent_ticks(int64_t sys_ticks); void hwtimer_enable(uint64_t period); @@ -24,10 +27,7 @@ int64_t hwtimer_get_pending_silent_ticks(void); void hwtimer_reset_rtc(void); void hwtimer_set_rtc_offset(int64_t offset); -void hwtimer_set_rt_ratio(double ratio); - void hwtimer_adjust_rtc_offset(int64_t offset_delta); -void hwtimer_adjust_rt_ratio(double ratio_correction); int64_t hwtimer_get_simu_rtc_time(void); void hwtimer_get_pseudohost_rtc_time(uint32_t *nsec, uint64_t *sec); diff --git a/scripts/native_simulator/native/src/timer_model.c b/scripts/native_simulator/native/src/timer_model.c index bc509c0d0aae..082a3f24c0ef 100644 --- a/scripts/native_simulator/native/src/timer_model.c +++ b/scripts/native_simulator/native/src/timer_model.c @@ -63,6 +63,7 @@ static char *us_time_to_str(char *dest, uint64_t time) static uint64_t hw_timer_timer; /* Event timer exposed to the HW scheduler */ static uint64_t hw_timer_tick_timer; +static uint64_t hw_timer_rt_timer; static uint64_t hw_timer_awake_timer; static uint64_t tick_p; /* Period of the ticker */ @@ -84,6 +85,7 @@ static uint64_t boot_time; * than real time */ static double clock_ratio = 1.0; +static uint64_t rt_period = 10e3; /* in microseconds */ #if DEBUG_NP_TIMER /* @@ -108,14 +110,25 @@ static uint64_t last_radj_rtime; /* Last simulated time when the ratio was adjusted */ static uint64_t last_radj_stime; +/* Set in real time mode. Note this can only be called before HW_INIT */ void hwtimer_set_real_time_mode(bool new_rt) { - real_time_mode = new_rt; + if (hw_timer_rt_timer) { + nsi_print_warning("%s: can't be called after init. Ignored\n", __func__); + } else { + real_time_mode = new_rt; + } +} + +void hwtimer_set_rt_period(uint64_t period) +{ + rt_period = period; } static void hwtimer_update_timer(void) { hw_timer_timer = NSI_MIN(hw_timer_tick_timer, hw_timer_awake_timer); + hw_timer_timer = NSI_MIN(hw_timer_timer, hw_timer_rt_timer); } static inline void host_clock_gettime(struct timespec *tv) @@ -140,17 +153,28 @@ uint64_t get_host_us_time(void) return (uint64_t)tv.tv_sec * 1e6 + tv.tv_nsec / 1000; } -static void hwtimer_init(void) +static void hwtimer_rt_init(void) { - silent_ticks = 0; - hw_timer_tick_timer = NSI_NEVER; - hw_timer_awake_timer = NSI_NEVER; - hwtimer_update_timer(); if (real_time_mode) { boot_time = get_host_us_time(); last_radj_rtime = boot_time; last_radj_stime = 0U; + + hw_timer_rt_timer = rt_period; + } else { + hw_timer_rt_timer = NSI_NEVER; } +} + +static void hwtimer_init(void) +{ + hwtimer_rt_init(); + + silent_ticks = 0; + hw_timer_tick_timer = NSI_NEVER; + hw_timer_awake_timer = NSI_NEVER; + hwtimer_update_timer(); + if (!reset_rtc) { struct timespec tv; uint64_t realhosttime; @@ -186,39 +210,44 @@ void hwtimer_enable(uint64_t period) nsi_hws_find_next_event(); } -static void hwtimer_tick_timer_reached(void) +static void hwtimer_rt_timer_reached(void) { - if (real_time_mode) { - uint64_t expected_rt = (hw_timer_tick_timer - last_radj_stime) - / clock_ratio - + last_radj_rtime; - uint64_t real_time = get_host_us_time(); + uint64_t expected_rt = (hw_timer_rt_timer - last_radj_stime) / clock_ratio + + last_radj_rtime; + uint64_t real_time = get_host_us_time(); - int64_t diff = expected_rt - real_time; + int64_t diff = expected_rt - real_time; #if DEBUG_NP_TIMER - char es[30]; - char rs[30]; - - us_time_to_str(es, expected_rt - boot_time); - us_time_to_str(rs, real_time - boot_time); - printf("tick @%5"PRIu64"ms: diff = expected_rt - real_time = " - "%5"PRIi64" = %s - %s\n", - hw_timer_tick_timer/1000U, diff, es, rs); + char es[30]; + char rs[30]; + + us_time_to_str(es, expected_rt - boot_time); + us_time_to_str(rs, real_time - boot_time); + printf("rt sync @%5"PRIu64"ms: diff = expected_rt - real_time = " + "%5"PRIi64" = %s - %s\n", + hw_timer_rt_timer/1000U, diff, es, rs); #endif - if (diff > 0) { /* we need to slow down */ - struct timespec requested_time; - struct timespec remaining; + if (diff > 0) { /* we need to slow down */ + struct timespec requested_time; + struct timespec remaining; + + requested_time.tv_sec = diff / 1e6; + requested_time.tv_nsec = (diff - requested_time.tv_sec*1e6)*1e3; - requested_time.tv_sec = diff / 1e6; - requested_time.tv_nsec = (diff - - requested_time.tv_sec*1e6)*1e3; + (void) nanosleep(&requested_time, &remaining); + } - (void) nanosleep(&requested_time, &remaining); - } + hw_timer_rt_timer += rt_period; + if (hw_timer_rt_timer < nsi_hws_get_time()) { /* wrapped around the end of time */ + hw_timer_rt_timer = NSI_NEVER; } + hwtimer_update_timer(); +} +static void hwtimer_tick_timer_reached(void) +{ if (tick_p > NSI_NEVER - hw_timer_tick_timer) { /* We'd wrap around the end of time */ hw_timer_tick_timer = NSI_NEVER; } else { @@ -248,6 +277,10 @@ static void hwtimer_timer_reached(void) hwtimer_awake_timer_reached(); } + if (hw_timer_rt_timer == Now) { + hwtimer_rt_timer_reached(); + } + if (hw_timer_tick_timer == Now) { hwtimer_tick_timer_reached(); } @@ -428,6 +461,7 @@ static struct { double stop_at; double rtc_offset; double rt_drift; + double rt_period; double rt_ratio; } args; @@ -485,6 +519,24 @@ static void cmd_rt_ratio_found(char *argv, int offset) hwtimer_set_rt_ratio(args.rt_ratio); } +static void cmd_rt_period_found(char *argv, int offset) +{ + NSI_ARG_UNUSED(argv); + NSI_ARG_UNUSED(offset); + + if (args.rt_period < 1e-6) { + nsi_print_error_and_exit("The rt-period (%le) needs to be >= 1e-6. " + "Please use --help for more info\n", args.rt_period); + } + if ((args.rt_period < 1e-3) || (args.rt_period > 60)) { + nsi_print_warning("rt-period has a weird value (%le). " + "Are you sure this is what you want?\n", + args.rt_period); + } + hwtimer_set_rt_period(args.rt_period * 1e6); + hwtimer_set_real_time_mode(true); +} + static void cmd_rtcreset_found(char *argv, int offset) { (void) argv; @@ -536,6 +588,19 @@ static void nsi_add_time_options(void) "simultaneously. " "This option has no effect in non real time mode" }, + { + .option = "rt-period", + .name = "period", + .type = 'd', + .dest = (void *)&args.rt_period, + .call_when_found = cmd_rt_period_found, + .descript = "In seconds, periodicity at which real time and simulated time " + "will be sync'ed. " + "For ex. set to 50e-3 to have the simulation held every 50ms " + "until the real time catches up. By default it is 10ms. " + "This option has no effect in non real time mode, but setting " + "it implies `-rt`" + }, { .option = "rtc-offset", .name = "time_offset", From 7a39aa6774905d0312f3ed5d45e14176e4a0acbf Mon Sep 17 00:00:00 2001 From: Piotr Zierhoffer Date: Mon, 24 Aug 2026 15:48:14 +0200 Subject: [PATCH 023/455] maintainers: renode: Update collaborators list Also removing an inactive collaborator from LiteX Signed-off-by: Piotr Zierhoffer --- MAINTAINERS.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 2de5d7dddd3b..3cfedb0056d4 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -3844,7 +3844,6 @@ LiteX Platforms: - maass-hamburg collaborators: - kgugala - - mateusz-holenko files: - boards/enjoydigital/litex_vexriscv/ - drivers/*/*litex* @@ -6298,7 +6297,7 @@ Testing with Renode: # This area is to be converted to a subarea status: odd fixes collaborators: - - mateusz-holenko + - PiotrZierhoffer - fkokosinski files: - cmake/emu/renode.cmake From 2d589ed69e1aa833a5e95a95db17613f0333f6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 11 Aug 2026 14:44:44 +0200 Subject: [PATCH 024/455] net: if: validate multicast address before locking interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check multicast ip address before locking interface. Signed-off-by: Fin Maaß --- subsys/net/ip/net_if.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 246f4becde41..aaf559159862 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -2495,15 +2495,15 @@ struct net_if_mcast_addr *net_if_ipv6_maddr_add(struct net_if *iface, return NULL; } - net_if_lock(iface); - - if (net_if_config_ipv6_get(iface, &ipv6) < 0) { - goto out; - } - if (!net_ipv6_is_addr_mcast(addr)) { NET_DBG("Address %s is not a multicast address.", net_sprint_ipv6_addr(addr)); + return NULL; + } + + net_if_lock(iface); + + if (net_if_config_ipv6_get(iface, &ipv6) < 0) { goto out; } @@ -5464,15 +5464,15 @@ struct net_if_mcast_addr *net_if_ipv4_maddr_add(struct net_if *iface, return NULL; } - net_if_lock(iface); - - if (net_if_config_ipv4_get(iface, NULL) < 0) { - goto out; - } - if (!net_ipv4_is_addr_mcast(addr)) { NET_DBG("Address %s is not a multicast address.", net_sprint_ipv4_addr(addr)); + return NULL; + } + + net_if_lock(iface); + + if (net_if_config_ipv4_get(iface, NULL) < 0) { goto out; } From 1bc05d5cb47bbd87b87782db2c9e6c51073403fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Thu, 20 Aug 2026 20:25:58 +0200 Subject: [PATCH 025/455] drivers: ethernet: dwc_mac: esp32: disable tx checksum offloading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deactive tx checksum offloading on the esp32, while it has a big enough fifo, the tx checksum is not correctly calculated on every second packet. Signed-off-by: Fin Maaß --- drivers/ethernet/dwc_mac/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ethernet/dwc_mac/Kconfig b/drivers/ethernet/dwc_mac/Kconfig index 576dab06377e..514da474e7f3 100644 --- a/drivers/ethernet/dwc_mac/Kconfig +++ b/drivers/ethernet/dwc_mac/Kconfig @@ -141,6 +141,7 @@ rsource "Kconfig.mcast" config ETH_DWC_ETHER_TX_HW_CHECKSUM bool "Use TX hardware checksum" select NET_CHECKSUM_OFFLOAD_SUPPORTED if NET_L2_ETHERNET + depends on !SOC_SERIES_ESP32 depends on !SOC_SERIES_ESP32P4 default y help From 24dd24f02348385e389a02afccb6f25512acdd6e Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 10:36:16 -0700 Subject: [PATCH 026/455] net: lwm2m: Don't act on partial Package URI writes lwm2m_write_handler() declared last_block as true and never assigned it, so every non-opaque post-write callback was told the write had completed on every block of a block-wise transfer. Neither package_uri_write_cb() checked the flag anyway, so a Package URI written block-wise started a firmware download from the first non-empty block, using whatever fragment sat in the resource buffer instead of the intended URI. Signed-off-by: Flavio Ceolin --- subsys/net/lib/lwm2m/lwm2m_message_handling.c | 1 + subsys/net/lib/lwm2m/lwm2m_obj_firmware.c | 16 ++++++++++++++-- subsys/net/lib/lwm2m/lwm2m_obj_swmgmt.c | 13 +++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/subsys/net/lib/lwm2m/lwm2m_message_handling.c b/subsys/net/lib/lwm2m/lwm2m_message_handling.c index 3f706dd00dfc..27d26985feb2 100644 --- a/subsys/net/lib/lwm2m/lwm2m_message_handling.c +++ b/subsys/net/lib/lwm2m/lwm2m_message_handling.c @@ -1103,6 +1103,7 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_eng /* Get block_ctx for total_size (might be zero) */ total_size = msg->in.block_ctx->ctx.total_size; offset = msg->in.block_ctx->opaque.offset; + last_block = msg->in.block_ctx->last_block; LOG_DBG("BLOCK1: total:%zu current:%zu" " last:%u", diff --git a/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c b/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c index 48d81e379596..4eaa64ef941b 100644 --- a/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c +++ b/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c @@ -305,8 +305,20 @@ static int package_uri_write_cb(uint16_t obj_inst_id, uint16_t res_id, LOG_DBG("PACKAGE_URI WRITE: %s", package_uri[obj_inst_id]); #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT - uint8_t state = lwm2m_firmware_get_update_state_inst(obj_inst_id); - bool empty_uri = data_len == 0 || strnlen(data, data_len) == 0; + uint8_t state; + bool empty_uri; + + /* writes every block of a block-wise transfer to the start of + * the buffer, so it never holds the assembled URI. Reject the + * write rather than act on whichever fragment happens to be present. + */ + if (!last_block) { + LOG_ERR("PACKAGE_URI: block-wise write is not supported"); + return -EFBIG; + } + + state = lwm2m_firmware_get_update_state_inst(obj_inst_id); + empty_uri = data_len == 0 || strnlen(data, data_len) == 0; if (state == STATE_IDLE) { if (!empty_uri) { diff --git a/subsys/net/lib/lwm2m/lwm2m_obj_swmgmt.c b/subsys/net/lib/lwm2m/lwm2m_obj_swmgmt.c index a792220a585a..a447eeeadd60 100644 --- a/subsys/net/lib/lwm2m/lwm2m_obj_swmgmt.c +++ b/subsys/net/lib/lwm2m/lwm2m_obj_swmgmt.c @@ -656,7 +656,20 @@ static int package_uri_write_cb(uint16_t obj_inst_id, uint16_t res_id, int error_code; struct lwm2m_swmgmt_data *instance = NULL; + /* writes every block of a block-wise transfer to the start of + * the buffer, so it never holds the assembled URI. Reject the + * write rather than act on whichever fragment happens to be present. + */ + if (!last_block) { + LOG_ERR("PACKAGE_URI: block-wise write is not supported"); + return -EFBIG; + } + instance = find_index(obj_inst_id); + if (instance == NULL) { + LOG_ERR("Instance %u not found", obj_inst_id); + return -ENOENT; + } struct requesting_object req = { .obj_inst_id = obj_inst_id, .is_firmware_uri = false, From 9eedfa0defeb8cdc2d07d5a1da26aa49b3770e62 Mon Sep 17 00:00:00 2001 From: Alex Ciascai Date: Thu, 20 Aug 2026 21:02:10 +0300 Subject: [PATCH 027/455] bluetooth: tester: fix BIS_Sync in receive state event btp_send_broadcast_receive_state_ev() right-shifted subgroup->bis_sync by one before packing it into the BTP event. BIS_Sync is a bitfield in which bit 0 represents BIS index 1, not an index, so the shift silently corrupted the value: 0x00000001 (BIS 1 synced) was reported as 0, and 0xFFFFFFFF was reported as 0x7FFFFFFF. Signed-off-by: Alex Ciascai --- tests/bluetooth/tester/src/audio/btp_bap_broadcast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c b/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c index 5fe81c273050..28c96028ead1 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c +++ b/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c @@ -1313,7 +1313,7 @@ btp_send_broadcast_receive_state_ev(struct bt_conn *conn, for (uint8_t i = 0U; i < ev->num_subgroups; i++) { const struct bt_bap_bass_subgroup *subgroup = &state->subgroups[i]; - sys_put_le32(subgroup->bis_sync >> 1, ptr); + sys_put_le32(subgroup->bis_sync, ptr); ptr += sizeof(subgroup->bis_sync); *ptr = subgroup->metadata_len; ptr += sizeof(subgroup->metadata_len); From 13f737bdbd574d5786bcb1be01f0b07cefaa1ea6 Mon Sep 17 00:00:00 2001 From: Nhut Nguyen Date: Tue, 14 Jul 2026 17:41:23 +0700 Subject: [PATCH 028/455] dts: arm64: renesas: Change interrupt type of TINT for RZ/A3UL As per RZ/A3UL User's Manual, the interupt type of TINT for RZ/A3UL is fixed to level. Signed-off-by: Nhut Nguyen --- dts/arm64/renesas/rz/rza/r9a07g063.dtsi | 64 ++++++++++++------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/dts/arm64/renesas/rz/rza/r9a07g063.dtsi b/dts/arm64/renesas/rz/rza/r9a07g063.dtsi index f25abc983c84..0bc538812520 100644 --- a/dts/arm64/renesas/rz/rza/r9a07g063.dtsi +++ b/dts/arm64/renesas/rz/rza/r9a07g063.dtsi @@ -751,7 +751,7 @@ tint0: tint0@0 { compatible = "renesas,rz-tint"; reg = <0x0>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -760,7 +760,7 @@ tint1: tint1@1 { compatible = "renesas,rz-tint"; reg = <0x1>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -769,7 +769,7 @@ tint2: tint2@2 { compatible = "renesas,rz-tint"; reg = <0x2>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -778,7 +778,7 @@ tint3: tint3@3 { compatible = "renesas,rz-tint"; reg = <0x3>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -787,7 +787,7 @@ tint4: tint4@4 { compatible = "renesas,rz-tint"; reg = <0x4>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -796,7 +796,7 @@ tint5: tint5@5 { compatible = "renesas,rz-tint"; reg = <0x5>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -805,7 +805,7 @@ tint6: tint6@6 { compatible = "renesas,rz-tint"; reg = <0x6>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -814,7 +814,7 @@ tint7: tint7@7 { compatible = "renesas,rz-tint"; reg = <0x7>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -823,7 +823,7 @@ tint8: tint8@8 { compatible = "renesas,rz-tint"; reg = <0x8>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -832,7 +832,7 @@ tint9: tint9@9 { compatible = "renesas,rz-tint"; reg = <0x9>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -841,7 +841,7 @@ tint10: tint10@a { compatible = "renesas,rz-tint"; reg = <0xa>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -850,7 +850,7 @@ tint11: tint11@b { compatible = "renesas,rz-tint"; reg = <0xb>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -859,7 +859,7 @@ tint12: tint12@c { compatible = "renesas,rz-tint"; reg = <0xc>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -868,7 +868,7 @@ tint13: tint13@d { compatible = "renesas,rz-tint"; reg = <0xd>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -877,7 +877,7 @@ tint14: tint14@e { compatible = "renesas,rz-tint"; reg = <0xe>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -886,7 +886,7 @@ tint15: tint15@f { compatible = "renesas,rz-tint"; reg = <0xf>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -895,7 +895,7 @@ tint16: tint16@10 { compatible = "renesas,rz-tint"; reg = <0x10>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -904,7 +904,7 @@ tint17: tint17@11 { compatible = "renesas,rz-tint"; reg = <0x11>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -913,7 +913,7 @@ tint18: tint18@12 { compatible = "renesas,rz-tint"; reg = <0x12>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -922,7 +922,7 @@ tint19: tint19@13 { compatible = "renesas,rz-tint"; reg = <0x13>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -931,7 +931,7 @@ tint20: tint20@14 { compatible = "renesas,rz-tint"; reg = <0x14>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -940,7 +940,7 @@ tint21: tint21@15 { compatible = "renesas,rz-tint"; reg = <0x15>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -949,7 +949,7 @@ tint22: tint22@16 { compatible = "renesas,rz-tint"; reg = <0x16>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -958,7 +958,7 @@ tint23: tint23@17 { compatible = "renesas,rz-tint"; reg = <0x17>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -967,7 +967,7 @@ tint24: tint24@18 { compatible = "renesas,rz-tint"; reg = <0x18>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -976,7 +976,7 @@ tint25: tint25@19 { compatible = "renesas,rz-tint"; reg = <0x19>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -985,7 +985,7 @@ tint26: tint26@1a { compatible = "renesas,rz-tint"; reg = <0x1a>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -994,7 +994,7 @@ tint27: tint27@1b { compatible = "renesas,rz-tint"; reg = <0x1b>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -1003,7 +1003,7 @@ tint28: tint28@1c { compatible = "renesas,rz-tint"; reg = <0x1c>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -1012,7 +1012,7 @@ tint29: tint29@1d { compatible = "renesas,rz-tint"; reg = <0x1d>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -1021,7 +1021,7 @@ tint30: tint30@1e { compatible = "renesas,rz-tint"; reg = <0x1e>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; @@ -1030,7 +1030,7 @@ tint31: tint31@1f { compatible = "renesas,rz-tint"; reg = <0x1f>; - interrupts = ; + interrupts = ; trigger-type = "rising"; #irq-cells = <1>; status = "disabled"; From 38bb76d8c62d7a9045803eb86127bd8afbcd123a Mon Sep 17 00:00:00 2001 From: Nhut Nguyen Date: Wed, 15 Jul 2026 16:02:13 +0700 Subject: [PATCH 029/455] drivers: intc: renesas: Add locking for shared TINT registers Introduce a spinlock to protect the shared TSCR/TITSR/TSSR registers in intc_rz_tint_set_type() and intc_rz_tint_connect(), which can be called concurrently from different tint instances. Signed-off-by: Nhut Nguyen --- .../intc_renesas_rz_tint.c | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/drivers/interrupt_controller/intc_renesas_rz_tint.c b/drivers/interrupt_controller/intc_renesas_rz_tint.c index e07c724df114..670ed48968b6 100644 --- a/drivers/interrupt_controller/intc_renesas_rz_tint.c +++ b/drivers/interrupt_controller/intc_renesas_rz_tint.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,7 @@ struct intc_rz_tint_data { }; static const uint8_t gpioint_table[] = DT_PROP(DT_NODELABEL(intc), gpioint_table); +static struct k_spinlock lock; static inline bool intc_rz_tint_status_read(mem_addr_t base, uint8_t tint) { @@ -138,7 +140,7 @@ int intc_rz_tint_set_type(const struct device *dev, enum intc_rz_tint_trigger tr uint32_t flags = IRQ_TYPE_LEVEL; uint32_t set = 0; mem_addr_t base = INTC_BASE; - uint32_t reg_val = REG_TITSR_READ(base, tint); + uint32_t reg_val; switch (trig) { case RZ_TINT_FAILING_EDGE: @@ -158,30 +160,33 @@ int intc_rz_tint_set_type(const struct device *dev, enum intc_rz_tint_trigger tr return -ENOTSUP; } - /* Select interrupt type */ - reg_val = (reg_val & ~REG_TITSR_TITSEL_MASK(tint)) | - FIELD_PREP(REG_TITSR_TITSEL_MASK(tint), set); - REG_TITSR_WRITE(base, tint, reg_val); - - /* - * User's manual: Precaution when Changing Interrupt Settings - * When changing the TINT interrupt detection method to the edge type, - * write 0 to the TSTATn bit of TSCR. - */ - if ((trig == RZ_TINT_RISING_EDGE) || (trig == RZ_TINT_FAILING_EDGE)) { - flags = IRQ_TYPE_EDGE; - intc_rz_tint_clear_irq_status(dev); - } - - /* Set interrupt type for GIC, and clear pending interrupt */ + K_SPINLOCK(&lock) { + /* Select interrupt type */ + reg_val = REG_TITSR_READ(base, tint); + reg_val = (reg_val & ~REG_TITSR_TITSEL_MASK(tint)) | + FIELD_PREP(REG_TITSR_TITSEL_MASK(tint), set); + REG_TITSR_WRITE(base, tint, reg_val); + + /* + * User's manual: Precaution when Changing Interrupt Settings + * When changing the TINT interrupt detection method to the edge type, + * write 0 to the TSTATn bit of TSCR. + */ + if ((trig == RZ_TINT_RISING_EDGE) || (trig == RZ_TINT_FAILING_EDGE)) { + flags = IRQ_TYPE_EDGE; + intc_rz_tint_clear_irq_status(dev); + } + + /* Set interrupt type for GIC, and clear pending interrupt */ #ifdef CONFIG_GIC - arm_gic_irq_set_priority(config->irq, config->prio, flags); - arm_gic_irq_clear_pending(config->irq); + arm_gic_irq_set_priority(config->irq, config->prio, flags); + arm_gic_irq_clear_pending(config->irq); #else - NVIC_ClearPendingIRQ(config->irq); + NVIC_ClearPendingIRQ(config->irq); #endif - data->trigger_type = trig; + data->trigger_type = trig; + } return 0; } @@ -248,16 +253,18 @@ int intc_rz_tint_connect(const struct device *dev, uint8_t port, uint8_t pin) return -EINVAL; } - uint32_t reg_val = REG_TSSR_READ(base, tint); + K_SPINLOCK(&lock) { + uint32_t reg_val = REG_TSSR_READ(base, tint); - reg_val &= ~(REG_TSSR_TSSEL_MASK(tint) | REG_TSSR_TIEN_MASK(tint)); - reg_val |= FIELD_PREP(REG_TSSR_TSSEL_MASK(tint), gpioint); - reg_val |= FIELD_PREP(REG_TSSR_TIEN_MASK(tint), 1U); - REG_TSSR_WRITE(base, tint, reg_val); + reg_val &= ~(REG_TSSR_TSSEL_MASK(tint) | REG_TSSR_TIEN_MASK(tint)); + reg_val |= FIELD_PREP(REG_TSSR_TSSEL_MASK(tint), gpioint); + reg_val |= FIELD_PREP(REG_TSSR_TIEN_MASK(tint), 1U); + REG_TSSR_WRITE(base, tint, reg_val); - data->gpioint = gpioint; - data->port = port; - data->pin = pin; + data->gpioint = gpioint; + data->port = port; + data->pin = pin; + } return 0; } From 0724d4c7947e1f50a49bed5f0ab9adbd4f2667a0 Mon Sep 17 00:00:00 2001 From: Nhut Nguyen Date: Wed, 15 Jul 2026 16:06:02 +0700 Subject: [PATCH 030/455] drivers: intc: renesas: Fix status clear Fix intc_rz_tint_clear_irq_status() to only clear the TINT status flag when it is actually set, since writing to it otherwise has no effect per the register spec. Add explanatory comments describing the V2H/V2N vs other SoC register differences and the interrupt-setting precautions from the user's manual. Signed-off-by: Nhut Nguyen --- .../intc_renesas_rz_tint.c | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/interrupt_controller/intc_renesas_rz_tint.c b/drivers/interrupt_controller/intc_renesas_rz_tint.c index 670ed48968b6..38448ccd0514 100644 --- a/drivers/interrupt_controller/intc_renesas_rz_tint.c +++ b/drivers/interrupt_controller/intc_renesas_rz_tint.c @@ -79,6 +79,10 @@ struct intc_rz_tint_data { static const uint8_t gpioint_table[] = DT_PROP(DT_NODELABEL(intc), gpioint_table); static struct k_spinlock lock; +/* Helper function to read TINT status + * V2H, V2N: TSTATn bit of TSCTR register + * Others: TSTATSn bit of TSCR register + */ static inline bool intc_rz_tint_status_read(mem_addr_t base, uint8_t tint) { #ifdef CONFIG_RENESAS_RZ_TINT_SUPPORT_STATUS_CLEAR_REG @@ -89,6 +93,10 @@ static inline bool intc_rz_tint_status_read(mem_addr_t base, uint8_t tint) #endif /* CONFIG_RENESAS_RZ_TINT_SUPPORT_STATUS_CLEAR_REG */ } +/* Helper function to clear TINT status + * V2H, V2N: TCLRn bit of TSCLR register + * Others: TSTATSn bit of TSCR register + */ static inline void intc_rz_tint_status_clear(mem_addr_t base, uint8_t tint) { #ifdef CONFIG_RENESAS_RZ_TINT_SUPPORT_STATUS_CLEAR_REG @@ -99,19 +107,27 @@ static inline void intc_rz_tint_status_clear(mem_addr_t base, uint8_t tint) #endif /* CONFIG_RENESAS_RZ_TINT_SUPPORT_STATUS_CLEAR_REG */ } +/* Helper function to perform the process of clearing the interrupt status after changing + * the setting as per User's manual "Precaution when Changing Interrupt Settings". + */ static inline void intc_rz_tint_clear_irq_status(const struct device *dev) { const struct intc_rz_tint_config *config = dev->config; mem_addr_t base = INTC_BASE; uint8_t tint = config->tint; - intc_rz_tint_status_clear(base, tint); - - /* - * User's manual: Clear Timing of Interrupt Cause - * Dummy read is required after write + /* As per TSCLR register (V2H, V2N, G3E) and TSCR register (A3x, G2x, G3S, V2L) + * Only clear TINTn status flags when it is 1. Otherwise, writing has no effect */ - (void)intc_rz_tint_status_read(base, tint); + if (intc_rz_tint_status_read(base, tint)) { + intc_rz_tint_status_clear(base, tint); + + /* + * User's manual: Clear Timing of Interrupt Cause + * Dummy read is required after write + */ + (void)intc_rz_tint_status_read(base, tint); + } } int intc_rz_tint_enable(const struct device *dev) From a035e179fbd96b77b9cfdcdc17c15b2fa50d5b58 Mon Sep 17 00:00:00 2001 From: Nhut Nguyen Date: Wed, 15 Jul 2026 16:11:19 +0700 Subject: [PATCH 031/455] drivers: intc: renesas: Limit GIC flags set to RZ/V2H Only RZ/V2H supports both GIC interrupt types level/edge for TINT, while other RZ series keep GIC interrupt type unchanged regardless of TINT trigger type. Signed-off-by: Nhut Nguyen --- drivers/interrupt_controller/intc_renesas_rz_tint.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/interrupt_controller/intc_renesas_rz_tint.c b/drivers/interrupt_controller/intc_renesas_rz_tint.c index 38448ccd0514..3bf82f6066b6 100644 --- a/drivers/interrupt_controller/intc_renesas_rz_tint.c +++ b/drivers/interrupt_controller/intc_renesas_rz_tint.c @@ -153,7 +153,6 @@ int intc_rz_tint_set_type(const struct device *dev, enum intc_rz_tint_trigger tr const struct intc_rz_tint_config *config = dev->config; struct intc_rz_tint_data *data = dev->data; uint8_t tint = config->tint; - uint32_t flags = IRQ_TYPE_LEVEL; uint32_t set = 0; mem_addr_t base = INTC_BASE; uint32_t reg_val; @@ -189,13 +188,17 @@ int intc_rz_tint_set_type(const struct device *dev, enum intc_rz_tint_trigger tr * write 0 to the TSTATn bit of TSCR. */ if ((trig == RZ_TINT_RISING_EDGE) || (trig == RZ_TINT_FAILING_EDGE)) { - flags = IRQ_TYPE_EDGE; intc_rz_tint_clear_irq_status(dev); } /* Set interrupt type for GIC, and clear pending interrupt */ #ifdef CONFIG_GIC +#if defined(CONFIG_SOC_SERIES_RZV2H) + uint32_t flags = ((trig == RZ_TINT_RISING_EDGE) || (trig == RZ_TINT_FAILING_EDGE)) + ? IRQ_TYPE_EDGE : IRQ_TYPE_LEVEL; + arm_gic_irq_set_priority(config->irq, config->prio, flags); +#endif arm_gic_irq_clear_pending(config->irq); #else NVIC_ClearPendingIRQ(config->irq); From 322b2d2d254539529f82fd5fee88d4d3cfd77da4 Mon Sep 17 00:00:00 2001 From: Nhut Nguyen Date: Wed, 15 Jul 2026 13:21:55 +0700 Subject: [PATCH 032/455] drivers: intc: renesas: Prevent duplicate gpioint/tint mapping Add a bitmap to track which gpioint values are already assigned to a tint, and reject connect requests that would either remap an already-assigned tint or reuse a gpioint that's already in use. Initialize data->gpioint to 0xFF (unassigned) at device init so the new checks work correctly on first connect. Signed-off-by: Nhut Nguyen --- .../intc_renesas_rz_tint.c | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/interrupt_controller/intc_renesas_rz_tint.c b/drivers/interrupt_controller/intc_renesas_rz_tint.c index 3bf82f6066b6..9e50ca5e1687 100644 --- a/drivers/interrupt_controller/intc_renesas_rz_tint.c +++ b/drivers/interrupt_controller/intc_renesas_rz_tint.c @@ -60,6 +60,9 @@ DEVICE_MMIO_TOPLEVEL_STATIC(intc_regs, DT_NODELABEL(intc)); #define REG_INTSEL_WRITE(base, irq, v) sys_write32((v), INTSEL(base) + (OFFSET(irq) / 3) * 4) #define REG_INTSEL_SPIk_SEL_MASK(irq) (BIT_MASK(10) << ((OFFSET(irq) % 3) * 10)) +/* data->gpioint value for a tint channel that is not connected to a GPIO pin yet */ +#define GPIOINT_UNASSIGNED 0xFFU + struct intc_rz_tint_config { uint8_t tint; uint8_t max_gpioint; @@ -78,6 +81,9 @@ struct intc_rz_tint_data { static const uint8_t gpioint_table[] = DT_PROP(DT_NODELABEL(intc), gpioint_table); static struct k_spinlock lock; +#if defined(CONFIG_ASSERT) +static bool used_gpioint[DT_PROP(DT_NODELABEL(intc), max_gpioint) + 1]; +#endif /* Helper function to read TINT status * V2H, V2N: TSTATn bit of TSCTR register @@ -268,13 +274,29 @@ int intc_rz_tint_connect(const struct device *dev, uint8_t port, uint8_t pin) /* Map to GPIOINT */ uint8_t gpioint = gpioint_table[port] + pin; - if (gpioint > config->max_gpioint) { - return -EINVAL; - } + __ASSERT(gpioint <= config->max_gpioint, + "port %u pin %u maps to gpioint %u, out of range (max %u)", port, pin, gpioint, + config->max_gpioint); K_SPINLOCK(&lock) { - uint32_t reg_val = REG_TSSR_READ(base, tint); + uint32_t reg_val; + + /* Already mapped, no need to remap and return successfully */ + if (data->gpioint == gpioint) { + K_SPINLOCK_BREAK; + } + + __ASSERT(data->gpioint == GPIOINT_UNASSIGNED, + "tint %u already assigned to port %u pin %u", tint, + data->port, data->pin); + __ASSERT(!used_gpioint[gpioint], + "port %u pin %u (gpioint %u) already assigned to another tint", port, + pin, gpioint); +#if defined(CONFIG_ASSERT) + used_gpioint[gpioint] = true; +#endif + reg_val = REG_TSSR_READ(base, tint); reg_val &= ~(REG_TSSR_TSSEL_MASK(tint) | REG_TSSR_TIEN_MASK(tint)); reg_val |= FIELD_PREP(REG_TSSR_TSSEL_MASK(tint), gpioint); reg_val |= FIELD_PREP(REG_TSSR_TIEN_MASK(tint), 1U); @@ -311,6 +333,7 @@ int intc_rz_tint_set_callback(const struct device *dev, intc_rz_tint_callback_t .max_gpioint = DT_PROP(DT_INST_PARENT(index), max_gpioint), \ }; \ struct intc_rz_tint_data intc_rz_tint_data##index = { \ + .gpioint = GPIOINT_UNASSIGNED, \ .trigger_type = DT_INST_ENUM_IDX_OR(index, trigger_type, 0), \ }; \ static int intc_rz_tint_init##index(const struct device *dev) \ From 21e609b847625f626e999b410db1fac4c1c3a40d Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 21 Jan 2026 14:15:09 +0100 Subject: [PATCH 033/455] Bluetooth: CCP: Client: Add support for get bearer tech Add support for getting the remote bearer tech. Also slightly modified the test of the CCP samples to properly catch issues (e.g. we disconnect on error, and now we check for disconnect as the samples may reconnect and thus have false positives). Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/ccp.h | 43 ++++++++++- .../audio/ccp_call_control_client/prj.conf | 1 + .../audio/ccp_call_control_client/src/main.c | 60 ++++++++++++++- .../bluetooth/audio/ccp_call_control_client.c | 74 ++++++++++++++++-- .../audio/shell/ccp_call_control_client.c | 75 ++++++++++++++++++- .../src/test_procedures.c | 67 ++++++++++++++++- .../ccp_call_control_client/uut/tbs_client.c | 16 ++++ .../audio/src/ccp_call_control_client_test.c | 53 ++++++++++++- .../ccp/call_control_client/src/test_main.c | 17 ++++- .../ccp/call_control_server/src/test_main.c | 17 ++++- 10 files changed, 401 insertions(+), 22 deletions(-) diff --git a/include/zephyr/bluetooth/audio/ccp.h b/include/zephyr/bluetooth/audio/ccp.h index a3fcb8d74a47..937c00a28ff4 100644 --- a/include/zephyr/bluetooth/audio/ccp.h +++ b/include/zephyr/bluetooth/audio/ccp.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -265,6 +265,25 @@ struct bt_ccp_call_control_client_cb { const char *uci, void *user_data); #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) || defined(__DOXYGEN__) + /** + * @brief Callback function for bt_ccp_call_control_client_read_bearer_tech(). + * + * This callback is called once the read bearer technology procedure is completed. + * + * @param bearer Call Control Client bearer pointer. + * @param err Error value. 0 on success, GATT error on positive + * value or errno on negative value. + * @param tech The technology of the bearer. + * The value may be outside the values of the enum. + * @param user_data User data stored in the callback struct. Will always be NULL if + * @kconfig{CONFIG_BT_CCP_CALL_CONTROL_CLIENT_CB_USER_DATA} is not + * enabled. + */ + void (*bearer_tech)(struct bt_ccp_call_control_client_bearer *bearer, int err, + enum bt_bearer_tech tech, void *user_data); +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ + #if defined(CONFIG_BT_CCP_CALL_CONTROL_CLIENT_CB_USER_DATA) || defined(__DOXYGEN__) /** User data that will be supplied to all callbacks */ void *user_data; @@ -292,7 +311,7 @@ struct bt_ccp_call_control_client_cb { * @retval -ENOTCONN @p conn is not connected * @retval -ENOMEM Could not allocated memory for the request * @retval -EBUSY Already doing discovery for @p conn - * @retval -ENOEXEC Rejected by the GATT layer + * @retval -ENOEXEC The underlying TBS client returned an unexpected error */ int bt_ccp_call_control_client_discover(struct bt_conn *conn, struct bt_ccp_call_control_client **out_client); @@ -345,6 +364,7 @@ int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *cl * @retval -EBUSY The @ref bt_ccp_call_control_client identified by @p bearer is busy, or the TBS * instance of @p bearer is busy. * @retval -ENOTCONN The @ref bt_ccp_call_control_client identified by @p bearer is not connected + * @retval -ENOEXEC The underlying TBS client returned an unexpected error */ int bt_ccp_call_control_client_read_bearer_provider_name( struct bt_ccp_call_control_client_bearer *bearer); @@ -363,8 +383,27 @@ int bt_ccp_call_control_client_read_bearer_provider_name( * @retval -EBUSY The @ref bt_ccp_call_control_client identified by @p bearer is busy, or the TBS * instance of @p bearer is busy. * @retval -ENOTCONN The @ref bt_ccp_call_control_client identified by @p bearer is not connected + * @retval -ENOEXEC The underlying TBS client returned an unexpected error */ int bt_ccp_call_control_client_read_bearer_uci(struct bt_ccp_call_control_client_bearer *bearer); + +/** + * @brief Read the bearer technology of a remote TBS bearer. + * + * @kconfig_dep{CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY} + * + * @param bearer The bearer to read the technology from + * + * @retval 0 Success. + * @retval -EINVAL @p bearer is NULL. + * @retval -EFAULT @p bearer has not been discovered. + * @retval -EEXIST A @ref bt_ccp_call_control_client could not be identified for @p bearer. + * @retval -EBUSY The @ref bt_ccp_call_control_client identified by @p bearer is busy, or the TBS + * instance of @p bearer is busy. + * @retval -ENOTCONN The @ref bt_ccp_call_control_client identified by @p bearer is not connected. + * @retval -ENOEXEC The underlying TBS client returned an unexpected error. + */ +int bt_ccp_call_control_client_read_bearer_tech(struct bt_ccp_call_control_client_bearer *bearer); /** @} */ /* End of group bt_ccp_call_control_client */ #ifdef __cplusplus } diff --git a/samples/bluetooth/audio/ccp_call_control_client/prj.conf b/samples/bluetooth/audio/ccp_call_control_client/prj.conf index e55bcbaea1cc..083dc35d41fd 100644 --- a/samples/bluetooth/audio/ccp_call_control_client/prj.conf +++ b/samples/bluetooth/audio/ccp_call_control_client/prj.conf @@ -18,6 +18,7 @@ CONFIG_BT_TBS_CLIENT_TBS=y CONFIG_BT_TBS_CLIENT_MAX_TBS_INSTANCES=1 CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME=y CONFIG_BT_TBS_CLIENT_BEARER_UCI=y +CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY=y CONFIG_UTF8=y # TBS Client may require up to 12 buffers diff --git a/samples/bluetooth/audio/ccp_call_control_client/src/main.c b/samples/bluetooth/audio/ccp_call_control_client/src/main.c index 3d6323cbb089..200e5e7d9b64 100644 --- a/samples/bluetooth/audio/ccp_call_control_client/src/main.c +++ b/samples/bluetooth/audio/ccp_call_control_client/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -237,6 +237,24 @@ static void ccp_call_control_client_read_bearer_provider_name_cb( } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) +static void +ccp_call_control_client_read_bearer_tech_cb(struct bt_ccp_call_control_client_bearer *bearer, + int err, enum bt_bearer_tech tech, void *user_data) +{ + ARG_UNUSED(user_data); + + if (err != 0) { + LOG_ERR("Failed to read bearer %p technology: %d\n", (void *)bearer, err); + return; + } + + LOG_INF("Bearer %p technology: %d", (void *)bearer, tech); + + k_sem_give(&sem_ccp_action_completed); +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ + #if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) static void ccp_call_control_client_read_bearer_uci_cb(struct bt_ccp_call_control_client_bearer *bearer, @@ -341,6 +359,24 @@ static int read_bearer_uci(struct bt_ccp_call_control_client_bearer *bearer) return 0; } +static int read_bearer_tech(struct bt_ccp_call_control_client_bearer *bearer) +{ + int err; + + err = bt_ccp_call_control_client_read_bearer_tech(bearer); + if (err != 0) { + return err; + } + + err = k_sem_take(&sem_ccp_action_completed, SEM_TIMEOUT); + if (err != 0) { + LOG_ERR("Failed to take sem_ccp_action_completed: %d", err); + return err; + } + + return 0; +} + static int read_bearer_values(void) { int err; @@ -361,6 +397,14 @@ static int read_bearer_values(void) return err; } } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY)) { + err = read_bearer_tech(client_bearers.gtbs_bearer); + if (err != 0) { + LOG_ERR("Failed to read technology for GTBS bearer: %d", err); + return err; + } + } #endif /* CONFIG_BT_TBS_CLIENT_GTBS */ #if defined(CONFIG_BT_TBS_CLIENT_TBS) @@ -380,6 +424,15 @@ static int read_bearer_values(void) return err; } } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY)) { + err = read_bearer_tech(client_bearers.tbs_bearers[i]); + if (err != 0) { + LOG_ERR("Failed to read technology for TBS bearer[%zu]: %d", i, + err); + return err; + } + } } #endif /* CONFIG_BT_TBS_CLIENT_TBS */ @@ -396,6 +449,9 @@ static int init_ccp_call_control_client(void) #if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) .bearer_uci = ccp_call_control_client_read_bearer_uci_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) + .bearer_tech = ccp_call_control_client_read_bearer_tech_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ }; static struct bt_le_scan_cb scan_cbs = { .recv = scan_recv_cb, @@ -456,7 +512,7 @@ int main(void) continue; } - read_bearer_values(); + err = read_bearer_values(); if (err != 0) { continue; } diff --git a/subsys/bluetooth/audio/ccp_call_control_client.c b/subsys/bluetooth/audio/ccp_call_control_client.c index 21f914f077d7..5d33e0b9ba11 100644 --- a/subsys/bluetooth/audio/ccp_call_control_client.c +++ b/subsys/bluetooth/audio/ccp_call_control_client.c @@ -1,6 +1,6 @@ /* Bluetooth CCP - Call Control Profile Call Control Client * - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -310,7 +311,6 @@ int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *cl return 0; } -#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) || defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) /** * @brief Validates a bearer and provides a client with ownership of the busy flag * @@ -320,8 +320,9 @@ int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *cl * * @return 0 if the bearer is valid and the @p client has been populated, else an error. */ -static int validate_bearer_and_get_client(const struct bt_ccp_call_control_client_bearer *bearer, - struct bt_ccp_call_control_client **client) +__maybe_unused static int +validate_bearer_and_get_client(const struct bt_ccp_call_control_client_bearer *bearer, + struct bt_ccp_call_control_client **client) { if (bearer == NULL) { LOG_DBG("bearer is NULL"); @@ -350,7 +351,6 @@ static int validate_bearer_and_get_client(const struct bt_ccp_call_control_clien return 0; } -#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI || CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ #if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) static void tbs_client_read_bearer_provider_name_cb(struct bt_conn *conn, int err, @@ -480,3 +480,67 @@ int bt_ccp_call_control_client_read_bearer_uci(struct bt_ccp_call_control_client return 0; } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ + +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) +static void tbs_client_read_bearer_tech_cb(struct bt_conn *conn, int err, uint8_t inst_index, + enum bt_bearer_tech tech) +{ + struct bt_ccp_call_control_client *client = get_client_by_conn(conn); + struct bt_ccp_call_control_client_cb *listener, *next; + struct bt_ccp_call_control_client_bearer *bearer; + + atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY); + + bearer = get_bearer_by_tbs_index(client, inst_index); + if (bearer == NULL) { + LOG_DBG("Could not lookup bearer for client %p and index 0x%02X", client, + inst_index); + + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ccp_call_control_client_cbs, listener, next, _node) { + if (listener->bearer_tech != NULL) { + void *user_data = + COND_CODE_1(CONFIG_BT_CCP_CALL_CONTROL_CLIENT_CB_USER_DATA, + (listener->user_data), (NULL)); + + listener->bearer_tech(bearer, err, tech, user_data); + } + } +} + +int bt_ccp_call_control_client_read_bearer_tech(struct bt_ccp_call_control_client_bearer *bearer) +{ + struct bt_ccp_call_control_client *client; + int err; + + err = validate_bearer_and_get_client(bearer, &client); + if (err != 0) { + return err; + } + + tbs_client_cbs.technology = tbs_client_read_bearer_tech_cb; + + err = bt_tbs_client_read_technology(client->conn, bearer->tbs_index); + if (err != 0) { + atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY); + + /* Return expected return values directly */ + if (err == -ENOTCONN || err == -EBUSY) { + LOG_DBG("bt_tbs_client_read_technology returned %d", err); + + return err; + } + + /* Assert if the return value is -EINVAL as that means we are missing a check */ + __ASSERT(err != -EINVAL, "err shall not be -EINVAL"); + + LOG_DBG("Unexpected error from bt_tbs_client_read_technology: %d", err); + + return -ENOEXEC; + } + + return 0; +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ diff --git a/subsys/bluetooth/audio/shell/ccp_call_control_client.c b/subsys/bluetooth/audio/shell/ccp_call_control_client.c index 163f7dff5967..268f8ec5be61 100644 --- a/subsys/bluetooth/audio/shell/ccp_call_control_client.c +++ b/subsys/bluetooth/audio/shell/ccp_call_control_client.c @@ -3,7 +3,7 @@ */ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -84,6 +85,22 @@ static void ccp_call_control_client_bearer_uci_cb(struct bt_ccp_call_control_cli } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) +static void ccp_call_control_client_bearer_tech_cb(struct bt_ccp_call_control_client_bearer *bearer, + int err, enum bt_bearer_tech tech, + void *user_data) +{ + ARG_UNUSED(user_data); + + if (err != 0) { + bt_shell_error("Failed to read bearer %p technology: %d", (void *)bearer, err); + return; + } + + bt_shell_info("Bearer %p technology: %d", (void *)bearer, tech); +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ + static struct bt_ccp_call_control_client_cb ccp_call_control_client_cbs = { .discover = ccp_call_control_client_discover_cb, #if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) @@ -92,6 +109,9 @@ static struct bt_ccp_call_control_client_cb ccp_call_control_client_cbs = { #if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) .bearer_uci = ccp_call_control_client_bearer_uci_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) + .bearer_tech = ccp_call_control_client_bearer_tech_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ }; static int cmd_ccp_call_control_client_discover(const struct shell *sh, size_t argc, char *argv[]) @@ -129,7 +149,7 @@ static int cmd_ccp_call_control_client_discover(const struct shell *sh, size_t a return 0; } -static int validate_and_get_index(const struct shell *sh, const char *index_arg) +__maybe_unused static int validate_and_get_index(const struct shell *sh, const char *index_arg) { unsigned long index; int err = 0; @@ -150,7 +170,7 @@ static int validate_and_get_index(const struct shell *sh, const char *index_arg) return (int)index; } -static struct bt_ccp_call_control_client_bearer *get_bearer_by_index(uint8_t index) +__maybe_unused static struct bt_ccp_call_control_client_bearer *get_bearer_by_index(uint8_t index) { struct bt_ccp_call_control_client_bearers bearers; struct bt_ccp_call_control_client *client; @@ -178,9 +198,10 @@ static struct bt_ccp_call_control_client_bearer *get_bearer_by_index(uint8_t ind #if defined(CONFIG_BT_TBS_CLIENT_TBS) return bearers.tbs_bearers[index]; -#endif /* CONFIG_BT_TBS_CLIENT_GTBS */ +#endif /* CONFIG_BT_TBS_CLIENT_TBS */ } +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) static int cmd_ccp_call_control_client_read_bearer_name(const struct shell *sh, size_t argc, char *argv[]) { @@ -211,7 +232,9 @@ static int cmd_ccp_call_control_client_read_bearer_name(const struct shell *sh, return 0; } +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) static int cmd_ccp_call_control_client_read_bearer_uci(const struct shell *sh, size_t argc, char *argv[]) { @@ -242,6 +265,40 @@ static int cmd_ccp_call_control_client_read_bearer_uci(const struct shell *sh, s return 0; } +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ + +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) +static int cmd_ccp_call_control_client_read_bearer_tech(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct bt_ccp_call_control_client_bearer *bearer; + int index = 0; + int err; + + if (argc > 1) { + index = validate_and_get_index(sh, argv[1]); + if (index < 0) { + return index; + } + } + + bearer = get_bearer_by_index(index); + if (bearer == NULL) { + shell_error(sh, "Failed to get bearer for index %d", index); + + return -ENOEXEC; + } + + err = bt_ccp_call_control_client_read_bearer_tech(bearer); + if (err != 0) { + shell_error(sh, "Failed to read bearer[%d] technology: %d", index, err); + + return -ENOEXEC; + } + + return 0; +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ static int cmd_ccp_call_control_client(const struct shell *sh, size_t argc, char **argv) { @@ -258,10 +315,20 @@ SHELL_STATIC_SUBCMD_SET_CREATE(ccp_call_control_client_cmds, SHELL_CMD_ARG(discover, NULL, "Discover GTBS and TBS on remote device", cmd_ccp_call_control_client_discover, 1, 0), + +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) SHELL_CMD_ARG(read_bearer_name, NULL, "Get bearer name [index]", cmd_ccp_call_control_client_read_bearer_name, 1, 1), +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) SHELL_CMD_ARG(read_bearer_uci, NULL, "Get bearer UCI [index]", cmd_ccp_call_control_client_read_bearer_uci, 1, 1), +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) + SHELL_CMD_ARG(read_bearer_tech, NULL, + "Get bearer technology [index]", + cmd_ccp_call_control_client_read_bearer_tech, 1, 1), +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ SHELL_SUBCMD_SET_END); SHELL_CMD_ARG_REGISTER(ccp_call_control_client, &ccp_call_control_client_cmds, diff --git a/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c b/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c index 688f77244540..b0467ad3b272 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c +++ b/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c @@ -1,7 +1,7 @@ /* test_procedures.c - Testing of CCP procedures */ /* - * Copyright (c) 2024-2025 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,7 @@ struct ccp_call_control_client_procedures_test_suite_fixture { *bearers[CONFIG_BT_CCP_CALL_CONTROL_CLIENT_BEARER_COUNT]; char bearer_name[CONFIG_BT_TBS_MAX_PROVIDER_NAME_LENGTH]; char bearer_uci[BT_TBS_MAX_UCI_SIZE]; + enum bt_bearer_tech tech; }; static void discover_cb(struct bt_ccp_call_control_client *client, int err, @@ -93,6 +95,19 @@ static void bearer_uci_cb(struct bt_ccp_call_control_client_bearer *bearer, int utf8_lcpy(fixture->bearer_uci, uci, BT_TBS_MAX_UCI_SIZE); } +static void bearer_tech_cb(struct bt_ccp_call_control_client_bearer *bearer, int err, + enum bt_bearer_tech tech, void *user_data) +{ + struct ccp_call_control_client_procedures_test_suite_fixture *fixture = user_data; + + zassert_not_null(bearer); + zassert_equal(err, 0); + + zassert_not_null(user_data); + + fixture->tech = tech; +} + static void *ccp_call_control_client_procedures_test_suite_setup(void) { struct ccp_call_control_client_procedures_test_suite_fixture *fixture; @@ -114,6 +129,7 @@ static void ccp_call_control_client_procedures_test_suite_before(void *f) fixture->client_cbs.discover = discover_cb; fixture->client_cbs.bearer_provider_name = bearer_provider_name_cb; fixture->client_cbs.bearer_uci = bearer_uci_cb; + fixture->client_cbs.bearer_tech = bearer_tech_cb; fixture->client_cbs.user_data = fixture; err = bt_ccp_call_control_client_register_cb(&fixture->client_cbs); @@ -169,7 +185,7 @@ static ZTEST_F(ccp_call_control_client_procedures_test_suite, { int err; - /* Fake disconnection to clear the discovered value for the bearers*/ + /* Fake disconnection to clear the discovered value for the bearers */ mock_bt_conn_disconnected(&fixture->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); /* Mark as connected again but without discovering */ test_conn_init(&fixture->conn); @@ -214,7 +230,7 @@ static ZTEST_F(ccp_call_control_client_procedures_test_suite, { int err; - /* Fake disconnection to clear the discovered value for the bearers*/ + /* Fake disconnection to clear the discovered value for the bearers */ mock_bt_conn_disconnected(&fixture->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); /* Mark as connected again but without discovering */ test_conn_init(&fixture->conn); @@ -233,3 +249,48 @@ static ZTEST_F(ccp_call_control_client_procedures_test_suite, err = bt_ccp_call_control_client_read_bearer_uci(invalid_bearer); zassert_equal(err, -EEXIST, "Unexpected return value %d", err); } + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_tech) +{ + int err; + + err = bt_ccp_call_control_client_read_bearer_tech(fixture->bearers[0]); + zassert_equal(err, 0, "Unexpected return value %d", err); + + zassert_true(fixture->tech != 0); +} + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_tech_inval_null_bearer) +{ + int err; + + err = bt_ccp_call_control_client_read_bearer_tech(NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_tech_inval_not_discovered) +{ + int err; + + /* Fake disconnection to clear the discovered value for the bearers */ + mock_bt_conn_disconnected(&fixture->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + /* Mark as connected again but without discovering */ + test_conn_init(&fixture->conn); + + err = bt_ccp_call_control_client_read_bearer_tech(fixture->bearers[0]); + zassert_equal(err, -EFAULT, "Unexpected return value %d", err); +} + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_tech_inval_bearer) +{ + struct bt_ccp_call_control_client_bearer *invalid_bearer = + (struct bt_ccp_call_control_client_bearer *)0xdeadbeefU; + int err; + + err = bt_ccp_call_control_client_read_bearer_tech(invalid_bearer); + zassert_equal(err, -EEXIST, "Unexpected return value %d", err); +} diff --git a/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c b/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c index 9e0d5cec6cd1..89915b7e9c6a 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c +++ b/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -64,3 +65,18 @@ int bt_tbs_client_read_bearer_uci(struct bt_conn *conn, uint8_t inst_index) return 0; } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ + +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) +int bt_tbs_client_read_technology(struct bt_conn *conn, uint8_t inst_index) +{ + if (conn == NULL) { + return -ENOTCONN; + } + + if (tbs_cbs != NULL && tbs_cbs->technology != NULL) { + tbs_cbs->technology(conn, 0, inst_index, BT_BEARER_TECH_4G); + } + + return 0; +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ diff --git a/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c b/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c index a4eca39101bb..5b6cf7fed8d6 100644 --- a/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c +++ b/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ extern enum bst_result_t bst_result; CREATE_FLAG(flag_discovery_complete); CREATE_FLAG(flag_bearer_name_read); CREATE_FLAG(flag_bearer_uci); +CREATE_FLAG(flag_bearer_tech); static struct bt_ccp_call_control_client *call_control_client; static struct bt_ccp_call_control_client_bearers client_bearers; @@ -87,6 +89,23 @@ ccp_call_control_client_read_bearer_uci_cb(struct bt_ccp_call_control_client_bea SET_FLAG(flag_bearer_uci); } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) +static void +ccp_call_control_client_read_bearer_tech_cb(struct bt_ccp_call_control_client_bearer *bearer, + int err, enum bt_bearer_tech tech, void *user_data) +{ + ARG_UNUSED(user_data); + + if (err != 0) { + FAIL("Failed to read bearer %p technology: %d\n", (void *)bearer, err); + return; + } + + LOG_INF("Bearer %p technology: %d", (void *)bearer, tech); + + SET_FLAG(flag_bearer_tech); +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ static void discover_tbs(void) { @@ -96,7 +115,7 @@ static void discover_tbs(void) err = bt_ccp_call_control_client_discover(default_conn, &call_control_client); if (err != 0) { - FAIL("Failed to discover TBS: %d", err); + FAIL("Failed to discover TBS: %d\n", err); return; } @@ -111,7 +130,7 @@ static void read_bearer_name(struct bt_ccp_call_control_client_bearer *bearer) err = bt_ccp_call_control_client_read_bearer_provider_name(bearer); if (err != 0) { - FAIL("Failed to read name of bearer %p: %d", bearer, err); + FAIL("Failed to read name of bearer %p: %d\n", bearer, err); return; } @@ -126,13 +145,28 @@ static void read_bearer_uci(struct bt_ccp_call_control_client_bearer *bearer) err = bt_ccp_call_control_client_read_bearer_uci(bearer); if (err != 0) { - FAIL("Failed to read UCI of bearer %p: %d", bearer, err); + FAIL("Failed to read UCI of bearer %p: %d\n", bearer, err); return; } WAIT_FOR_FLAG(flag_bearer_uci); } +static void read_bearer_tech(struct bt_ccp_call_control_client_bearer *bearer) +{ + int err; + + UNSET_FLAG(flag_bearer_tech); + + err = bt_ccp_call_control_client_read_bearer_tech(bearer); + if (err != 0) { + FAIL("Failed to read technology of bearer %p: %d\n", bearer, err); + return; + } + + WAIT_FOR_FLAG(flag_bearer_tech); +} + static void read_bearer_values(void) { #if defined(CONFIG_BT_TBS_CLIENT_GTBS) @@ -143,6 +177,10 @@ static void read_bearer_values(void) if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_UCI)) { read_bearer_uci(client_bearers.gtbs_bearer); } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY)) { + read_bearer_tech(client_bearers.gtbs_bearer); + } #endif /* CONFIG_BT_TBS_CLIENT_GTBS */ #if defined(CONFIG_BT_TBS_CLIENT_TBS) @@ -154,6 +192,10 @@ static void read_bearer_values(void) if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_UCI)) { read_bearer_uci(client_bearers.tbs_bearers[i]); } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY)) { + read_bearer_tech(client_bearers.tbs_bearers[i]); + } } #endif /* CONFIG_BT_TBS_CLIENT_TBS */ } @@ -168,6 +210,9 @@ static void init(void) #if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) .bearer_uci = ccp_call_control_client_read_bearer_uci_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) + .bearer_tech = ccp_call_control_client_read_bearer_tech_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ }; int err; diff --git a/tests/bsim/bluetooth/audio_samples/ccp/call_control_client/src/test_main.c b/tests/bsim/bluetooth/audio_samples/ccp/call_control_client/src/test_main.c index 50f46cd17a77..b984165a3d12 100644 --- a/tests/bsim/bluetooth/audio_samples/ccp/call_control_client/src/test_main.c +++ b/tests/bsim/bluetooth/audio_samples/ccp/call_control_client/src/test_main.c @@ -1,10 +1,13 @@ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include +#include + +#include #include #include "bs_types.h" @@ -49,6 +52,18 @@ static void test_ccp_call_control_client_sample_tick(bs_time_t HW_device_time) } } +static void disconnected_cb(struct bt_conn *conn, uint8_t reason) +{ + ARG_UNUSED(conn); + ARG_UNUSED(reason); + + FAIL("CCP Call Control Client sample FAILED\n"); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .disconnected = disconnected_cb, +}; + static const struct bst_test_instance test_sample[] = { { .test_id = "ccp_call_control_client", diff --git a/tests/bsim/bluetooth/audio_samples/ccp/call_control_server/src/test_main.c b/tests/bsim/bluetooth/audio_samples/ccp/call_control_server/src/test_main.c index 725726d05168..b6c3d373bbc9 100644 --- a/tests/bsim/bluetooth/audio_samples/ccp/call_control_server/src/test_main.c +++ b/tests/bsim/bluetooth/audio_samples/ccp/call_control_server/src/test_main.c @@ -1,10 +1,13 @@ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include +#include + +#include #include #include "bs_types.h" @@ -44,6 +47,18 @@ static void test_ccp_call_control_server_sample_tick(bs_time_t HW_device_time) PASS("CCP Call Control Server sample PASSED\n"); } +static void disconnected_cb(struct bt_conn *conn, uint8_t reason) +{ + ARG_UNUSED(conn); + ARG_UNUSED(reason); + + FAIL("CCP Call Control Server sample FAILED\n"); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .disconnected = disconnected_cb, +}; + static const struct bst_test_instance test_sample[] = { { .test_id = "ccp_call_control_server", From b88d7dfd98824339c2fa244f1ef504b90a396070 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 15 Jul 2026 11:26:35 +0200 Subject: [PATCH 034/455] Bluetooth: CCP: Client: Modify how TBS callbacks are registered Instead of assigning the individual callbacks at read time, they are now all assigned at once. The main reason for this is to be able to handle notifications that come, that use the same callbacks, before any reads have been done. Notifications would otherwise have been missed, if the application did not perform a read first. The CONN_CB functions were just moved to avoid needing function prototypes. Signed-off-by: Emil Gydesen --- .../bluetooth/audio/ccp_call_control_client.c | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/subsys/bluetooth/audio/ccp_call_control_client.c b/subsys/bluetooth/audio/ccp_call_control_client.c index 5d33e0b9ba11..66bb4e885bf7 100644 --- a/subsys/bluetooth/audio/ccp_call_control_client.c +++ b/subsys/bluetooth/audio/ccp_call_control_client.c @@ -29,9 +29,6 @@ LOG_MODULE_REGISTER(bt_ccp_call_control_client, CONFIG_BT_CCP_CALL_CONTROL_CLIEN static sys_slist_t ccp_call_control_client_cbs = SYS_SLIST_STATIC_INIT(&ccp_call_control_client_cbs); -static struct bt_tbs_client_cb tbs_client_cbs; - -static struct bt_tbs_client_cb tbs_client_cbs; /* A service instance can either be a GTBS or a TBS instance */ struct bt_ccp_call_control_client_bearer { @@ -88,45 +85,6 @@ static struct bt_ccp_call_control_client *get_client_by_conn(const struct bt_con return &clients[bt_conn_index(conn)]; } -static void connected_cb(struct bt_conn *conn, uint8_t err) -{ - static bool cbs_registered; - - ARG_UNUSED(conn); - - /* We register the callbacks in the connected callback. That way we ensure that they are - * registered before any procedures are completed or we receive any notifications, while - * registering them as late as possible - */ - if (err == BT_HCI_ERR_SUCCESS && !cbs_registered) { - int cb_err; - - cb_err = bt_tbs_client_register_cb(&tbs_client_cbs); - __ASSERT(cb_err == 0, "Failed to register TBS callbacks: %d", cb_err); - - cbs_registered = true; - } -} - -static void disconnected_cb(struct bt_conn *conn, uint8_t reason) -{ - struct bt_ccp_call_control_client *client = get_client_by_conn(conn); - - ARG_UNUSED(reason); - - /* client->conn may be NULL */ - if (client->conn == conn) { - bt_conn_drop(&client->conn); - - memset(client->bearers, 0, sizeof(client->bearers)); - } -} - -BT_CONN_CB_DEFINE(conn_callbacks) = { - .connected = connected_cb, - .disconnected = disconnected_cb, -}; - static void populate_bearers(struct bt_ccp_call_control_client *client, struct bt_ccp_call_control_client_bearers *bearers) { @@ -228,8 +186,6 @@ int bt_ccp_call_control_client_discover(struct bt_conn *conn, return -EBUSY; } - tbs_client_cbs.discover = tbs_client_discover_cb; - ref = bt_conn_ref(conn); if (ref == NULL) { err = -ENOTCONN; @@ -392,8 +348,6 @@ int bt_ccp_call_control_client_read_bearer_provider_name( return err; } - tbs_client_cbs.bearer_provider_name = tbs_client_read_bearer_provider_name_cb; - err = bt_tbs_client_read_bearer_provider_name(client->conn, bearer->tbs_index); if (err != 0) { atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY); @@ -456,8 +410,6 @@ int bt_ccp_call_control_client_read_bearer_uci(struct bt_ccp_call_control_client return err; } - tbs_client_cbs.bearer_uci = tbs_client_read_bearer_uci_cb; - err = bt_tbs_client_read_bearer_uci(client->conn, bearer->tbs_index); if (err != 0) { atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY); @@ -520,8 +472,6 @@ int bt_ccp_call_control_client_read_bearer_tech(struct bt_ccp_call_control_clien return err; } - tbs_client_cbs.technology = tbs_client_read_bearer_tech_cb; - err = bt_tbs_client_read_technology(client->conn, bearer->tbs_index); if (err != 0) { atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY); @@ -544,3 +494,54 @@ int bt_ccp_call_control_client_read_bearer_tech(struct bt_ccp_call_control_clien return 0; } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ + +static void connected_cb(struct bt_conn *conn, uint8_t err) +{ + static bool cbs_registered; + + ARG_UNUSED(conn); + + /* We register the callbacks in the connected callback. That way we ensure that they are + * registered before any procedures are completed or we receive any notifications, while + * registering them as late as possible + */ + if (err == BT_HCI_ERR_SUCCESS && !cbs_registered) { + static struct bt_tbs_client_cb tbs_client_cbs = { + .discover = tbs_client_discover_cb, +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) + .bearer_provider_name = tbs_client_read_bearer_provider_name_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) + .bearer_uci = tbs_client_read_bearer_uci_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) + .technology = tbs_client_read_bearer_tech_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY */ + }; + __maybe_unused int cb_err; + + cb_err = bt_tbs_client_register_cb(&tbs_client_cbs); + __ASSERT(cb_err == 0, "Failed to register TBS callbacks: %d", cb_err); + + cbs_registered = true; + } +} + +static void disconnected_cb(struct bt_conn *conn, uint8_t reason) +{ + struct bt_ccp_call_control_client *client = get_client_by_conn(conn); + + ARG_UNUSED(reason); + + /* client->conn may be NULL */ + if (client->conn == conn) { + bt_conn_drop(&client->conn); + + memset(client->bearers, 0, sizeof(client->bearers)); + } +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected_cb, + .disconnected = disconnected_cb, +}; From 792e36cc4f98f358cec039f19ffd327b56abbcce Mon Sep 17 00:00:00 2001 From: Janez Ugovsek Date: Sun, 9 Aug 2026 23:04:36 +0200 Subject: [PATCH 035/455] drivers: eeprom: add RV3028 EEPROM support Add an EEPROM child driver for the Micro Crystal RV-3028 RTC. The driver provides EEPROM read and write access through the RV3028 MFD parent driver. Signed-off-by: Janez Ugovsek --- drivers/eeprom/CMakeLists.txt | 1 + drivers/eeprom/Kconfig | 1 + drivers/eeprom/Kconfig.rv3028 | 11 ++ drivers/eeprom/eeprom_rv3028.c | 157 ++++++++++++++++++ drivers/mfd/CMakeLists.txt | 4 + dts/bindings/mfd/microcrystal,rv3028.yaml | 4 + .../mtd/microcrystal,rv3028-eeprom.yaml | 28 ++++ tests/drivers/build_all/eeprom/app.overlay | 12 ++ 8 files changed, 218 insertions(+) create mode 100644 drivers/eeprom/Kconfig.rv3028 create mode 100644 drivers/eeprom/eeprom_rv3028.c create mode 100644 dts/bindings/mtd/microcrystal,rv3028-eeprom.yaml diff --git a/drivers/eeprom/CMakeLists.txt b/drivers/eeprom/CMakeLists.txt index f39b0c43493e..ce4f1911dd63 100644 --- a/drivers/eeprom/CMakeLists.txt +++ b/drivers/eeprom/CMakeLists.txt @@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_EEPROM_FM25XXX eeprom_fm25xxx.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_LPC11U6X eeprom_lpc11u6x.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RCXX eeprom_mb85rcxx.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RSXX eeprom_mb85rsxx.c) +zephyr_library_sources_ifdef(CONFIG_EEPROM_RV3028 eeprom_rv3028.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_STM32 eeprom_stm32.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_TMP11X eeprom_tmp11x.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_XEC eeprom_mchp_xec.c) diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index ec90b1e73101..5c555338890f 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -101,6 +101,7 @@ source "drivers/eeprom/Kconfig.xec" source "drivers/eeprom/Kconfig.mb85rcxx" source "drivers/eeprom/Kconfig.mb85rsxx" source "drivers/eeprom/Kconfig.fm25xxx" +source "drivers/eeprom/Kconfig.rv3028" config EEPROM_SIMULATOR bool "Simulated EEPROM driver" diff --git a/drivers/eeprom/Kconfig.rv3028 b/drivers/eeprom/Kconfig.rv3028 new file mode 100644 index 000000000000..17b2dd2964be --- /dev/null +++ b/drivers/eeprom/Kconfig.rv3028 @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Janez Ugovsek +# SPDX-License-Identifier: Apache-2.0 + +config EEPROM_RV3028 + bool "Micro Crystal RV3028 EEPROM driver" + default y + depends on DT_HAS_MICROCRYSTAL_RV3028_EEPROM_ENABLED + depends on DT_HAS_MICROCRYSTAL_RV3028_ENABLED + select MFD + help + Enable the EEPROM child driver for the Micro Crystal RV3028. diff --git a/drivers/eeprom/eeprom_rv3028.c b/drivers/eeprom/eeprom_rv3028.c new file mode 100644 index 000000000000..2c3ce3d40dc5 --- /dev/null +++ b/drivers/eeprom/eeprom_rv3028.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026 Janez Ugovsek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define DT_DRV_COMPAT microcrystal_rv3028_eeprom + +LOG_MODULE_REGISTER(eeprom_rv3028, CONFIG_EEPROM_LOG_LEVEL); + +struct rv3028_config { + const struct device *mfd; +}; + +static size_t rv3028_eeprom_size(const struct device *dev) +{ + ARG_UNUSED(dev); + + return RV3028_EEPROM_SIZE; +} + +static int rv3028_eeprom_write(const struct device *dev, off_t offset, const void *buf, size_t len) +{ + const struct rv3028_config *config = dev->config; + const uint8_t *data = buf; + int ret = 0; + + if ((offset < 0) || ((offset + len) > RV3028_EEPROM_SIZE)) { + LOG_WRN("EEPROM write out of range"); + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + mfd_rv3028_lock_sem(config->mfd); + ret = mfd_rv3028_enter_eerd(config->mfd); + if (ret) { + mfd_rv3028_unlock_sem(config->mfd); + return ret; + } + + for (size_t i = 0; i < len; i++) { + ret = mfd_rv3028_write_reg8(config->mfd, RV3028_REG_EEPROM_ADDRESS, offset + i); + if (ret) { + LOG_WRN("Cannot set EEPROM address"); + ret = -EIO; + goto unlock; + } + + ret = mfd_rv3028_write_reg8(config->mfd, RV3028_REG_EEPROM_DATA, data[i]); + if (ret) { + LOG_WRN("Cannot set EEPROM data"); + ret = -EIO; + goto unlock; + } + + ret = mfd_rv3028_eeprom_command(config->mfd, RV3028_EEPROM_CMD_WRITE); + if (ret) { + LOG_WRN("Cannot set EEPROM write command"); + ret = -EIO; + goto unlock; + } + + ret = mfd_rv3028_eeprom_wait_busy(config->mfd, RV3028_EEBUSY_WRITE_POLL_MS); + if (ret) { + LOG_WRN("EEPROM write command timed out"); + ret = -EIO; + goto unlock; + } + } + +unlock: + mfd_rv3028_exit_eerd(config->mfd); + mfd_rv3028_unlock_sem(config->mfd); + return ret; +} + +static int rv3028_eeprom_read(const struct device *dev, off_t offset, void *buf, size_t len) +{ + const struct rv3028_config *config = dev->config; + uint8_t *data = buf; + int ret = 0; + + if ((offset < 0) || ((offset + len) > RV3028_EEPROM_SIZE)) { + LOG_WRN("EEPROM read out of range"); + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + mfd_rv3028_lock_sem(config->mfd); + ret = mfd_rv3028_enter_eerd(config->mfd); + if (ret) { + mfd_rv3028_unlock_sem(config->mfd); + return ret; + } + + for (size_t i = 0; i < len; i++) { + ret = mfd_rv3028_write_reg8(config->mfd, RV3028_REG_EEPROM_ADDRESS, offset + i); + if (ret) { + LOG_WRN("Cannot set EEPROM address"); + ret = -EIO; + goto unlock; + } + + ret = mfd_rv3028_eeprom_command(config->mfd, RV3028_EEPROM_CMD_READ); + if (ret) { + LOG_WRN("Cannot set EEPROM read command"); + ret = -EIO; + goto unlock; + } + + ret = mfd_rv3028_eeprom_wait_busy(config->mfd, RV3028_EEBUSY_READ_POLL_MS); + if (ret) { + LOG_WRN("EEPROM read command timed out"); + ret = -EIO; + goto unlock; + } + + ret = mfd_rv3028_read_reg8(config->mfd, RV3028_REG_EEPROM_DATA, &data[i]); + if (ret) { + LOG_WRN("Cannot read EEPROM data"); + ret = -EIO; + goto unlock; + } + } + +unlock: + mfd_rv3028_exit_eerd(config->mfd); + mfd_rv3028_unlock_sem(config->mfd); + return ret; +} + +static DEVICE_API(eeprom, rv3028_driver_api) = { + .read = rv3028_eeprom_read, + .write = rv3028_eeprom_write, + .size = rv3028_eeprom_size, +}; + +#define INIT(inst) \ + static const struct rv3028_config rv3028_config_##inst = { \ + .mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, NULL, NULL, NULL, &rv3028_config_##inst, POST_KERNEL, \ + CONFIG_EEPROM_INIT_PRIORITY, &rv3028_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(INIT) diff --git a/drivers/mfd/CMakeLists.txt b/drivers/mfd/CMakeLists.txt index 32afd7225e63..9ca0f88b07d8 100644 --- a/drivers/mfd/CMakeLists.txt +++ b/drivers/mfd/CMakeLists.txt @@ -47,3 +47,7 @@ zephyr_library_sources_ifdef(CONFIG_MFD_TLA2528 mfd_tla2528.c) zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c) zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c) # zephyr-keep-sorted-stop + +if(CONFIG_MFD_RV3028) + zephyr_include_directories(${CMAKE_CURRENT_LIST_DIR}) +endif() diff --git a/dts/bindings/mfd/microcrystal,rv3028.yaml b/dts/bindings/mfd/microcrystal,rv3028.yaml index 3bfc4df1a118..a0b5e2d031d2 100644 --- a/dts/bindings/mfd/microcrystal,rv3028.yaml +++ b/dts/bindings/mfd/microcrystal,rv3028.yaml @@ -80,5 +80,9 @@ examples: status = "okay"; }; + rv3028_eeprom: rv3028_eeprom { + compatible = "microcrystal,rv3028-eeprom"; + status = "okay"; + }; }; }; diff --git a/dts/bindings/mtd/microcrystal,rv3028-eeprom.yaml b/dts/bindings/mtd/microcrystal,rv3028-eeprom.yaml new file mode 100644 index 000000000000..e9cb1fe5e4f2 --- /dev/null +++ b/dts/bindings/mtd/microcrystal,rv3028-eeprom.yaml @@ -0,0 +1,28 @@ +# Copyright (c) 2026 Janez Ugovsek +# SPDX-License-Identifier: Apache-2.0 + +description: Micro Crystal RV3028 RTC EEPROM driver + +compatible: "microcrystal,rv3028-eeprom" + +include: base.yaml + +examples: + - | + /* Example node for RV3028 EEPROM on the i2c0 bus. */ + &i2c0 { + status = "okay"; + clock-frequency = ; + + rv3028: rv3028@52 { + compatible = "microcrystal,rv3028"; + reg = <0x52>; + backup-switch-mode = "disabled"; + status = "okay"; + + rv3028_eeprom: rv3028_eeprom { + compatible = "microcrystal,rv3028-eeprom"; + status = "okay"; + }; + }; + }; diff --git a/tests/drivers/build_all/eeprom/app.overlay b/tests/drivers/build_all/eeprom/app.overlay index ff20fd2c83bc..e6a043d9d163 100644 --- a/tests/drivers/build_all/eeprom/app.overlay +++ b/tests/drivers/build_all/eeprom/app.overlay @@ -68,6 +68,18 @@ read-only; }; }; + + test_rv3028: rv3028@3 { + compatible = "microcrystal,rv3028"; + reg = <0x3>; + backup-switch-mode = "disabled"; + status = "okay"; + + test_rv3028_eeprom: rv3028_eeprom { + compatible = "microcrystal,rv3028-eeprom"; + status = "okay"; + }; + }; }; test_spi: spi@33334444 { From c8c1675e09921e54c6f90252137b05fb9fecc1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 14:47:38 +0200 Subject: [PATCH 036/455] cmake: request the CMake file-based API when building an SBOM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "west spdx" reads CMake's file-based API object model -- the codemodel and toolchains replies -- to work out what went into a build. A file API query has to exist in the build directory before CMake starts, so it could not be created from the build system itself. Hence "west spdx --init": a separate pass, run once per build directory before configuring, easy to forget and recoverable only by reconfiguring. CMake 3.27 added cmake_file_api(), which registers a query for the current invocation and has the reply written at generation time. Zephyr required CMake 3.20 until the floor was raised to 3.28, so the build can now ask for its own object model and the pre-configure step goes away. Tie it to CONFIG_BUILD_OUTPUT_META, which "west spdx" already requires: it reads the zephyr.meta that option emits and bails without it. Builds that are not producing an SBOM pay nothing. This also covers sysbuild, where the query had to be created in each domain's build directory -- one that does not exist until sysbuild has configured it. Each domain is an ordinary Zephyr build, so each now requests its own reply. Cost, measured on hello_world and an LVGL sample for qemu_x86 with the same tree and a warm ccache, varying only whether the query is present: no measurable configure time (-0.02 s and +0.04 s median of 6 runs, both inside run-to-run spread; CMake's generate phase does not move) and no change to no-op rebuilds. The reply costs 1.1-1.4 MB of build-directory disk. Without CONFIG_BUILD_OUTPUT_META no reply is written at all. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- CMakeLists.txt | 3 +++ doc/develop/west/zephyr-cmds.rst | 17 ++++------------- scripts/pylib/zspdx/walker.py | 5 ++++- scripts/west_commands/spdx.py | 16 +++++++++------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eed6d3cca4f5..c9aacfd4a0e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1938,6 +1938,9 @@ endif() set(KERNEL_META_PATH ${PROJECT_BINARY_DIR}/${KERNEL_META_NAME} CACHE INTERNAL "") if(CONFIG_BUILD_OUTPUT_META) + # SBOM tooling reads CMake's object model alongside this metadata. + cmake_file_api(QUERY API_VERSION 1 CODEMODEL 2 TOOLCHAINS 1) + list(APPEND post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/zephyr_module.py diff --git a/doc/develop/west/zephyr-cmds.rst b/doc/develop/west/zephyr-cmds.rst index 0d073ef214fe..f8fbd190efd5 100644 --- a/doc/develop/west/zephyr-cmds.rst +++ b/doc/develop/west/zephyr-cmds.rst @@ -100,18 +100,10 @@ richer, machine-readable build provenance described in :ref:`west-spdx-build-pro Generating SPDX documents ------------------------- -#. Pre-populate a build directory :file:`BUILD_DIR` like this: +#. Enable :kconfig:option:`CONFIG_BUILD_OUTPUT_META` in your project, so that the build + records what ``west spdx`` needs. - .. code-block:: bash - - west spdx --init -d BUILD_DIR - - This step ensures the build directory contains the CMake metadata (a CMake file-API query) - required for SPDX document generation. - -#. Enable :kconfig:option:`CONFIG_BUILD_OUTPUT_META` in your project. - -#. Build your application using this pre-created build directory, like so: +#. Build your application: .. code-block:: bash @@ -138,8 +130,7 @@ Generating SPDX documents .. code-block:: bash - west spdx --init -d BUILD_DIR/hello_world - west build -d BUILD_DIR/hello_world + west build --sysbuild -d BUILD_DIR west spdx -d BUILD_DIR/hello_world Output documents diff --git a/scripts/pylib/zspdx/walker.py b/scripts/pylib/zspdx/walker.py index 592f41562605..301c531dbb62 100644 --- a/scripts/pylib/zspdx/walker.py +++ b/scripts/pylib/zspdx/walker.py @@ -350,7 +350,10 @@ def get_reply_index_path(self): cmake_reply_dir_path = os.path.join(self.cfg.build_dir, ".cmake", "api", "v1", "reply") if not os.path.exists(cmake_reply_dir_path): _logger.error(f'cmake api reply directory {cmake_reply_dir_path} does not exist') - _logger.error('was query directory created before cmake build ran?') + _logger.error( + 're-run CMake with CONFIG_BUILD_OUTPUT_META enabled so the build ' + 'requests the file-based API reply' + ) return None if not os.path.isdir(cmake_reply_dir_path): _logger.error( diff --git a/scripts/west_commands/spdx.py b/scripts/west_commands/spdx.py index 2a1a3cd2732b..d7c2775569b3 100644 --- a/scripts/west_commands/spdx.py +++ b/scripts/west_commands/spdx.py @@ -25,11 +25,9 @@ This command creates an SPDX bill of materials following the completion of a Zephyr build. -Prior to the build, an empty file must be created at -BUILDDIR/.cmake/api/v1/query/codemodel-v2 in order to enable -the CMake file-based API, which the SPDX command relies upon. -This can be done by calling `west spdx --init` prior to -calling `west build`.""" +Enable CONFIG_BUILD_OUTPUT_META in the application and build it as usual. +The build then asks CMake for the file-based API this command reads, so the +build directory needs no preparation.""" class ZephyrSpdx(WestCommand): @@ -40,9 +38,13 @@ def do_add_parser(self, parser_adder): parser = parser_adder.add_parser(self.name, description=self.description) # If you update these options, make sure to keep the docs in - # doc/guides/west/zephyr-cmds.rst up to date. + # doc/develop/west/zephyr-cmds.rst up to date. parser.add_argument( - '-i', '--init', action="store_true", help="initialize CMake file-based API" + '-i', + '--init', + action="store_true", + help="initialize CMake file-based API; no longer required, as a build with " + "CONFIG_BUILD_OUTPUT_META requests it itself", ) parser.add_argument('-d', '--build-dir', help="build directory") parser.add_argument('-n', '--namespace-prefix', help="namespace prefix") From ddfc6f578356893be8d1c5cec4304d01b39fdec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 14:53:06 +0200 Subject: [PATCH 037/455] tests: sbom: drop the PreLoad.cmake file-API workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SBOM test seeded the CMake file API query from PreLoad.cmake, a CMake feature its own comment flags as undocumented, because the query had to exist before the configure step and there was no supported way to create it from the build system. The build now requests the object model itself when CONFIG_BUILD_OUTPUT_META is set, which this test's prj.conf already does, so the workaround has nothing left to do. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- .../software_bill_of_materials/PreLoad.cmake | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tests/application_development/software_bill_of_materials/PreLoad.cmake diff --git a/tests/application_development/software_bill_of_materials/PreLoad.cmake b/tests/application_development/software_bill_of_materials/PreLoad.cmake deleted file mode 100644 index 2691ca6703ae..000000000000 --- a/tests/application_development/software_bill_of_materials/PreLoad.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2024 Basalte bv -# SPDX-License-Identifier: Apache-2.0 - -# WARNING: the PreLoad.cmake is an undocumented feature -# We need to create the CMake file API query before the configure step - -execute_process( - COMMAND west spdx --init -d ${CMAKE_BINARY_DIR} - COMMAND_ERROR_IS_FATAL ANY -) From d8f979b0195c4fa8a5e5b81e1affc2b67199281c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 15:18:17 +0200 Subject: [PATCH 038/455] scripts: west: spdx: deprecate --init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing needs to call "west spdx --init" any more: a build with CONFIG_BUILD_OUTPUT_META asks CMake for the file-based API object model itself, so the build directory no longer has to be prepared before it is configured. Warn when it is used and note it in the docs, release notes and migration guide. The option still works, so existing scripts and CI keep running until it is removed. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- doc/develop/west/zephyr-cmds.rst | 4 ++++ doc/releases/migration-guide-4.5.rst | 5 +++++ doc/releases/release-notes-4.5.rst | 7 +++++++ scripts/west_commands/spdx.py | 9 +++++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/develop/west/zephyr-cmds.rst b/doc/develop/west/zephyr-cmds.rst index f8fbd190efd5..6aab5ceebaca 100644 --- a/doc/develop/west/zephyr-cmds.rst +++ b/doc/develop/west/zephyr-cmds.rst @@ -203,6 +203,10 @@ Command-line options ``west spdx`` accepts these additional options: +- ``-i``, ``--init``: create the CMake file-based API query in a build directory before it is + configured. Deprecated, and to be removed in Zephyr 5.0: a build with + :kconfig:option:`CONFIG_BUILD_OUTPUT_META` now requests the query itself. + - ``-n PREFIX``: a prefix for the Document Namespaces that will be included in the generated SPDX documents. See `SPDX specification clause 6`_ for details. If ``-n`` is omitted, a default namespace will be generated diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 9d07ac2271a7..0dd0d7cf5678 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -57,6 +57,11 @@ Build System now fails on one. Expand the pattern with ``file(GLOB ...)`` and pass the resulting file names instead. +* ``west spdx --init`` is deprecated and will be removed in Zephyr 5.0. A build with + :kconfig:option:`CONFIG_BUILD_OUTPUT_META` now asks CMake for the file-based API object model + that ``west spdx`` reads, so generating an SBOM no longer needs the build directory to be + prepared beforehand: build as usual, then run ``west spdx``. + Kernel ****** diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index e711d9c6bdf2..129d6173d4a6 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -274,6 +274,13 @@ Deprecated APIs and options * All functions in the video driver API (````) have moved to the video subsystem (````). Application only need to rename the ``#include``. +* West + + * ``west spdx --init`` is deprecated. A build with + :kconfig:option:`CONFIG_BUILD_OUTPUT_META` now asks CMake for the file-based API that + ``west spdx`` reads, so the build directory no longer has to be prepared before it is + configured. See :ref:`west-spdx`. + * Work queue * :c:member:`k_work_q.thread` has been deprecated. Use :c:member:`k_work_q.thread_id` instead. diff --git a/scripts/west_commands/spdx.py b/scripts/west_commands/spdx.py index d7c2775569b3..db7f6f866bf6 100644 --- a/scripts/west_commands/spdx.py +++ b/scripts/west_commands/spdx.py @@ -43,8 +43,8 @@ def do_add_parser(self, parser_adder): '-i', '--init', action="store_true", - help="initialize CMake file-based API; no longer required, as a build with " - "CONFIG_BUILD_OUTPUT_META requests it itself", + help="[DEPRECATED] initialize CMake file-based API; a build with " + "CONFIG_BUILD_OUTPUT_META now requests it itself", ) parser.add_argument('-d', '--build-dir', help="build directory") parser.add_argument('-n', '--namespace-prefix', help="namespace prefix") @@ -86,6 +86,11 @@ def do_run(self, args, unknown_args): self.do_run_spdx(args) def do_run_init(self, args): + self.wrn( + "west spdx --init is deprecated and will be removed in Zephyr 5.0: " + "a build with CONFIG_BUILD_OUTPUT_META requests the CMake file-based API " + "itself, so the build directory no longer needs to be prepared." + ) self.inf("initializing CMake file-based API prior to build") if not args.build_dir: From 15770dbc7d369ea5c0b1847f7be4fed6bf0c8eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 02:21:59 +0000 Subject: [PATCH 039/455] bluetooth: host: remove deprecated CONFIG_BT_AUTO_PHY_UPDATE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_BT_AUTO_PHY_UPDATE was deprecated in Zephyr 4.3 in favour of the role-specific BT_AUTO_PHY_CENTRAL and BT_AUTO_PHY_PERIPHERAL Kconfig choices, which also allow picking which PHY is preferred. Remove it as part of the 4.5 deprecation removal cycle. All C code and all in-tree samples and tests were already migrated to the role-specific options when the deprecation was introduced, so only the remaining Kconfig references need dropping. Since the deprecated symbol lost its "default y if !BT_USER_PHY_UPDATE" in 4.3, it has been unset in every in-tree configuration ever since, which makes each of these references dead code today: - The BT_AUTO_PHY_PERIPHERAL choice loses its "default BT_AUTO_PHY_PERIPHERAL_2M if BT_AUTO_PHY_UPDATE" entry and keeps falling through to _NONE, as it already did. - BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM loses the always-false "BT_AUTO_PHY_UPDATE=y" disjunct of its "default 6" condition, which now reads exactly as it already evaluated: BT_AUTO_DATA_LEN_UPDATE=y && BT_CTLR_LLCP_CONN < 4. - The STM32WB0 SoC defconfig set BT_AUTO_PHY_UPDATE=n, which had no effect on either role, and is simply dropped. This keeps the change behaviour-neutral. Two pre-existing quirks are deliberately left alone rather than fixed here, since they are policy changes for their respective maintainers and not part of removing a deprecated symbol: the local procedure context count no longer reaches 6 via automatic PHY update, and the STM32WB0 series follows the BT_AUTO_PHY_CENTRAL default of _2M for central-role connections. The BT_BUF_CMD_TX_COUNT help text is updated to name the replacement choices. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/migration-guide-4.5.rst | 5 +++++ doc/releases/release-notes-4.5.rst | 3 +++ soc/st/stm32/stm32wb0x/Kconfig.defconfig | 3 --- subsys/bluetooth/common/Kconfig | 5 +++-- subsys/bluetooth/controller/Kconfig.ll_sw_split | 2 +- subsys/bluetooth/host/Kconfig | 13 ------------- 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 0dd0d7cf5678..9a2a60f44c3a 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -1418,6 +1418,11 @@ Bluetooth Host should review their synchronization and callback stack requirements. See pull request :github:`93033` for details. +* ``CONFIG_BT_AUTO_PHY_UPDATE`` has been removed. Use the per-role ``BT_AUTO_PHY_CENTRAL`` + and ``BT_AUTO_PHY_PERIPHERAL`` choices instead. ``=n`` does not translate to dropping the + option: the central choice defaults to :kconfig:option:`CONFIG_BT_AUTO_PHY_CENTRAL_2M`, so + both roles must be set to ``_NONE`` explicitly. + Bluetooth Services ================== diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 129d6173d4a6..ec0f1e62b867 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -104,6 +104,9 @@ Removed APIs and options buffers unconditionally. Applications still setting these options can simply drop them. + * ``CONFIG_BT_AUTO_PHY_UPDATE``, replaced by the ``BT_AUTO_PHY_CENTRAL`` and + ``BT_AUTO_PHY_PERIPHERAL`` choices + * Build system * ``CONFIG_BUILD_NO_GAP_FILL`` diff --git a/soc/st/stm32/stm32wb0x/Kconfig.defconfig b/soc/st/stm32/stm32wb0x/Kconfig.defconfig index 5c667be44042..2393bf9c41a3 100644 --- a/soc/st/stm32/stm32wb0x/Kconfig.defconfig +++ b/soc/st/stm32/stm32wb0x/Kconfig.defconfig @@ -20,9 +20,6 @@ if BT configdefault SYSTEM_WORKQUEUE_STACK_SIZE default 1152 -config BT_AUTO_PHY_UPDATE - default n - config BT_AUTO_DATA_LEN_UPDATE default n diff --git a/subsys/bluetooth/common/Kconfig b/subsys/bluetooth/common/Kconfig index 7fd34c58fea5..f7182b21ee8e 100644 --- a/subsys/bluetooth/common/Kconfig +++ b/subsys/bluetooth/common/Kconfig @@ -173,8 +173,9 @@ config BT_BUF_CMD_TX_COUNT HCI Controllers may not support Num_HCI_Command_Packets > 1, hence they default to 1 when not enabling Controller to Host data flow control (BT_HCI_ACL_FLOW_CONTROL), Read Remote - Version Information (BT_REMOTE_VERSION), Auto-Initiate PHY update (BT_AUTO_PHY_UPDATE), or - Auto-Initiate Data Length Update (BT_AUTO_DATA_LEN_UPDATE). + Version Information (BT_REMOTE_VERSION), Auto-Initiate PHY update + (BT_AUTO_PHY_CENTRAL and BT_AUTO_PHY_PERIPHERAL choices), or Auto-Initiate Data Length + Update (BT_AUTO_DATA_LEN_UPDATE). Normal HCI commands follow the HCI command flow control using Num_HCI_Command_Packets return in HCI command complete and status. diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index 19471b361ea8..0c029fe3dbd4 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -1092,7 +1092,7 @@ config BT_CTLR_LLCP_COMMON_TX_CTRL_BUF_NUM config BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM int "Number of local control procedure contexts to be available across all connections" - default 6 if (BT_AUTO_PHY_UPDATE=y || BT_AUTO_DATA_LEN_UPDATE=y) && BT_CTLR_LLCP_CONN < 4 + default 6 if BT_AUTO_DATA_LEN_UPDATE=y && BT_CTLR_LLCP_CONN < 4 default 2 if BT_CTLR_LLCP_CONN = 1 default BT_CTLR_LLCP_CONN if BT_CTLR_LLCP_CONN > 1 range 2 $(UINT8_MAX) diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index de3063e2576d..d62b7016c60c 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -354,22 +354,9 @@ config BT_USER_PHY_UPDATE changes on the connection. The current PHY info is available in the connection info. -config BT_AUTO_PHY_UPDATE - bool "Auto-initiate PHY Update Procedure [DEPRECATED]" - select DEPRECATED - help - Initiate PHY Update Procedure on connection establishment. This will attempt - to update the connection to use 2M PHY, however it doesn't actually guarantee - that this is what will be used in the end. - - This option has been deprecated in favor of role specific options. The equivalent - behavior can be accomplished by enabling BT_AUTO_PHY_PERIPHERAL_2M and - BT_AUTO_PHY_CENTRAL_2M. - choice BT_AUTO_PHY_PERIPHERAL prompt "Auto PHY update for peripheral role" depends on BT_PERIPHERAL - default BT_AUTO_PHY_PERIPHERAL_2M if BT_AUTO_PHY_UPDATE default BT_AUTO_PHY_PERIPHERAL_NONE config BT_AUTO_PHY_PERIPHERAL_NONE From 01d3e7b5383ee2f1b5a392b5d0c3b173e60ae863 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 6 Aug 2025 08:53:26 +0200 Subject: [PATCH 040/455] tests: Bluetooth: CAP: Handover b->u unittests Add unit tests for the CAP handover broadcast to unicast functionality. Signed-off-by: Emil Gydesen --- .../audio/cap_handover/CMakeLists.txt | 1 + .../audio/cap_handover/include/cap_handover.h | 2 + .../cap_handover/include/cap_initiator.h | 1 + .../audio/cap_handover/include/test_common.h | 6 + tests/bluetooth/audio/cap_handover/prj.conf | 3 +- .../cap_handover/src/broadcast_to_unicast.c | 472 ++++++++++++++++++ .../audio/cap_handover/src/test_common.c | 106 ++-- .../cap_handover/src/unicast_to_broadcast.c | 31 +- .../uut/bap_broadcast_assistant.c | 217 ++++++-- .../audio/cap_handover/uut/cap_handover.c | 7 +- .../audio/cap_handover/uut/cap_initiator.c | 6 +- 11 files changed, 758 insertions(+), 94 deletions(-) create mode 100644 tests/bluetooth/audio/cap_handover/src/broadcast_to_unicast.c diff --git a/tests/bluetooth/audio/cap_handover/CMakeLists.txt b/tests/bluetooth/audio/cap_handover/CMakeLists.txt index 64d8c5be9809..84f1fa43412d 100644 --- a/tests/bluetooth/audio/cap_handover/CMakeLists.txt +++ b/tests/bluetooth/audio/cap_handover/CMakeLists.txt @@ -15,6 +15,7 @@ target_include_directories(app PRIVATE target_sources(app PRIVATE # Test source files src/callbacks.c + src/broadcast_to_unicast.c src/unicast_to_broadcast.c src/test_common.c diff --git a/tests/bluetooth/audio/cap_handover/include/cap_handover.h b/tests/bluetooth/audio/cap_handover/include/cap_handover.h index cf2c41eea5e3..58f4bc60e027 100644 --- a/tests/bluetooth/audio/cap_handover/include/cap_handover.h +++ b/tests/bluetooth/audio/cap_handover/include/cap_handover.h @@ -18,5 +18,7 @@ void mock_cap_handover_init(void); DECLARE_FAKE_VOID_FUNC(mock_unicast_to_broadcast_complete_cb, int, struct bt_conn *, struct bt_cap_unicast_group *, struct bt_cap_broadcast_source *); +DECLARE_FAKE_VOID_FUNC(mock_broadcast_to_unicast_complete_cb, int, struct bt_conn *, + struct bt_cap_broadcast_source *, struct bt_cap_unicast_group *); #endif /* MOCKS_CAP_HANDOVER_H_ */ diff --git a/tests/bluetooth/audio/cap_handover/include/cap_initiator.h b/tests/bluetooth/audio/cap_handover/include/cap_initiator.h index cc2b4a97ad8c..1913afad3c5e 100644 --- a/tests/bluetooth/audio/cap_handover/include/cap_initiator.h +++ b/tests/bluetooth/audio/cap_handover/include/cap_initiator.h @@ -17,5 +17,6 @@ extern const struct bt_cap_initiator_cb mock_cap_initiator_cb; void mock_cap_initiator_init(void); DECLARE_FAKE_VOID_FUNC(mock_unicast_start_complete_cb, int, struct bt_conn *); +DECLARE_FAKE_VOID_FUNC(mock_broadcast_start_cb, struct bt_cap_broadcast_source *); #endif /* MOCKS_CAP_INITIATOR_H_ */ diff --git a/tests/bluetooth/audio/cap_handover/include/test_common.h b/tests/bluetooth/audio/cap_handover/include/test_common.h index 9793f0416c4b..f440cb90eff9 100644 --- a/tests/bluetooth/audio/cap_handover/include/test_common.h +++ b/tests/bluetooth/audio/cap_handover/include/test_common.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,11 @@ #include "conn.h" +#define TEST_COMMON_ADV_TYPE BT_ADDR_LE_RANDOM +#define TEST_COMMON_ADV_SID 0x00U +#define TEST_COMMON_BROADCAST_ID 0x123456U +#define TEST_COMMON_SRC_ID 0x00U + void test_mocks_init(void); void test_mocks_cleanup(void); void mock_bt_csip_cleanup(void); diff --git a/tests/bluetooth/audio/cap_handover/prj.conf b/tests/bluetooth/audio/cap_handover/prj.conf index b9543256c1b8..8ae56dc1af87 100644 --- a/tests/bluetooth/audio/cap_handover/prj.conf +++ b/tests/bluetooth/audio/cap_handover/prj.conf @@ -11,9 +11,10 @@ CONFIG_ASSERT_LEVEL=2 CONFIG_ASSERT_VERBOSE=y CONFIG_LOG=y +CONFIG_BT_BAP_ISO_LOG_LEVEL_DBG=y CONFIG_BT_BAP_STREAM_LOG_LEVEL_DBG=y CONFIG_BT_CAP_COMMON_LOG_LEVEL_DBG=y -CONFIG_BT_CAP_INITIATOR_LOG_LEVEL_DBG=y CONFIG_BT_CAP_COMMANDER_LOG_LEVEL_DBG=y CONFIG_BT_CAP_HANDOVER_LOG_LEVEL_DBG=y +CONFIG_BT_CAP_INITIATOR_LOG_LEVEL_DBG=y CONFIG_BT_BAP_BROADCAST_SOURCE_LOG_LEVEL_DBG=y diff --git a/tests/bluetooth/audio/cap_handover/src/broadcast_to_unicast.c b/tests/bluetooth/audio/cap_handover/src/broadcast_to_unicast.c new file mode 100644 index 000000000000..45bffa22ff1f --- /dev/null +++ b/tests/bluetooth/audio/cap_handover/src/broadcast_to_unicast.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio/bap_endpoint.h" +#include "audio/bap_iso.h" +#include "bluetooth.h" +#include "cap_initiator.h" +#include "cap_handover.h" +#include "conn.h" +#include "expects_util.h" +#include "test_common.h" + +static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture) +{ + ARG_UNUSED(test); + ARG_UNUSED(fixture); + + test_mocks_init(); +} + +static void mock_destroy_rule_after(const struct ztest_unit_test *test, void *fixture) +{ + ARG_UNUSED(test); + ARG_UNUSED(fixture); + + test_mocks_cleanup(); +} + +ZTEST_RULE(mock_rule, mock_init_rule_before, mock_destroy_rule_after); + +#define MAX_STREAMS 2 +BUILD_ASSERT(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT >= MAX_STREAMS); +BUILD_ASSERT(CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT >= MAX_STREAMS); +BUILD_ASSERT(CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT >= MAX_STREAMS); + +struct cap_handover_broadcast_to_unicast_test_suite_fixture { + struct bt_cap_unicast_audio_start_stream_param + unicast_audio_start_stream_params[MAX_STREAMS]; + struct bt_cap_commander_broadcast_reception_stop_member_param + stop_member_params[CONFIG_BT_MAX_CONN]; + struct bt_bap_ep *snk_eps[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT]; + struct bt_cap_unicast_group_stream_pair_param unicast_group_stream_pair_params[MAX_STREAMS]; + struct bt_cap_initiator_broadcast_stream_param broadcast_stream_params[MAX_STREAMS]; + struct bt_cap_unicast_group_stream_param unicast_group_stream_params[MAX_STREAMS]; + struct bt_cap_handover_broadcast_to_unicast_param broadcast_to_unicast_param; + struct bt_cap_commander_broadcast_reception_stop_param reception_stop_param; + struct bt_cap_initiator_broadcast_create_param broadcast_create_param; + struct bt_cap_unicast_audio_start_param unicast_audio_start_param; + struct bt_cap_initiator_broadcast_subgroup_param subgroup_params; + struct bt_bap_lc3_preset unicast_presets[MAX_STREAMS]; + struct bt_cap_unicast_group_param unicast_group_param; + struct bt_cap_broadcast_source *broadcast_source; + struct bt_cap_stream cap_streams[MAX_STREAMS]; + struct bt_bap_lc3_preset broadcast_preset; + struct bt_conn conns[CONFIG_BT_MAX_CONN]; + struct bt_le_ext_adv ext_adv; +}; + +static void *cap_handover_broadcast_to_unicast_test_suite_setup(void) +{ + struct cap_handover_broadcast_to_unicast_test_suite_fixture *fixture; + + fixture = malloc(sizeof(*fixture)); + zassert_not_null(fixture); + + return fixture; +} + +static void cap_handover_broadcast_to_unicast_test_suite_before(void *f) +{ + struct cap_handover_broadcast_to_unicast_test_suite_fixture *fixture = f; + int err; + + (void)memset(fixture, 0, sizeof(*fixture)); + + err = bt_cap_initiator_register_cb(&mock_cap_initiator_cb); + zassert_equal(0, err, "Unexpected return value %d", err); + + err = bt_cap_handover_register_cb(&mock_cap_handover_cb); + zassert_equal(0, err, "Unexpected return value %d", err); + + ARRAY_FOR_EACH(fixture->conns, i) { + test_conn_init(&fixture->conns[i], i); + err = bt_bap_broadcast_assistant_discover(&fixture->conns[i]); + zassert_equal(err, 0, "Unexpected return value %d", err); + + mock_unicast_client_discover(&fixture->conns[i], fixture->snk_eps[i], NULL); + } + + /* Create advertising set */ + fixture->ext_adv.ext_adv_state = BT_LE_EXT_ADV_STATE_ENABLED; + fixture->ext_adv.per_adv_state = BT_LE_PER_ADV_STATE_ENABLED; + + fixture->broadcast_preset = (struct bt_bap_lc3_preset)BT_BAP_LC3_BROADCAST_PRESET_16_2_1( + BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED); + + fixture->broadcast_create_param.subgroup_count = 1U; + fixture->broadcast_create_param.subgroup_params = &fixture->subgroup_params; + fixture->broadcast_create_param.qos = &fixture->broadcast_preset.qos; + fixture->broadcast_create_param.packing = BT_ISO_PACKING_SEQUENTIAL; + fixture->broadcast_create_param.encryption = false; + + fixture->subgroup_params.stream_count = ARRAY_SIZE(fixture->cap_streams); + fixture->subgroup_params.stream_params = fixture->broadcast_stream_params; + fixture->subgroup_params.codec_cfg = &fixture->broadcast_preset.codec_cfg; + + ARRAY_FOR_EACH(fixture->cap_streams, i) { + fixture->broadcast_stream_params[i].stream = &fixture->cap_streams[i]; + } + + /* Start broadcast source */ + err = bt_cap_initiator_broadcast_audio_create(&fixture->broadcast_create_param, + &fixture->broadcast_source); + zassert_equal(err, 0, "Unexpected return value %d", err); + err = bt_cap_initiator_broadcast_audio_start(fixture->broadcast_source, &fixture->ext_adv); + zassert_equal(err, 0, "Unexpected return value %d", err); + zexpect_call_count("bt_cap_initiator_cb.broadcast_start_cb", 1, + mock_broadcast_start_cb_fake.call_count); + + /* Prepare default handover parameters including unicast group create parameters */ + ARRAY_FOR_EACH(fixture->unicast_presets, i) { + fixture->unicast_presets[i] = + (struct bt_bap_lc3_preset)BT_BAP_LC3_UNICAST_PRESET_16_2_1( + BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED); + } + + ARRAY_FOR_EACH(fixture->cap_streams, i) { + struct bt_cap_unicast_audio_start_stream_param *start_stream_param = + &fixture->unicast_audio_start_stream_params[i]; + struct bt_cap_unicast_group_stream_param *group_stream_param = + &fixture->unicast_group_stream_params[i]; + + /* Distribute the streams like + * [0]: conn[0] snk[0] + * [1]: conn[1] snk[0] + * [2]: conn[0] snk[1] + * [3]: conn[1] snk[1] + */ + const size_t conn_index = i % ARRAY_SIZE(fixture->conns); + const size_t ep_index = i / ARRAY_SIZE(fixture->conns); + + start_stream_param->stream = &fixture->cap_streams[i]; + start_stream_param->codec_cfg = &fixture->unicast_presets[i].codec_cfg; + + start_stream_param->member.member = &fixture->conns[conn_index]; + start_stream_param->ep = fixture->snk_eps[conn_index][ep_index]; + group_stream_param->stream = &fixture->cap_streams[i]; + group_stream_param->qos_cfg = &fixture->unicast_presets[i].qos; + + fixture->unicast_group_stream_pair_params[i].tx_param = group_stream_param; + } + + fixture->unicast_audio_start_param.type = BT_CAP_SET_TYPE_AD_HOC; + fixture->unicast_audio_start_param.count = ARRAY_SIZE(fixture->cap_streams); + fixture->unicast_audio_start_param.stream_params = + fixture->unicast_audio_start_stream_params; + + fixture->unicast_group_param.packing = BT_ISO_PACKING_SEQUENTIAL; + fixture->unicast_group_param.params_count = ARRAY_SIZE(fixture->cap_streams); + fixture->unicast_group_param.params = fixture->unicast_group_stream_pair_params; + + fixture->broadcast_to_unicast_param.broadcast_id = TEST_COMMON_BROADCAST_ID; + fixture->broadcast_to_unicast_param.adv_sid = TEST_COMMON_ADV_SID; + fixture->broadcast_to_unicast_param.adv_type = TEST_COMMON_ADV_TYPE; + fixture->broadcast_to_unicast_param.broadcast_source = fixture->broadcast_source; + fixture->broadcast_to_unicast_param.unicast_group_param = &fixture->unicast_group_param; + fixture->broadcast_to_unicast_param.unicast_start_param = + &fixture->unicast_audio_start_param; + + /* Prepare reception start parameters */ + fixture->reception_stop_param.type = fixture->unicast_audio_start_param.type; + fixture->reception_stop_param.param = fixture->stop_member_params; + fixture->reception_stop_param.count = ARRAY_SIZE(fixture->stop_member_params); + + ARRAY_FOR_EACH(fixture->stop_member_params, i) { + fixture->stop_member_params[i].member.member = &fixture->conns[i]; + fixture->stop_member_params[i].src_id = TEST_COMMON_SRC_ID; + fixture->stop_member_params[i].num_subgroups = + fixture->broadcast_create_param.subgroup_count; + } +} + +static void cap_handover_broadcast_to_unicast_test_suite_after(void *f) +{ + struct cap_handover_broadcast_to_unicast_test_suite_fixture *fixture = f; + int err; + + err = bt_cap_initiator_unregister_cb(&mock_cap_initiator_cb); + zassert_true(err == 0 || err == -EINVAL, "Unexpected error: %d", err); + + err = bt_cap_handover_unregister_cb(&mock_cap_handover_cb); + zassert_true(err == 0 || err == -EINVAL, "Unexpected error: %d", err); + + for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) { + mock_bt_conn_disconnected(&fixture->conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + + /* In the case of a test failing, we cancel the procedure so that subsequent won't fail */ + err = bt_cap_initiator_unicast_audio_cancel(); + /* May fail if no CAP procedure is in progress */ + zassert_true(err == 0 || err == -EALREADY, "Unexpected error: %d", err); + + /* In the case of a test failing, we delete the source so that subsequent tests won't fail + */ + if (fixture->broadcast_source != NULL) { + err = bt_cap_initiator_broadcast_audio_stop(fixture->broadcast_source); + zassert_true(err == 0 || err == -EALREADY || err == -EBADMSG, + "Unexpected error: %d", err); + err = bt_cap_initiator_broadcast_audio_delete(fixture->broadcast_source); + zassert_true(err == 0, "Unexpected error: %d", err); + fixture->broadcast_source = NULL; + } + + /* If a unicast group was created it exists as the 4th parameter in the callback */ + if (mock_broadcast_to_unicast_complete_cb_fake.arg3_history[0] != NULL) { + struct bt_cap_unicast_group *unicast_group = + mock_broadcast_to_unicast_complete_cb_fake.arg3_history[0]; + struct bt_cap_stream *cap_stream_ptrs[MAX_STREAMS]; + + const struct bt_cap_unicast_audio_stop_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .count = ARRAY_SIZE(fixture->cap_streams), + .streams = cap_stream_ptrs, + .release = true, + }; + + ARRAY_FOR_EACH(cap_stream_ptrs, idx) { + cap_stream_ptrs[idx] = &fixture->cap_streams[idx]; + } + + err = bt_cap_initiator_unicast_audio_stop(¶m); + zassert_true(err == 0 || err == -EALREADY, "Unexpected error: %d", err); + + err = bt_cap_unicast_group_delete(unicast_group); + zassert_true(err == 0, "Unexpected error: %d", err); + } +} + +static void cap_handover_broadcast_to_unicast_test_suite_teardown(void *f) +{ + free(f); +} + +ZTEST_SUITE(cap_handover_broadcast_to_unicast_test_suite, NULL, + cap_handover_broadcast_to_unicast_test_suite_setup, + cap_handover_broadcast_to_unicast_test_suite_before, + cap_handover_broadcast_to_unicast_test_suite_after, + cap_handover_broadcast_to_unicast_test_suite_teardown); + +static void validate_handover_callback(void) +{ + zexpect_call_count("bt_cap_initiator_cb.broadcast_to_unicast_complete_cb", 1, + mock_broadcast_to_unicast_complete_cb_fake.call_count); + zassert_equal(0, mock_broadcast_to_unicast_complete_cb_fake.arg0_history[0]); + zassert_equal_ptr(NULL, mock_broadcast_to_unicast_complete_cb_fake.arg1_history[0]); + zassert_equal_ptr(NULL, mock_broadcast_to_unicast_complete_cb_fake.arg2_history[0]); + zassert_not_equal(NULL, mock_broadcast_to_unicast_complete_cb_fake.arg3_history[0]); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, test_handover_broadcast_to_unicast) +{ + int err; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, 0, "Unexpected return value %d", err); + validate_handover_callback(); + fixture->broadcast_source = NULL; +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_reception_stop) +{ + int err; + + fixture->broadcast_to_unicast_param.reception_stop_param = &fixture->reception_stop_param; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, 0, "Unexpected return value %d", err); + validate_handover_callback(); + fixture->broadcast_source = NULL; +} + +static ZTEST(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_null_param) +{ + int err; + + err = bt_cap_handover_broadcast_to_unicast(NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_reception_stop_param_type) +{ + int err; + + /* Mismatch between unicast_start_param and this */ + fixture->reception_stop_param.type = BT_CAP_SET_TYPE_CSIP; + + fixture->broadcast_to_unicast_param.reception_stop_param = &fixture->reception_stop_param; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_reception_stop_missing_conn) +{ + int err; + + if (fixture->reception_stop_param.count == 1U) { + ztest_test_skip(); + } + + fixture->reception_stop_param.count--; + fixture->broadcast_to_unicast_param.reception_stop_param = &fixture->reception_stop_param; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_broadcast_id) +{ + int err; + + fixture->broadcast_to_unicast_param.broadcast_id = 0xFFFFFFFFU; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_adv_sid) +{ + int err; + + fixture->broadcast_to_unicast_param.adv_sid = 0xFFU; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_adv_type) +{ + int err; + + fixture->broadcast_to_unicast_param.adv_type = 0xFFU; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_null_broadcast_source) +{ + int err; + + fixture->broadcast_to_unicast_param.broadcast_source = NULL; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_null_unicast_group_param) +{ + int err; + + fixture->broadcast_to_unicast_param.unicast_group_param = NULL; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_null_unicast_start_param) +{ + int err; + + fixture->broadcast_to_unicast_param.unicast_start_param = NULL; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_unicast_stream) +{ + struct bt_cap_stream cap_stream = {0}; + int err; + + /* Attempt to use a stream not in the broadcast source */ + fixture->unicast_audio_start_stream_params[0].stream = &cap_stream; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_stream_state) +{ + int err; + + /* Attempt to use a stream not in the broadcast source */ + fixture->unicast_audio_start_stream_params[0].stream->bap_stream.ep->state = + BT_BAP_EP_STATE_QOS_CONFIGURED; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_stream_group) +{ + int err; + void *group = fixture->unicast_audio_start_stream_params[0].stream->bap_stream.group; + + /* Attempt to use a stream not in the broadcast source */ + fixture->unicast_audio_start_stream_params[0].stream->bap_stream.group = + UINT_TO_POINTER(0x12345678U); + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); + + /* Restore group to support proper cleanup after the test */ + fixture->unicast_audio_start_stream_params[0].stream->bap_stream.group = group; +} + +static ZTEST_F(cap_handover_broadcast_to_unicast_test_suite, + test_handover_broadcast_to_unicast_inval_unicast_start_stream_cnt) +{ + int err; + + if (fixture->unicast_audio_start_param.count == 1) { + ztest_test_skip(); + } + + /* Attempt to use a stream not in the broadcast source */ + fixture->unicast_audio_start_param.count -= 1; + + err = bt_cap_handover_broadcast_to_unicast(&fixture->broadcast_to_unicast_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} diff --git a/tests/bluetooth/audio/cap_handover/src/test_common.c b/tests/bluetooth/audio/cap_handover/src/test_common.c index 8cce8717b565..574519e62e0a 100644 --- a/tests/bluetooth/audio/cap_handover/src/test_common.c +++ b/tests/bluetooth/audio/cap_handover/src/test_common.c @@ -91,57 +91,65 @@ void mock_unicast_client_discover( err = bt_bap_unicast_client_register_cb(&unicast_client_cb); zassert_equal(0, err, "Unexpected return value %d", err); - RESET_FAKE(mock_bap_discover_endpoint); - - err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SINK); - zassert_equal(0, err, "Unexpected return value %d", err); - - zexpect_call_count("unicast_client_cb.bap_discover_endpoint", - CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, - mock_bap_discover_endpoint_fake.call_count); - for (size_t i = 0U; i < mock_bap_discover_endpoint_fake.call_count; i++) { - /* Verify conn */ - zassert_equal(mock_bap_discover_endpoint_fake.arg0_history[i], conn, "%p", - mock_bap_discover_endpoint_fake.arg0_history[i]); - - /* Verify dir */ - zassert_equal(mock_bap_discover_endpoint_fake.arg1_history[i], BT_AUDIO_DIR_SINK, - "%d", mock_bap_discover_endpoint_fake.arg1_history[i]); - - /* Verify and store ep */ - zassert_not_equal(mock_bap_discover_endpoint_fake.arg2_history[i], NULL, "%p", - mock_bap_discover_endpoint_fake.arg2_history[i]); - - snk_eps[i] = mock_bap_discover_endpoint_fake.arg2_history[i]; - - zassert_equal(bt_bap_ep_get_conn(snk_eps[i]), conn, "Unexpected conn %p != %p", - bt_bap_ep_get_conn(snk_eps[i]), conn); + if (snk_eps != NULL) { + RESET_FAKE(mock_bap_discover_endpoint); + + err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SINK); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("unicast_client_cb.bap_discover_endpoint", + CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, + mock_bap_discover_endpoint_fake.call_count); + for (size_t i = 0U; i < mock_bap_discover_endpoint_fake.call_count; i++) { + /* Verify conn */ + zassert_equal(mock_bap_discover_endpoint_fake.arg0_history[i], conn, "%p", + mock_bap_discover_endpoint_fake.arg0_history[i]); + + /* Verify dir */ + zassert_equal(mock_bap_discover_endpoint_fake.arg1_history[i], + BT_AUDIO_DIR_SINK, "%d", + mock_bap_discover_endpoint_fake.arg1_history[i]); + + /* Verify and store ep */ + zassert_not_equal(mock_bap_discover_endpoint_fake.arg2_history[i], NULL, + "%p", mock_bap_discover_endpoint_fake.arg2_history[i]); + + snk_eps[i] = mock_bap_discover_endpoint_fake.arg2_history[i]; + + zassert_equal(bt_bap_ep_get_conn(snk_eps[i]), conn, + "Unexpected conn %p != %p", bt_bap_ep_get_conn(snk_eps[i]), + conn); + } } - RESET_FAKE(mock_bap_discover_endpoint); - err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SOURCE); - zassert_equal(0, err, "Unexpected return value %d", err); - - zexpect_call_count("unicast_client_cb.bap_discover_endpoint", - CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT, - mock_bap_discover_endpoint_fake.call_count); - for (size_t i = 0U; i < mock_bap_discover_endpoint_fake.call_count; i++) { - /* Verify conn */ - zassert_equal(mock_bap_discover_endpoint_fake.arg0_history[i], conn, "%p", - mock_bap_discover_endpoint_fake.arg0_history[i]); - - /* Verify dir */ - zassert_equal(mock_bap_discover_endpoint_fake.arg1_history[i], BT_AUDIO_DIR_SOURCE, - "%d", mock_bap_discover_endpoint_fake.arg1_history[i]); - - /* Verify and store ep */ - zassert_not_equal(mock_bap_discover_endpoint_fake.arg2_history[i], NULL, "%p", - mock_bap_discover_endpoint_fake.arg2_history[i]); - - src_eps[i] = mock_bap_discover_endpoint_fake.arg2_history[i]; - - zassert_equal(bt_bap_ep_get_conn(src_eps[i]), conn, "Unexpected conn %p != %p", - bt_bap_ep_get_conn(src_eps[i]), conn); + if (src_eps != NULL) { + RESET_FAKE(mock_bap_discover_endpoint); + err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SOURCE); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("unicast_client_cb.bap_discover_endpoint", + CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT, + mock_bap_discover_endpoint_fake.call_count); + for (size_t i = 0U; i < mock_bap_discover_endpoint_fake.call_count; i++) { + /* Verify conn */ + zassert_equal(mock_bap_discover_endpoint_fake.arg0_history[i], conn, "%p", + mock_bap_discover_endpoint_fake.arg0_history[i]); + + /* Verify dir */ + zassert_equal(mock_bap_discover_endpoint_fake.arg1_history[i], + BT_AUDIO_DIR_SOURCE, "%d", + mock_bap_discover_endpoint_fake.arg1_history[i]); + + /* Verify and store ep */ + zassert_not_equal(mock_bap_discover_endpoint_fake.arg2_history[i], NULL, + "%p", mock_bap_discover_endpoint_fake.arg2_history[i]); + + src_eps[i] = mock_bap_discover_endpoint_fake.arg2_history[i]; + + zassert_equal(bt_bap_ep_get_conn(src_eps[i]), conn, + "Unexpected conn %p != %p", bt_bap_ep_get_conn(src_eps[i]), + conn); + } } /* We don't need the callbacks anymore */ diff --git a/tests/bluetooth/audio/cap_handover/src/unicast_to_broadcast.c b/tests/bluetooth/audio/cap_handover/src/unicast_to_broadcast.c index f1c5583e20c3..54c3b956aa9b 100644 --- a/tests/bluetooth/audio/cap_handover/src/unicast_to_broadcast.c +++ b/tests/bluetooth/audio/cap_handover/src/unicast_to_broadcast.c @@ -204,7 +204,7 @@ static void cap_handover_unicast_to_broadcast_test_suite_before(void *f) fixture->unicast_to_broadcast_param.ext_adv = &fixture->ext_adv; fixture->unicast_to_broadcast_param.unicast_group = fixture->unicast_group; fixture->unicast_to_broadcast_param.pa_interval = 0x1234U; - fixture->unicast_to_broadcast_param.broadcast_id = 0x123456U; + fixture->unicast_to_broadcast_param.broadcast_id = TEST_COMMON_BROADCAST_ID; fixture->unicast_to_broadcast_param.broadcast_create_param = &fixture->broadcast_create_param; @@ -228,16 +228,22 @@ static void cap_handover_unicast_to_broadcast_test_suite_before(void *f) static void cap_handover_unicast_to_broadcast_test_suite_after(void *f) { struct cap_handover_unicast_to_broadcast_test_suite_fixture *fixture = f; + int err; + + err = bt_cap_initiator_unregister_cb(&mock_cap_initiator_cb); + zassert_true(err == 0 || err == -EINVAL, "Unexpected error: %d", err); - (void)bt_cap_initiator_unregister_cb(&mock_cap_initiator_cb); - (void)bt_cap_handover_unregister_cb(&mock_cap_handover_cb); + err = bt_cap_handover_unregister_cb(&mock_cap_handover_cb); + zassert_true(err == 0 || err == -EINVAL, "Unexpected error: %d", err); ARRAY_FOR_EACH_PTR(fixture->conns, conn) { mock_bt_conn_disconnected(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); } /* In the case of a test failing, we cancel the procedure so that subsequent won't fail */ - (void)bt_cap_initiator_unicast_audio_cancel(); + err = bt_cap_initiator_unicast_audio_cancel(); + /* May fail if no CAP procedure is in progress */ + zassert_true(err == 0 || err == -EALREADY, "Unexpected error: %d", err); /* In the case of a test failing, we delete the group so that subsequent tests won't fail */ if (fixture->unicast_group != NULL) { @@ -254,8 +260,12 @@ static void cap_handover_unicast_to_broadcast_test_suite_after(void *f) cap_stream_ptrs[idx] = &fixture->cap_streams[idx]; } - (void)bt_cap_initiator_unicast_audio_stop(¶m); - (void)bt_cap_unicast_group_delete(fixture->unicast_group); + err = bt_cap_initiator_unicast_audio_stop(¶m); + zassert_true(err == 0 || err == -EALREADY, "Unexpected error: %d", err); + + err = bt_cap_unicast_group_delete(fixture->unicast_group); + zassert_true(err == 0, "Unexpected error: %d", err); + fixture->unicast_group = NULL; } /* If a broadcast source was create it exists as the 4th parameter in the callback */ @@ -263,8 +273,11 @@ static void cap_handover_unicast_to_broadcast_test_suite_after(void *f) struct bt_cap_broadcast_source *broadcast_source = mock_unicast_to_broadcast_complete_cb_fake.arg3_history[0]; - (void)bt_cap_initiator_broadcast_audio_stop(broadcast_source); - (void)bt_cap_initiator_broadcast_audio_delete(broadcast_source); + err = bt_cap_initiator_broadcast_audio_stop(broadcast_source); + zassert_true(err == 0 || err == -EALREADY || err == -EBADMSG, + "Unexpected error: %d", err); + err = bt_cap_initiator_broadcast_audio_delete(broadcast_source); + zassert_true(err == 0, "Unexpected error: %d", err); } } @@ -492,7 +505,7 @@ static ZTEST_F(cap_handover_unicast_to_broadcast_test_suite, void *group = fixture->broadcast_stream_params[0].stream->bap_stream.group; /* Attempt to use a stream with invalid unicast group */ - fixture->broadcast_stream_params[0].stream->bap_stream.group = UINT_TO_POINTER(0x12345678); + fixture->broadcast_stream_params[0].stream->bap_stream.group = UINT_TO_POINTER(0x12345678U); err = bt_cap_handover_unicast_to_broadcast(&fixture->unicast_to_broadcast_param); zassert_equal(err, -EINVAL, "Unexpected return value %d", err); diff --git a/tests/bluetooth/audio/cap_handover/uut/bap_broadcast_assistant.c b/tests/bluetooth/audio/cap_handover/uut/bap_broadcast_assistant.c index 755f2ef69f75..35e7a5279e74 100644 --- a/tests/bluetooth/audio/cap_handover/uut/bap_broadcast_assistant.c +++ b/tests/bluetooth/audio/cap_handover/uut/bap_broadcast_assistant.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -15,34 +16,114 @@ #include #include #include +#include +#include +#include +#include -static struct bt_bap_broadcast_assistant_cb *broadcast_assistant_cb; +#include "test_common.h" -struct bap_broadcast_assistant_recv_state_info { - uint8_t src_id; - /** Cached PAST available */ - bool past_avail; - uint8_t adv_sid; - uint32_t broadcast_id; - bt_addr_le_t addr; -}; +static struct bt_bap_broadcast_assistant_cb *broadcast_assistant_cb; struct bap_broadcast_assistant_instance { struct bt_conn *conn; - struct bap_broadcast_assistant_recv_state_info recv_state; + struct bt_bap_scan_delegator_recv_state recv_state; /* * the following are not part of the broadcast_assistant instance, but adding them allow us * to easily check pa_sync and bis_sync states */ - enum bt_bap_pa_state pa_sync_state; - uint8_t num_subgroups; - struct bt_bap_bass_subgroup subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]; + bool past_avail; }; static struct bap_broadcast_assistant_instance broadcast_assistants[CONFIG_BT_MAX_CONN]; +static void bap_broadcast_source_started_cb(struct bt_bap_broadcast_source *source) +{ + ARG_UNUSED(source); + + ARRAY_FOR_EACH_PTR(broadcast_assistants, assistant) { + bool receive_state_callback = true; + + if (assistant->conn == NULL) { + continue; + } + + assistant->recv_state.adv_sid = TEST_COMMON_ADV_SID; + assistant->recv_state.addr.type = TEST_COMMON_ADV_TYPE; + assistant->recv_state.broadcast_id = TEST_COMMON_BROADCAST_ID; + + /* If we have called the recv_state callback as part of add, mod or rem source + * operations, we should not call it here again + */ + for (uint8_t i = 0U; i < assistant->recv_state.num_subgroups; i++) { + if (assistant->recv_state.subgroups[i].bis_sync != 0U) { + receive_state_callback = false; + break; + } + /* Set the BIS sync to any valid value */ + assistant->recv_state.subgroups[i].bis_sync = BIT(i); + } + + if (receive_state_callback && broadcast_assistant_cb != NULL && + broadcast_assistant_cb->recv_state != NULL) { + broadcast_assistant_cb->recv_state(assistant->conn, 0, + &assistant->recv_state); + } + } +} + +static void bap_broadcast_source_stopped_cb(struct bt_bap_broadcast_source *source, uint8_t reason) +{ + ARG_UNUSED(source); + ARG_UNUSED(reason); + + ARRAY_FOR_EACH_PTR(broadcast_assistants, assistant) { + bool receive_state_callback = true; + + if (assistant->conn == NULL) { + continue; + } + + assistant->recv_state.adv_sid = TEST_COMMON_ADV_SID; + assistant->recv_state.addr.type = TEST_COMMON_ADV_TYPE; + assistant->recv_state.broadcast_id = TEST_COMMON_BROADCAST_ID; + + /* If we have called the recv_state callback as part of add, mod or rem source + * operations, we should not call it here again + */ + for (uint8_t i = 0U; i < assistant->recv_state.num_subgroups; i++) { + if (assistant->recv_state.subgroups[i].bis_sync == 0) { + receive_state_callback = false; + break; + } + } + + if (receive_state_callback && broadcast_assistant_cb != NULL && + broadcast_assistant_cb->recv_state != NULL) { + broadcast_assistant_cb->recv_state(assistant->conn, 0, + &assistant->recv_state); + } + } +} + int bt_bap_broadcast_assistant_register_cb(struct bt_bap_broadcast_assistant_cb *cb) { + static bool broadcast_source_cbs_registered; + + if (!broadcast_source_cbs_registered) { + static struct bt_bap_broadcast_source_cb bap_broadcast_source_cb = { + .started = bap_broadcast_source_started_cb, + .stopped = bap_broadcast_source_stopped_cb, + }; + const int err = bt_bap_broadcast_source_register_cb(&bap_broadcast_source_cb); + + if (err != 0) { + return err; + } + + broadcast_source_cbs_registered = true; + } + broadcast_assistant_cb = cb; return 0; @@ -50,51 +131,50 @@ int bt_bap_broadcast_assistant_register_cb(struct bt_bap_broadcast_assistant_cb static struct bap_broadcast_assistant_instance *inst_by_conn(struct bt_conn *conn) { - struct bap_broadcast_assistant_instance *inst; + struct bap_broadcast_assistant_instance *assistant; __ASSERT(conn != NULL, "conn is NULL"); - inst = &broadcast_assistants[bt_conn_index(conn)]; + assistant = &broadcast_assistants[bt_conn_index(conn)]; - return inst; + return assistant; } int bt_bap_broadcast_assistant_add_src(struct bt_conn *conn, const struct bt_bap_broadcast_assistant_add_src_param *param) { - struct bap_broadcast_assistant_instance *inst; - struct bt_bap_scan_delegator_recv_state state; + struct bap_broadcast_assistant_instance *assistant; /* Note that proper parameter checking is done in the caller */ __ASSERT(conn != NULL, "conn is NULL"); __ASSERT(param != NULL, "param is NULL"); - inst = inst_by_conn(conn); - __ASSERT(inst != NULL, "inst is NULL"); + assistant = inst_by_conn(conn); + __ASSERT(assistant != NULL, "assistant is NULL"); + __ASSERT(param->adv_sid == TEST_COMMON_ADV_SID, "Unexpected param->adv_sid: 0x%02X", + param->adv_sid); + __ASSERT(param->addr.type == TEST_COMMON_ADV_TYPE, "Unexpected param->addr.type: 0x%02X", + param->addr.type); - inst->recv_state.past_avail = false; - state.src_id = inst->recv_state.src_id = 1U; - state.adv_sid = inst->recv_state.adv_sid = param->adv_sid; - state.broadcast_id = inst->recv_state.broadcast_id = param->broadcast_id; - state.pa_sync_state = inst->pa_sync_state = + assistant->recv_state.src_id = TEST_COMMON_SRC_ID; + assistant->past_avail = false; + assistant->recv_state.adv_sid = param->adv_sid; + assistant->recv_state.broadcast_id = param->broadcast_id; + assistant->recv_state.pa_sync_state = param->pa_sync ? BT_BAP_PA_STATE_SYNCED : BT_BAP_PA_STATE_NOT_SYNCED; - state.num_subgroups = inst->num_subgroups = param->num_subgroups; - bt_addr_le_copy(&inst->recv_state.addr, ¶m->addr); - bt_addr_le_copy(&state.addr, ¶m->addr); - + assistant->recv_state.num_subgroups = param->num_subgroups; for (size_t i = 0; i < param->num_subgroups; i++) { - state.subgroups[i].bis_sync = inst->subgroups[i].bis_sync = - param->subgroups[i].bis_sync; + assistant->recv_state.subgroups[i].bis_sync = param->subgroups[i].bis_sync; } - bt_addr_le_copy(&inst->recv_state.addr, ¶m->addr); + bt_addr_le_copy(&assistant->recv_state.addr, ¶m->addr); if (broadcast_assistant_cb != NULL) { if (broadcast_assistant_cb->add_src != NULL) { broadcast_assistant_cb->add_src(conn, 0); } if (broadcast_assistant_cb->recv_state != NULL) { - broadcast_assistant_cb->recv_state(conn, 0, &state); + broadcast_assistant_cb->recv_state(conn, 0, &assistant->recv_state); } } @@ -104,6 +184,36 @@ int bt_bap_broadcast_assistant_add_src(struct bt_conn *conn, int bt_bap_broadcast_assistant_mod_src(struct bt_conn *conn, const struct bt_bap_broadcast_assistant_mod_src_param *param) { + struct bap_broadcast_assistant_instance *assistant; + + zassert_not_null(conn, "conn is NULL"); + zassert_not_null(param, "param is NULL"); + + assistant = inst_by_conn(conn); + zassert_not_null(assistant, "assistant is NULL"); + + assistant->recv_state.src_id = param->src_id; + assistant->recv_state.pa_sync_state = + param->pa_sync ? BT_BAP_PA_STATE_SYNCED : BT_BAP_PA_STATE_NOT_SYNCED; + assistant->recv_state.adv_sid = TEST_COMMON_ADV_SID; + assistant->recv_state.addr.type = TEST_COMMON_ADV_TYPE; + assistant->recv_state.broadcast_id = TEST_COMMON_BROADCAST_ID; + + assistant->recv_state.num_subgroups = param->num_subgroups; + for (uint8_t i = 0U; i < param->num_subgroups; i++) { + assistant->recv_state.subgroups[i].bis_sync = param->subgroups[i].bis_sync; + } + + if (broadcast_assistant_cb != NULL) { + if (broadcast_assistant_cb->mod_src != NULL) { + broadcast_assistant_cb->mod_src(conn, 0); + } + + if (broadcast_assistant_cb->recv_state != NULL) { + broadcast_assistant_cb->recv_state(conn, 0, &assistant->recv_state); + } + } + return 0; } @@ -111,10 +221,51 @@ int bt_bap_broadcast_assistant_set_broadcast_code( struct bt_conn *conn, uint8_t src_id, const uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE]) { + ARG_UNUSED(conn); + ARG_UNUSED(src_id); + ARG_UNUSED(broadcast_code); + return 0; } int bt_bap_broadcast_assistant_rem_src(struct bt_conn *conn, uint8_t src_id) { + struct bap_broadcast_assistant_instance *assistant; + + zassert_not_null(conn, "conn is NULL"); + + assistant = inst_by_conn(conn); + zassert_not_null(assistant, "assistant is NULL"); + zassert_equal(src_id, assistant->recv_state.src_id, "Invalid src_id"); + zassert_equal(BT_BAP_PA_STATE_NOT_SYNCED, assistant->recv_state.pa_sync_state, + "Invalid sync state"); + for (uint8_t i = 0U; i < assistant->recv_state.num_subgroups; i++) { + zassert_equal(0U, assistant->recv_state.subgroups[i].bis_sync); + } + (void)memset(&assistant->recv_state, 0, sizeof(assistant->recv_state)); + + if (broadcast_assistant_cb != NULL) { + if (broadcast_assistant_cb->rem_src != NULL) { + broadcast_assistant_cb->rem_src(conn, 0); + } + + if (broadcast_assistant_cb->recv_state_removed != NULL) { + broadcast_assistant_cb->recv_state_removed(conn, src_id); + } + } + + return 0; +} + +int bt_bap_broadcast_assistant_discover(struct bt_conn *conn) +{ + struct bap_broadcast_assistant_instance *assistant; + + zassert_not_null(conn, "conn is NULL"); + + assistant = inst_by_conn(conn); + zassert_not_null(assistant, "assistant is NULL"); + assistant->conn = conn; + return 0; } diff --git a/tests/bluetooth/audio/cap_handover/uut/cap_handover.c b/tests/bluetooth/audio/cap_handover/uut/cap_handover.c index 2ea442430ba5..1d98e2dff969 100644 --- a/tests/bluetooth/audio/cap_handover/uut/cap_handover.c +++ b/tests/bluetooth/audio/cap_handover/uut/cap_handover.c @@ -12,13 +12,18 @@ #include "cap_handover.h" /* List of fakes used by this unit tester */ -#define FFF_FAKES_LIST(FAKE) FAKE(mock_unicast_to_broadcast_complete_cb) +#define FFF_FAKES_LIST(FAKE) \ + FAKE(mock_unicast_to_broadcast_complete_cb) \ + FAKE(mock_broadcast_to_unicast_complete_cb) DEFINE_FAKE_VOID_FUNC(mock_unicast_to_broadcast_complete_cb, int, struct bt_conn *, struct bt_cap_unicast_group *, struct bt_cap_broadcast_source *); +DEFINE_FAKE_VOID_FUNC(mock_broadcast_to_unicast_complete_cb, int, struct bt_conn *, + struct bt_cap_broadcast_source *, struct bt_cap_unicast_group *); const struct bt_cap_handover_cb mock_cap_handover_cb = { .unicast_to_broadcast_complete = mock_unicast_to_broadcast_complete_cb, + .broadcast_to_unicast_complete = mock_broadcast_to_unicast_complete_cb, }; void mock_cap_handover_init(void) diff --git a/tests/bluetooth/audio/cap_handover/uut/cap_initiator.c b/tests/bluetooth/audio/cap_handover/uut/cap_initiator.c index ce0a6449424a..9b2c0c8e4b13 100644 --- a/tests/bluetooth/audio/cap_handover/uut/cap_initiator.c +++ b/tests/bluetooth/audio/cap_handover/uut/cap_initiator.c @@ -12,12 +12,16 @@ #include "cap_initiator.h" /* List of fakes used by this unit tester */ -#define FFF_FAKES_LIST(FAKE) FAKE(mock_unicast_start_complete_cb) +#define FFF_FAKES_LIST(FAKE) \ + FAKE(mock_unicast_start_complete_cb) \ + FAKE(mock_broadcast_start_cb) DEFINE_FAKE_VOID_FUNC(mock_unicast_start_complete_cb, int, struct bt_conn *); +DEFINE_FAKE_VOID_FUNC(mock_broadcast_start_cb, struct bt_cap_broadcast_source *); const struct bt_cap_initiator_cb mock_cap_initiator_cb = { .unicast_start_complete = mock_unicast_start_complete_cb, + .broadcast_started = mock_broadcast_start_cb, }; void mock_cap_initiator_init(void) From e9bda9abbe8f829f2b888495862edf32435bc145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 13:00:33 +0100 Subject: [PATCH 041/455] samples: uart: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- samples/drivers/uart/passthrough/src/main.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/samples/drivers/uart/passthrough/src/main.c b/samples/drivers/uart/passthrough/src/main.c index ba872027ca57..32b7f8e23c5c 100644 --- a/samples/drivers/uart/passthrough/src/main.c +++ b/samples/drivers/uart/passthrough/src/main.c @@ -70,7 +70,7 @@ static void uart_cb(const struct device *dev, void *ctx) break; } - len = ring_buf_put_claim(patch->rx_ring_buf, &buf, RING_BUF_SIZE); + len = ring_buf_put_ptr(patch->rx_ring_buf, &buf, 0); if (len == 0) { /* no space for Rx, disable the IRQ */ uart_irq_rx_disable(patch->rx_dev); @@ -87,11 +87,7 @@ static void uart_cb(const struct device *dev, void *ctx) } len = ret; - ret = ring_buf_put_finish(patch->rx_ring_buf, len); - if (ret != 0) { - patch->rx_error = true; - break; - } + ring_buf_commit(patch->rx_ring_buf, len); } } @@ -111,7 +107,7 @@ static void passthrough(struct patch_info *patch) patch->rx_overflow = false; } - len = ring_buf_get_claim(patch->rx_ring_buf, &buf, RING_BUF_SIZE); + len = ring_buf_get_ptr(patch->rx_ring_buf, &buf, 0); if (len == 0) { goto done; } @@ -122,11 +118,7 @@ static void passthrough(struct patch_info *patch) } len = ret; - ret = ring_buf_get_finish(patch->rx_ring_buf, len); - if (ret < 0) { - goto error; - } - + ring_buf_consume(patch->rx_ring_buf, len); done: uart_irq_rx_enable(patch->rx_dev); return; From 537908a3040d796d6c0a16871341365ef2f7e3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Fri, 27 Mar 2026 22:00:24 +0100 Subject: [PATCH 042/455] console: Move to new ring_buffer zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- subsys/console/tty.c | 65 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/subsys/console/tty.c b/subsys/console/tty.c index 1f94373c8a12..f456314a0762 100644 --- a/subsys/console/tty.c +++ b/subsys/console/tty.c @@ -35,39 +35,35 @@ static void tty_uart_isr(const struct device *dev, void *user_data) static void uart_rx_handle(const struct device *dev, struct tty_serial *tty) { - uint8_t *data; + int rc; uint32_t len; - int rd_len; + uint8_t *data; bool new_data = false; - int err; - do { - len = ring_buf_put_claim(&tty->rx_buf, &data, ring_buf_capacity_get(&tty->rx_buf)); - if (len > 0) { - rd_len = uart_fifo_read(dev, data, len); - - if (rd_len > 0) { - new_data = true; - } + while (true) { + len = ring_buf_put_ptr(&tty->rx_buf, &data, 0); + if (len == 0) { + /* No space in the ring buffer - drop one byte and warn user. */ + uint8_t dummy; - err = ring_buf_put_finish(&tty->rx_buf, rd_len); - __ASSERT_NO_MSG(err == 0); - ARG_UNUSED(err); - if (rd_len < len) { - /* No more data in the FIFO, exit loop. */ + tty_write(tty, "~", 1); + if (uart_fifo_read(dev, &dummy, 1) <= 0) { break; } - } else { - uint8_t dummy; - const char dummy_char = '~'; - - /* Try to give a clue to user that some input was lost */ - tty_write(tty, &dummy_char, sizeof(dummy_char)); + continue; + } - /* No space in the ring buffer - consume byte. */ - rd_len = uart_fifo_read(dev, &dummy, 1); + rc = uart_fifo_read(dev, data, len); + __ASSERT_NO_MSG(rc >= 0); + if (rc <= 0) { + break; + } + ring_buf_commit(&tty->rx_buf, (uint32_t)rc); + new_data = true; + if (rc < len) { + break; } - } while (rd_len > 0); + } if (new_data) { k_event_post(&tty->signal_event, TTY_SIGNAL_RXRDY); @@ -76,16 +72,17 @@ static void uart_rx_handle(const struct device *dev, struct tty_serial *tty) static void uart_tx_handle(const struct device *dev, struct tty_serial *tty) { - uint32_t len; + int rc; + uint32_t available; uint8_t *data; - int err; - - len = ring_buf_get_claim(&tty->tx_buf, &data, ring_buf_capacity_get(&tty->tx_buf)); - if (len > 0) { - len = uart_fifo_fill(dev, data, len); - err = ring_buf_get_finish(&tty->tx_buf, len); - __ASSERT_NO_MSG(err == 0); - ARG_UNUSED(err); + + available = ring_buf_get_ptr(&tty->tx_buf, &data, 0); + if (available > 0) { + rc = uart_fifo_fill(dev, data, available); + __ASSERT(rc >= 0, "uart_fifo_fill() failed:%d", rc); + if (likely(rc > 0)) { + ring_buf_consume(&tty->tx_buf, (uint32_t)rc); + } } else { uart_irq_tx_disable(dev); atomic_clear(&tty->tx_busy); From 8c9ad65a5245467036c961da57f05d7c2ac3c20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 12:56:38 +0100 Subject: [PATCH 043/455] shell: backends: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- subsys/shell/backends/shell_mqtt.c | 6 +++--- subsys/shell/backends/shell_uart.c | 31 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/subsys/shell/backends/shell_mqtt.c b/subsys/shell/backends/shell_mqtt.c index 9b710e487ecf..908a80f93098 100644 --- a/subsys/shell/backends/shell_mqtt.c +++ b/subsys/shell/backends/shell_mqtt.c @@ -573,8 +573,8 @@ static void mqtt_evt_handler(struct mqtt_client *const client, const struct mqtt while (payload_left > 0) { /* Attempt to claim `payload_left` bytes of buffer in rb */ - size = (size_t)ring_buf_put_claim(&sh->rx_rb, &sh->rx_rb_ptr, - payload_left); + size = (size_t)MIN(ring_buf_put_ptr(&sh->rx_rb, &sh->rx_rb_ptr, 0), + payload_left); /* Read `size` bytes of payload from mqtt */ rc = mqtt_read_publish_payload_blocking(client, sh->rx_rb_ptr, size); @@ -586,7 +586,7 @@ static void mqtt_evt_handler(struct mqtt_client *const client, const struct mqtt size = (size_t)rc; /* Indicate that `size` bytes of payload has been written into rb */ - (void)ring_buf_put_finish(&sh->rx_rb, size); + ring_buf_commit(&sh->rx_rb, size); /* Update `payload_left` */ payload_left -= size; /* Tells the shell that we have new data for it */ diff --git a/subsys/shell/backends/shell_uart.c b/subsys/shell/backends/shell_uart.c index f562f232433d..d5f9c89de503 100644 --- a/subsys/shell/backends/shell_uart.c +++ b/subsys/shell/backends/shell_uart.c @@ -101,6 +101,7 @@ static void async_callback(const struct device *dev, struct uart_event *evt, voi static void uart_rx_handle(const struct device *dev, struct shell_uart_int_driven *sh_uart) { + int rc; uint8_t *data; uint32_t len; uint32_t rd_len; @@ -110,11 +111,12 @@ static void uart_rx_handle(const struct device *dev, struct shell_uart_int_drive #endif do { - len = ring_buf_put_claim(&sh_uart->rx_ringbuf, &data, - sh_uart->rx_ringbuf.size); + len = ring_buf_put_ptr(&sh_uart->rx_ringbuf, &data, 0); if (len > 0) { - rd_len = uart_fifo_read(dev, data, len); + rc = uart_fifo_read(dev, data, len); + __ASSERT_NO_MSG(rc >= 0); + rd_len = (rc >= 0) ? (uint32_t)rc : 0; /* If there is any new data to be either taken into * ring buffer or consumed by the SMP, signal the @@ -137,16 +139,15 @@ static void uart_rx_handle(const struct device *dev, struct shell_uart_int_drive } } #endif /* CONFIG_MCUMGR_TRANSPORT_SHELL */ - int err = ring_buf_put_finish(&sh_uart->rx_ringbuf, rd_len); - (void)err; - __ASSERT_NO_MSG(err == 0); + ring_buf_commit(&sh_uart->rx_ringbuf, rd_len); } else { uint8_t dummy; /* No space in the ring buffer - consume byte. */ LOG_WRN("RX ring buffer full."); - rd_len = uart_fifo_read(dev, &dummy, 1); + rc = uart_fifo_read(dev, &dummy, 1); + rd_len = (rc > 0) ? (uint32_t)rc : 0; #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL /* If successful in getting byte from the fifo, try * feeding it to SMP as a part of mcumgr frame. @@ -198,8 +199,9 @@ static void dtr_timer_handler(struct k_timer *timer) static void uart_tx_handle(const struct device *dev, struct shell_uart_int_driven *sh_uart) { + int rc; uint32_t len; - const uint8_t *data; + uint8_t *data; if (!uart_dtr_check(dev)) { /* Wait for DTR signal before sending anything to output. */ @@ -208,15 +210,12 @@ static void uart_tx_handle(const struct device *dev, struct shell_uart_int_drive return; } - len = ring_buf_get_claim(&sh_uart->tx_ringbuf, (uint8_t **)&data, - sh_uart->tx_ringbuf.size); + len = ring_buf_get_ptr(&sh_uart->tx_ringbuf, &data, 0); if (len) { - int err; - - len = uart_fifo_fill(dev, data, len); - err = ring_buf_get_finish(&sh_uart->tx_ringbuf, len); - __ASSERT_NO_MSG(err == 0); - ARG_UNUSED(err); + rc = uart_fifo_fill(dev, data, len); + __ASSERT_NO_MSG(rc >= 0); + len = (rc >= 0) ? (uint32_t)rc : 0; + ring_buf_consume(&sh_uart->tx_ringbuf, len); } else { uart_irq_tx_disable(dev); atomic_set(&sh_uart->tx_busy, 0); From 690809ecfce996418c4d26fa1a7664bbc1defb0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 12:53:44 +0100 Subject: [PATCH 044/455] drivers: serial: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- drivers/serial/uart_bitbang.c | 8 ++++---- drivers/serial/uart_bridge.c | 27 ++++++++------------------- drivers/serial/uart_bt.c | 4 ++-- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/drivers/serial/uart_bitbang.c b/drivers/serial/uart_bitbang.c index f59762ed4681..0fdfa3043f21 100644 --- a/drivers/serial/uart_bitbang.c +++ b/drivers/serial/uart_bitbang.c @@ -265,9 +265,9 @@ static void uart_bitbang_tx_counter_top_interrupt(const struct device *dev, void switch (data->tx_state) { case UART_BITBANG_IDLE: /* Claim the next data */ - size = ring_buf_get_claim(data->tx_ringbuf, (uint8_t **)&data->tx_data, - sizeof(uint16_t)); - if (size == sizeof(uint16_t)) { + size = ring_buf_get_ptr(data->tx_ringbuf, + (uint8_t **)&data->tx_data, 0); + if (sizeof(uint16_t) <= size) { /* Start next transmission */ data->tx_index = 0; data->tx_parity = uart_bitbang_compute_parity(uart_dev, *data->tx_data); @@ -333,7 +333,7 @@ static void uart_bitbang_tx_counter_top_interrupt(const struct device *dev, void break; case UART_BITBANG_COMPLETE: /* Terminate current transfer */ - ring_buf_get_finish(data->tx_ringbuf, sizeof(uint16_t)); + ring_buf_consume(data->tx_ringbuf, sizeof(uint16_t)); data->tx_state = UART_BITBANG_IDLE; break; } diff --git a/drivers/serial/uart_bridge.c b/drivers/serial/uart_bridge.c index 5f4ed48eeaf7..6d2716e581e0 100644 --- a/drivers/serial/uart_bridge.c +++ b/drivers/serial/uart_bridge.c @@ -137,8 +137,8 @@ static void uart_bridge_handle_rx(const struct device *dev, &data->peer[uart_bridge_get_idx(dev, bridge_dev, true)]; uint8_t *recv_buf; - int rb_len, recv_len; - int ret; + uint32_t rb_len; + int recv_len; if (ring_buf_space_get(&own_data->rb) < RING_BUF_FULL_THRESHOLD) { LOG_DBG("%s: buffer full: pause", dev->name); @@ -147,7 +147,7 @@ static void uart_bridge_handle_rx(const struct device *dev, return; } - rb_len = ring_buf_put_claim(&own_data->rb, &recv_buf, RING_BUF_SIZE); + rb_len = ring_buf_put_ptr(&own_data->rb, &recv_buf, 0); if (rb_len == 0) { LOG_WRN("%s: ring_buf full", dev->name); return; @@ -155,17 +155,11 @@ static void uart_bridge_handle_rx(const struct device *dev, recv_len = uart_fifo_read(dev, recv_buf, rb_len); if (recv_len < 0) { - ring_buf_put_finish(&own_data->rb, 0); LOG_ERR("%s: rx error: %d", dev->name, recv_len); return; } - ret = ring_buf_put_finish(&own_data->rb, recv_len); - if (ret < 0) { - LOG_ERR("%s: ring_buf_put_finish error: %d", dev->name, rb_len); - return; - } - + ring_buf_commit(&own_data->rb, (uint32_t)recv_len); uart_irq_tx_enable(peer_dev); } @@ -181,10 +175,10 @@ static void uart_bridge_handle_tx(const struct device *dev, &data->peer[uart_bridge_get_idx(dev, bridge_dev, false)]; uint8_t *send_buf; - int rb_len, sent_len; - int ret; + uint32_t rb_len; + int sent_len; - rb_len = ring_buf_get_claim(&peer_data->rb, &send_buf, RING_BUF_SIZE); + rb_len = ring_buf_get_ptr(&peer_data->rb, &send_buf, 0); if (rb_len == 0) { LOG_DBG("%s: buffer empty, disable tx irq", dev->name); uart_irq_tx_disable(dev); @@ -193,16 +187,11 @@ static void uart_bridge_handle_tx(const struct device *dev, sent_len = uart_fifo_fill(dev, send_buf, rb_len); if (sent_len < 0) { - ring_buf_get_finish(&peer_data->rb, 0); LOG_ERR("%s: tx error: %d", dev->name, sent_len); return; } - ret = ring_buf_get_finish(&peer_data->rb, sent_len); - if (ret < 0) { - LOG_ERR("ring_buf_get_finish error: %d", ret); - return; - } + ring_buf_consume(&peer_data->rb, (uint32_t)sent_len); if (peer_data->paused && ring_buf_space_get(&peer_data->rb) > RING_BUF_FULL_THRESHOLD) { diff --git a/drivers/serial/uart_bt.c b/drivers/serial/uart_bt.c index 1184e906b5d1..a56688ac646c 100644 --- a/drivers/serial/uart_bt.c +++ b/drivers/serial/uart_bt.c @@ -139,7 +139,7 @@ static void tx_work_handler(struct k_work *work) * peers, and the same chunk is sent to everyone. This avoids * managing separate read pointers: one per connection. */ - len = ring_buf_get_claim(dev_data->uart.tx_ringbuf, &data, chunk_size); + len = MIN(ring_buf_get_ptr(dev_data->uart.tx_ringbuf, &data, 0), chunk_size); if (len > 0) { err = bt_nus_inst_send(NULL, dev_data->bt.inst, data, len); if (err) { @@ -147,7 +147,7 @@ static void tx_work_handler(struct k_work *work) } } - ring_buf_get_finish(dev_data->uart.tx_ringbuf, len); + ring_buf_consume(dev_data->uart.tx_ringbuf, len); } while (len > 0 && !err); if ((ring_buf_space_get(dev_data->uart.tx_ringbuf) > 0) && dev_data->uart.tx_irq_ena) { From 96cd0a8d69e70e4ab2b2a36ae40deae3ae447de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 13:06:10 +0100 Subject: [PATCH 045/455] usb: cdc_acm: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- subsys/usb/device/class/cdc_acm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/subsys/usb/device/class/cdc_acm.c b/subsys/usb/device/class/cdc_acm.c index 39dff198c80e..7ea1945f38d0 100644 --- a/subsys/usb/device/class/cdc_acm.c +++ b/subsys/usb/device/class/cdc_acm.c @@ -269,8 +269,7 @@ static void tx_work_handler(struct k_work *work) return; } - len = ring_buf_get_claim(dev_data->tx_ringbuf, &data, - CONFIG_USB_CDC_ACM_RINGBUF_SIZE); + len = ring_buf_get_ptr(dev_data->tx_ringbuf, &data, 0); if (!len) { LOG_DBG("Nothing to send"); @@ -294,7 +293,7 @@ static void tx_work_handler(struct k_work *work) usb_transfer(ep, data, len, USB_TRANS_WRITE, cdc_acm_write_cb, dev_data); - ring_buf_get_finish(dev_data->tx_ringbuf, len); + ring_buf_consume(dev_data->tx_ringbuf, len); } static void cdc_acm_read_cb(uint8_t ep, int size, void *priv) From cfeb246284724332199f2108fd0c3878a46abb4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 13:18:21 +0100 Subject: [PATCH 046/455] tests: drivers: uart: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- tests/drivers/uart/uart_async_dual/src/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/drivers/uart/uart_async_dual/src/main.c b/tests/drivers/uart/uart_async_dual/src/main.c index 70ad7e5510d6..6e6991af4760 100644 --- a/tests/drivers/uart/uart_async_dual/src/main.c +++ b/tests/drivers/uart/uart_async_dual/src/main.c @@ -195,7 +195,7 @@ static void fill_tx(struct test_tx_data *data) return; } - while ((len = ring_buf_put_claim(&data->rbuf, &buf, 255)) > 0) { + while ((len = ring_buf_put_ptr(&data->rbuf, &buf, 0)) > 0) { uint8_t r = (sys_rand8_get() % MAX_PACKET_LEN) % len; uint8_t packet_len = MAX(r, MIN_PACKET_LEN); @@ -205,7 +205,7 @@ static void fill_tx(struct test_tx_data *data) buf[i] = packet_len - i; } - ring_buf_put_finish(&data->rbuf, packet_len); + ring_buf_commit(&data->rbuf, packet_len); } } @@ -236,7 +236,7 @@ static void try_tx(const struct device *dev, bool irq) return; } - len = ring_buf_get_claim(&tx_data.rbuf, &buf, 255); + len = MIN(ring_buf_get_ptr(&tx_data.rbuf, &buf, 0), 255U); if (len > 0) { err = uart_tx(dev, buf, len, TX_TIMEOUT); zassert_equal(err, 0, @@ -295,7 +295,7 @@ static void on_tx_done(const struct device *dev, struct uart_event *evt) } /* Finish previous data chunk and start new if any pending. */ - ring_buf_get_finish(&tx_data.rbuf, evt->data.tx.len); + ring_buf_consume(&tx_data.rbuf, evt->data.tx.len); atomic_set(&tx_data.busy, 0); try_tx(dev, true); } From 7d3096396e51949b53355f1c1d7bc90e8e374f81 Mon Sep 17 00:00:00 2001 From: YiMing Zhang Date: Sun, 16 Aug 2026 14:25:43 +0800 Subject: [PATCH 047/455] doc: dts: clarify that status "reserved" maps to disabled The status section claimed values other than "okay"/"disabled" result in undefined behavior, but edtlib treats "reserved" as disabled and the tree already uses it in 51 files to mark nodes that exist but are enabled elsewhere (multi-domain setups). Document that any value other than "okay" is treated as disabled, and that "reserved" is the conventional way to note a node used by another domain. Fixes: #116295 Signed-off-by: YiMing Zhang --- doc/build/dts/intro-syntax-structure.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/build/dts/intro-syntax-structure.rst b/doc/build/dts/intro-syntax-structure.rst index e45c1951633b..76a3064e9ee5 100644 --- a/doc/build/dts/intro-syntax-structure.rst +++ b/doc/build/dts/intro-syntax-structure.rst @@ -277,8 +277,12 @@ status The devicetree specification allows this property to have values ``"okay"``, ``"disabled"``, ``"reserved"``, ``"fail"``, and ``"fail-sss"``. - Only the values ``"okay"`` and ``"disabled"`` are currently relevant to - Zephyr; use of other values currently results in undefined behavior. + Zephyr treats any value other than ``"okay"`` as disabled. In particular, + ``"reserved"`` is used to document that a node exists but is enabled + elsewhere (e.g. by another core or domain in a multi-domain application); + it is handled the same as ``"disabled"`` by ``edtlib``. Use of the + remaining values (``"fail"`` and ``"fail-sss"``) is not currently + supported by Zephyr. A node is considered enabled if its status property is either ``"okay"`` or not defined (i.e. does not exist in the devicetree source). Nodes with From fc6373b2e289440f8ce6ee63eec2b2b913156c6f Mon Sep 17 00:00:00 2001 From: YiMing Zhang Date: Mon, 24 Aug 2026 17:24:57 +0800 Subject: [PATCH 048/455] doc: clarify unused dts status values Explain that values other than "okay" are treated as disabled. Also document "reserved" as the conventional value for nodes used by another domain. Signed-off-by: YiMing Zhang --- doc/build/dts/intro-syntax-structure.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build/dts/intro-syntax-structure.rst b/doc/build/dts/intro-syntax-structure.rst index 76a3064e9ee5..0ea0fcb8d93b 100644 --- a/doc/build/dts/intro-syntax-structure.rst +++ b/doc/build/dts/intro-syntax-structure.rst @@ -282,7 +282,7 @@ status elsewhere (e.g. by another core or domain in a multi-domain application); it is handled the same as ``"disabled"`` by ``edtlib``. Use of the remaining values (``"fail"`` and ``"fail-sss"``) is not currently - supported by Zephyr. + used by Zephyr. A node is considered enabled if its status property is either ``"okay"`` or not defined (i.e. does not exist in the devicetree source). Nodes with From 8a2f52be7a9f1c9958f7c56e4e3bf7964323fa3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 21 Aug 2026 10:27:56 +0200 Subject: [PATCH 049/455] cmake: kconfig: tidy up the Kconfig checksum accumulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accumulate the per-file MD5 checksums with string(APPEND) rather than set(var "${var}${checksum}"), and register the configure dependencies in a single set_property() call instead of one call per parsed Kconfig file, of which there are 5001 for a hello_world build. This is primarily a readability change; it may also be marginally faster depending on the CMake version and host. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- cmake/modules/kconfig.cmake | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/cmake/modules/kconfig.cmake b/cmake/modules/kconfig.cmake index fef0add1adc7..1c6d66ab1885 100644 --- a/cmake/modules/kconfig.cmake +++ b/cmake/modules/kconfig.cmake @@ -343,7 +343,7 @@ endif() set(merge_config_files_checksum "") foreach(f ${config_checksum_files}) file(MD5 ${f} checksum) - set(merge_config_files_checksum "${merge_config_files_checksum}${checksum}") + string(APPEND merge_config_files_checksum "${checksum}") endforeach() # Add to the checksum all the Kconfig files which were used last time @@ -353,7 +353,7 @@ if(EXISTS ${PARSED_KCONFIG_SOURCES_TXT}) foreach(f ${parsed_kconfig_sources_list}) if(EXISTS ${f}) file(MD5 ${f} checksum) - set(merge_kconfig_checksum "${merge_kconfig_checksum}${checksum}") + string(APPEND merge_kconfig_checksum "${checksum}") endif() endforeach() endif() @@ -429,17 +429,15 @@ file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} parsed_kconfig_sources_list ENCODING set(merge_kconfig_checksum "") foreach(f ${parsed_kconfig_sources_list}) file(MD5 ${f} checksum) - set(merge_kconfig_checksum "${merge_kconfig_checksum}${checksum}") + string(APPEND merge_kconfig_checksum "${checksum}") endforeach() # Force CMAKE configure when the Kconfig sources or configuration files changes. -foreach(kconfig_input - ${merge_config_files} - ${DOTCONFIG} - ${parsed_kconfig_sources_list} - ) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig_input}) -endforeach() +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + ${merge_config_files} + ${DOTCONFIG} + ${parsed_kconfig_sources_list} +) if(CREATE_NEW_DOTCONFIG) # Write the new configuration fragment checksum. Only do this if kconfig.py From dd6efdf7157d503ab017f612a3ca052637ab85ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 21 Aug 2026 10:28:12 +0200 Subject: [PATCH 050/455] cmake: kconfig: skip the Kconfig checksum recalculation when it is unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop that recalculates the checksum after kconfig.py runs is only consumed inside if(CREATE_NEW_DOTCONFIG). On any configure that does not regenerate .config its result is discarded, so hashing all 5001 parsed Kconfig sources is pure waste, measured in isolation at 168 ms. Move the loop into the block that consumes it. The file(STRINGS) read above stays where it is, as it also feeds the CMAKE_CONFIGURE_DEPENDS registration, which must keep running unconditionally. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- cmake/modules/kconfig.cmake | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmake/modules/kconfig.cmake b/cmake/modules/kconfig.cmake index 1c6d66ab1885..c42a65157b08 100644 --- a/cmake/modules/kconfig.cmake +++ b/cmake/modules/kconfig.cmake @@ -424,14 +424,6 @@ endif() # Read out the list of 'Kconfig' sources that were used by the engine. file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} parsed_kconfig_sources_list ENCODING UTF-8) -# Recalculate the Kconfig files' checksum, since the list of files may have -# changed. -set(merge_kconfig_checksum "") -foreach(f ${parsed_kconfig_sources_list}) - file(MD5 ${f} checksum) - string(APPEND merge_kconfig_checksum "${checksum}") -endforeach() - # Force CMAKE configure when the Kconfig sources or configuration files changes. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${merge_config_files} @@ -440,6 +432,14 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ) if(CREATE_NEW_DOTCONFIG) + # Recalculate the Kconfig files' checksum, since the list of files may have + # changed. + set(merge_kconfig_checksum "") + foreach(f ${parsed_kconfig_sources_list}) + file(MD5 ${f} checksum) + string(APPEND merge_kconfig_checksum "${checksum}") + endforeach() + # Write the new configuration fragment checksum. Only do this if kconfig.py # succeeds, to avoid marking zephyr/.config as up-to-date when it hasn't been # regenerated. From bdd5d679f1211ace46f0833ee979bd8a5025def2 Mon Sep 17 00:00:00 2001 From: Anders Frandsen Date: Wed, 19 Aug 2026 10:13:36 +0200 Subject: [PATCH 051/455] drivers: clock_control: fix CMakeLists indentation The if/elseif block that selects the per-series clock control source file sits inside if(CONFIG_CLOCK_CONTROL_STM32_CUBE) but was never indented, so the CMakeStyle compliance check fails. Re-indent the block to 2 spaces per level. No functional change. Signed-off-by: Anders Frandsen --- drivers/clock_control/CMakeLists.txt | 90 ++++++++++++++-------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index f98160d40048..93e7f9e845a1 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -10,51 +10,51 @@ endif() if(CONFIG_CLOCK_CONTROL_STM32_CUBE) zephyr_library_sources_ifdef(CONFIG_CLOCK_STM32_MUX clock_stm32_mux.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_STM32_MCO clock_stm32_mco.c) -if(CONFIG_SOC_SERIES_STM32MP1X) - zephyr_library_sources(clock_stm32_ll_mp1.c) -elseif(CONFIG_SOC_SERIES_STM32MP13X) - zephyr_library_sources(clock_stm32_ll_mp13.c) -elseif(CONFIG_SOC_SERIES_STM32MP2X) - zephyr_library_sources(clock_stm32_ll_mp2.c) -elseif(CONFIG_SOC_SERIES_STM32C5X) - zephyr_library_sources(clock_stm32_ll_c5.c) -elseif(CONFIG_SOC_SERIES_STM32H7X) - zephyr_library_sources(clock_stm32_ll_h7.c) -elseif(CONFIG_SOC_SERIES_STM32H7RSX) - zephyr_library_sources(clock_stm32_ll_h7.c) -elseif(CONFIG_SOC_SERIES_STM32H5X) - zephyr_library_sources(clock_stm32_ll_h5.c) -elseif(CONFIG_SOC_SERIES_STM32N6X) - zephyr_library_sources(clock_stm32_ll_n6.c) -elseif(CONFIG_SOC_SERIES_STM32U3X) - zephyr_library_sources(clock_stm32_ll_u3.c) -elseif(CONFIG_SOC_SERIES_STM32U5X) - zephyr_library_sources(clock_stm32_ll_u5.c) -elseif(CONFIG_SOC_SERIES_STM32WB0X) - zephyr_library_sources(clock_stm32_ll_wb0.c) -elseif(CONFIG_SOC_SERIES_STM32WBAX) - zephyr_library_sources(clock_stm32_ll_wba.c) -else() - # zephyr-keep-sorted-start - zephyr_library_sources(clock_stm32_ll_common.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32C0X clock_stm32c0.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F0X clock_stm32f0_f3.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F1X clock_stm32f1.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F2X clock_stm32f2_f4_f7.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F3X clock_stm32f0_f3.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F4X clock_stm32f2_f4_f7.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F7X clock_stm32f2_f4_f7.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32G0X clock_stm32g0_u0.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32G4X clock_stm32g4.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L0X clock_stm32l0_l1.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L1X clock_stm32l0_l1.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L4X clock_stm32l4_l5_wb_wl.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L5X clock_stm32l4_l5_wb_wl.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32U0X clock_stm32g0_u0.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32WBX clock_stm32l4_l5_wb_wl.c) - zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32WLX clock_stm32l4_l5_wb_wl.c) - # zephyr-keep-sorted-stop -endif() + if(CONFIG_SOC_SERIES_STM32MP1X) + zephyr_library_sources(clock_stm32_ll_mp1.c) + elseif(CONFIG_SOC_SERIES_STM32MP13X) + zephyr_library_sources(clock_stm32_ll_mp13.c) + elseif(CONFIG_SOC_SERIES_STM32MP2X) + zephyr_library_sources(clock_stm32_ll_mp2.c) + elseif(CONFIG_SOC_SERIES_STM32C5X) + zephyr_library_sources(clock_stm32_ll_c5.c) + elseif(CONFIG_SOC_SERIES_STM32H7X) + zephyr_library_sources(clock_stm32_ll_h7.c) + elseif(CONFIG_SOC_SERIES_STM32H7RSX) + zephyr_library_sources(clock_stm32_ll_h7.c) + elseif(CONFIG_SOC_SERIES_STM32H5X) + zephyr_library_sources(clock_stm32_ll_h5.c) + elseif(CONFIG_SOC_SERIES_STM32N6X) + zephyr_library_sources(clock_stm32_ll_n6.c) + elseif(CONFIG_SOC_SERIES_STM32U3X) + zephyr_library_sources(clock_stm32_ll_u3.c) + elseif(CONFIG_SOC_SERIES_STM32U5X) + zephyr_library_sources(clock_stm32_ll_u5.c) + elseif(CONFIG_SOC_SERIES_STM32WB0X) + zephyr_library_sources(clock_stm32_ll_wb0.c) + elseif(CONFIG_SOC_SERIES_STM32WBAX) + zephyr_library_sources(clock_stm32_ll_wba.c) + else() + # zephyr-keep-sorted-start + zephyr_library_sources(clock_stm32_ll_common.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32C0X clock_stm32c0.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F0X clock_stm32f0_f3.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F1X clock_stm32f1.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F2X clock_stm32f2_f4_f7.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F3X clock_stm32f0_f3.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F4X clock_stm32f2_f4_f7.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32F7X clock_stm32f2_f4_f7.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32G0X clock_stm32g0_u0.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32G4X clock_stm32g4.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L0X clock_stm32l0_l1.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L1X clock_stm32l0_l1.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L4X clock_stm32l4_l5_wb_wl.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32L5X clock_stm32l4_l5_wb_wl.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32U0X clock_stm32g0_u0.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32WBX clock_stm32l4_l5_wb_wl.c) + zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_STM32WLX clock_stm32l4_l5_wb_wl.c) + # zephyr-keep-sorted-stop + endif() endif() if(CONFIG_CLOCK_CONTROL_RCAR_CPG_MSSR) From 5420a7da186ea1071dca3bda525778d70aaf96cf Mon Sep 17 00:00:00 2001 From: Anders Frandsen Date: Mon, 3 Aug 2026 07:54:05 +0200 Subject: [PATCH 052/455] soc: st: stm32: add STM32WL3x series Add Zephyr SoC support for the STM32WL3x series (STM32WL30/WL31/WL33). Signed-off-by: Anders Frandsen --- dts/arm/st/wl3/stm32wl30X8.dtsi | 23 +++ dts/arm/st/wl3/stm32wl30Xb.dtsi | 23 +++ dts/arm/st/wl3/stm32wl30xx.dtsi | 14 ++ dts/arm/st/wl3/stm32wl31X8.dtsi | 23 +++ dts/arm/st/wl3/stm32wl31Xb.dtsi | 23 +++ dts/arm/st/wl3/stm32wl31xx.dtsi | 14 ++ dts/arm/st/wl3/stm32wl33X8.dtsi | 23 +++ dts/arm/st/wl3/stm32wl33Xb.dtsi | 23 +++ dts/arm/st/wl3/stm32wl33Xc.dtsi | 23 +++ dts/arm/st/wl3/stm32wl33xx.dtsi | 14 ++ dts/arm/st/wl3/stm32wl3x.dtsi | 186 ++++++++++++++++++ .../zephyr/dt-bindings/clock/stm32wl3_clock.h | 42 ++++ .../zephyr/dt-bindings/reset/stm32wl3_reset.h | 29 +++ modules/Kconfig.stm32 | 20 ++ soc/st/stm32/Kconfig.defconfig | 2 +- soc/st/stm32/soc.yml | 5 + soc/st/stm32/stm32wl3x/CMakeLists.txt | 7 + soc/st/stm32/stm32wl3x/Kconfig | 17 ++ soc/st/stm32/stm32wl3x/Kconfig.defconfig | 14 ++ soc/st/stm32/stm32wl3x/Kconfig.soc | 28 +++ soc/st/stm32/stm32wl3x/ram_sections.ld | 18 ++ soc/st/stm32/stm32wl3x/soc.c | 41 ++++ soc/st/stm32/stm32wl3x/soc.h | 27 +++ west.yml | 2 +- 24 files changed, 639 insertions(+), 2 deletions(-) create mode 100644 dts/arm/st/wl3/stm32wl30X8.dtsi create mode 100644 dts/arm/st/wl3/stm32wl30Xb.dtsi create mode 100644 dts/arm/st/wl3/stm32wl30xx.dtsi create mode 100644 dts/arm/st/wl3/stm32wl31X8.dtsi create mode 100644 dts/arm/st/wl3/stm32wl31Xb.dtsi create mode 100644 dts/arm/st/wl3/stm32wl31xx.dtsi create mode 100644 dts/arm/st/wl3/stm32wl33X8.dtsi create mode 100644 dts/arm/st/wl3/stm32wl33Xb.dtsi create mode 100644 dts/arm/st/wl3/stm32wl33Xc.dtsi create mode 100644 dts/arm/st/wl3/stm32wl33xx.dtsi create mode 100644 dts/arm/st/wl3/stm32wl3x.dtsi create mode 100644 include/zephyr/dt-bindings/clock/stm32wl3_clock.h create mode 100644 include/zephyr/dt-bindings/reset/stm32wl3_reset.h create mode 100644 soc/st/stm32/stm32wl3x/CMakeLists.txt create mode 100644 soc/st/stm32/stm32wl3x/Kconfig create mode 100644 soc/st/stm32/stm32wl3x/Kconfig.defconfig create mode 100644 soc/st/stm32/stm32wl3x/Kconfig.soc create mode 100644 soc/st/stm32/stm32wl3x/ram_sections.ld create mode 100644 soc/st/stm32/stm32wl3x/soc.c create mode 100644 soc/st/stm32/stm32wl3x/soc.h diff --git a/dts/arm/st/wl3/stm32wl30X8.dtsi b/dts/arm/st/wl3/stm32wl30X8.dtsi new file mode 100644 index 000000000000..1cd5d1725cf5 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl30X8.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(64)>; + ranges = <0 0x10040000 DT_SIZE_K(64)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(8)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl30Xb.dtsi b/dts/arm/st/wl3/stm32wl30Xb.dtsi new file mode 100644 index 000000000000..04650e03fe85 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl30Xb.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(128)>; + ranges = <0 0x10040000 DT_SIZE_K(128)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(16)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl30xx.dtsi b/dts/arm/st/wl3/stm32wl30xx.dtsi new file mode 100644 index 000000000000..19e29414624a --- /dev/null +++ b/dts/arm/st/wl3/stm32wl30xx.dtsi @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + compatible = "st,stm32wl30", "st,stm32wl3", "simple-bus"; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl31X8.dtsi b/dts/arm/st/wl3/stm32wl31X8.dtsi new file mode 100644 index 000000000000..b15a12119464 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl31X8.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(64)>; + ranges = <0 0x10040000 DT_SIZE_K(64)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(8)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl31Xb.dtsi b/dts/arm/st/wl3/stm32wl31Xb.dtsi new file mode 100644 index 000000000000..e516f1af5df5 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl31Xb.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(128)>; + ranges = <0 0x10040000 DT_SIZE_K(128)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(16)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl31xx.dtsi b/dts/arm/st/wl3/stm32wl31xx.dtsi new file mode 100644 index 000000000000..b356d4461f76 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl31xx.dtsi @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + compatible = "st,stm32wl31", "st,stm32wl3", "simple-bus"; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl33X8.dtsi b/dts/arm/st/wl3/stm32wl33X8.dtsi new file mode 100644 index 000000000000..8d21904c7a12 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl33X8.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(64)>; + ranges = <0 0x10040000 DT_SIZE_K(64)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(16)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl33Xb.dtsi b/dts/arm/st/wl3/stm32wl33Xb.dtsi new file mode 100644 index 000000000000..d4aee6fbda34 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl33Xb.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(128)>; + ranges = <0 0x10040000 DT_SIZE_K(128)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(32)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl33Xc.dtsi b/dts/arm/st/wl3/stm32wl33Xc.dtsi new file mode 100644 index 000000000000..9584f0777337 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl33Xc.dtsi @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(256)>; + ranges = <0 0x10040000 DT_SIZE_K(256)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(32)>; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl33xx.dtsi b/dts/arm/st/wl3/stm32wl33xx.dtsi new file mode 100644 index 000000000000..268364073af6 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl33xx.dtsi @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + compatible = "st,stm32wl33", "st,stm32wl3", "simple-bus"; + }; +}; diff --git a/dts/arm/st/wl3/stm32wl3x.dtsi b/dts/arm/st/wl3/stm32wl3x.dtsi new file mode 100644 index 000000000000..a7ba35af34e2 --- /dev/null +++ b/dts/arm/st/wl3/stm32wl3x.dtsi @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +/ { + chosen { + zephyr,flash-controller = &flash; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-m0+"; + reg = <0>; + }; + }; + + sram0: memory@20000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + zephyr,memory-region = "SRAM0"; + }; + + clocks { + /* + * Default to 48 MHz crystal as this should be the + * most common configuration, since it matches the + * HSI frequency when using RC64MPLL in PLL mode. + * + * However, 50 MHz is also supported. + */ + clk_hse: clk-hse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + clk_hsi: clk-hsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + /* Dummy node representing the RC64MPLL block in PLL mode + * Using this node as RCC node input requires HSE to be enabled. + */ + pll: pll64m { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&clk_hse>; + clock-mult = <4>; + clock-div = <3>; + status = "disabled"; + }; + + /* Slow speed clock nodes + * These nodes must only be used for the + * 'slow-clock' property of the RCC node. + */ + clk_lse: clk-lse { + #clock-cells = <0>; + compatible = "st,stm32-lse-clock"; + clock-frequency = <32768>; + driving-capability = <1>; + status = "disabled"; + }; + + clk_lsi: clk-lsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + /* Dummy node representing the "CLK_ROOT_DIV/512" slow clock source. + * WARNING: this clock is not active in DEEPSTOP, so all slow clock peripherals + * are stopped, and cannot wake up the SoC, if this is selected as slow-clock! + */ + clk_16mhz_div512: clk-16mhz-div512 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <(DT_FREQ_M(16) / 512)>; + status = "disabled"; + }; + }; + + soc { + flash: flash-controller@40001000 { + compatible = "st,stm32wl3-flash-controller", "st,stm32-flash-controller"; + reg = <0x40001000 DT_SIZE_K(4)>; + interrupts = <0 0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + st_nvm_user_otp: flash@10001800 { + compatible = "st,stm32-nvm-otp"; + reg = <0x10001800 DT_SIZE_K(1)>; + status = "disabled"; + }; + + flash0: flash@10040000 { + compatible = "st,stm32-nv-flash", "soc-nv-flash"; + #address-cells = <1>; + #size-cells = <1>; + write-block-size = <4>; + erase-block-size = <2048>; + max-erase-time = <40>; + }; + }; + + rcc: rcc@48400000 { + compatible = "st,stm32wl3-rcc"; + reg = <0x48400000 DT_SIZE_K(1)>; + #clock-cells = <2>; + + rctl: reset-controller { + compatible = "st,stm32-rcc-rctl"; + #reset-cells = <1>; + }; + }; + + gpio_intc: interrupt-controller@40000000 { + compatible = "st,stm32wb0-gpio-intc"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + reg = <0x40000000 0x40>; + num-lines = <32>; + interrupts = <15 0>, <16 0>; + interrupt-names = "gpioa", "gpiob"; + line-ranges = <0 16>, <16 16>; + }; + + pinctrl: pin-controller@48000000 { + compatible = "st,stm32-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x48000000 DT_SIZE_M(2)>; + + gpioa: gpio@48000000 { + compatible = "st,stm32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x48000000 DT_SIZE_K(1)>; + clocks = <&rcc STM32_CLOCK(AHB0, 2)>; + }; + + gpiob: gpio@48100000 { + compatible = "st,stm32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x48100000 DT_SIZE_K(1)>; + clocks = <&rcc STM32_CLOCK(AHB0, 3)>; + }; + }; + + usart1: serial@41004000 { + compatible = "st,stm32-usart", "st,stm32-uart"; + reg = <0x41004000 DT_SIZE_K(1)>; + clocks = <&rcc STM32_CLOCK(APB1, 10)>, + <&rcc STM32_SRC_CLK_ROOT_DIV NO_SEL>; + resets = <&rctl STM32_RESET(APB1, 10)>; + interrupts = <8 0>; + status = "disabled"; + }; + }; +}; + +&nvic { + arm,num-irq-priority-bits = <2>; +}; diff --git a/include/zephyr/dt-bindings/clock/stm32wl3_clock.h b/include/zephyr/dt-bindings/clock/stm32wl3_clock.h new file mode 100644 index 000000000000..56225a879a23 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/stm32wl3_clock.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief DT bindings for STM32WL3 clock system + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32WL3_CLOCK_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32WL3_CLOCK_H_ +/** @cond INTERNAL_HIDDEN */ + +/** Define system & low-speed clocks */ +#include "stm32_common_clocks.h" + +/** CLK_ROOT is the name given to the system clock. */ +#define STM32_SRC_CLK_ROOT STM32_SRC_SYSCLK + +/** Other fixed clocks. + * - CLKSLOWMUX: used to query slow clock tree frequency + * - CLK_ROOT_DIV: kernel clock for USART, I2C, RNG, LPAWUR, ADC and LPUART + * - CLKSYS: CLK_ROOT divided by CLKSYSDIV, clocks the CPU, AHB and APB + */ +#define STM32_SRC_CLKSLOWMUX (STM32_SRC_LSI + 1) +#define STM32_SRC_CLK_ROOT_DIV (STM32_SRC_CLKSLOWMUX + 1) +#define STM32_SRC_CLKSYS (STM32_SRC_CLK_ROOT_DIV + 1) + +/* Bus clocks: offset of the RCC clock enable register for each bus */ +#define STM32_CLOCK_BUS_AHB0 0x50 /**< RCC_AHBENR offset. */ +#define STM32_CLOCK_BUS_APB0 0x54 /**< RCC_APB0ENR offset. */ +#define STM32_CLOCK_BUS_APB1 0x58 /**< RCC_APB1ENR offset. */ +#define STM32_CLOCK_BUS_APB2 0x60 /**< RCC_APB2ENR offset. */ + +#define STM32_PERIPH_BUS_MIN STM32_CLOCK_BUS_AHB0 /**< Lowest bus register offset. */ +#define STM32_PERIPH_BUS_MAX STM32_CLOCK_BUS_APB2 /**< Highest bus register offset. */ + +/** @endcond */ +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32WL3_CLOCK_H_ */ diff --git a/include/zephyr/dt-bindings/reset/stm32wl3_reset.h b/include/zephyr/dt-bindings/reset/stm32wl3_reset.h new file mode 100644 index 000000000000..52d19a6effe3 --- /dev/null +++ b/include/zephyr/dt-bindings/reset/stm32wl3_reset.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief STM32 reset controller devicetree helper macros for STM32WL3 + * @ingroup reset_controller_stm32 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_RESET_STM32WL3_RESET_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_RESET_STM32WL3_RESET_H_ + +#include "stm32-common.h" + +/** @cond INTERNAL_HIDDEN */ + +/* RCC bus reset register offsets */ +#define STM32_RESET_BUS_AHB0 0x30 +#define STM32_RESET_BUS_APB0 0x34 +#define STM32_RESET_BUS_APB1 0x38 +#define STM32_RESET_BUS_APB2 0x40 + +/** @endcond */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_RESET_STM32WL3_RESET_H_ */ diff --git a/modules/Kconfig.stm32 b/modules/Kconfig.stm32 index 984fb179c176..e7e9da2417b0 100644 --- a/modules/Kconfig.stm32 +++ b/modules/Kconfig.stm32 @@ -390,6 +390,11 @@ config USE_STM32_HAL_LCD help Enable STM32Cube LCD controller (LCD) HAL module driver +config USE_STM32_HAL_LPAWUR + bool + help + Enable STM32Cube Low power wakeup radio (LPAWUR) HAL module driver + config USE_STM32_HAL_LPTIM bool help @@ -438,6 +443,16 @@ config USE_STM32_HAL_MMC_EX Enable STM32Cube Extended MultiMediaCard interface (SDMMC) HAL module driver +config USE_STM32_HAL_MRSUBG + bool + help + Enable STM32Cube Sub-GHz radio (MRSUBG) HAL module driver + +config USE_STM32_HAL_MRSUBG_TIMER + bool + help + Enable STM32Cube Sub-GHz radio timer (MRSUBG_TIMER) HAL module driver + config USE_STM32_HAL_MSP bool help @@ -831,6 +846,11 @@ config USE_STM32_LL_IPCC Enable STM32Cube Inter-Processor communication controller (IPCC) LL module driver +config USE_STM32_LL_LCSC + bool + help + Enable STM32Cube LC sensor controller (LCSC) LL module driver + config USE_STM32_LL_LPGPIO bool help diff --git a/soc/st/stm32/Kconfig.defconfig b/soc/st/stm32/Kconfig.defconfig index 74c1a81e8b10..ff622d22696e 100644 --- a/soc/st/stm32/Kconfig.defconfig +++ b/soc/st/stm32/Kconfig.defconfig @@ -162,7 +162,7 @@ config VIDEO_BUFFER_POOL_ALIGN default 16 if VIDEO_STM32_DCMIPP config STM32_BACKUP_PROTECTION - default y if !SOC_SERIES_STM32C0X && !SOC_SERIES_STM32WB0X + default y if !SOC_SERIES_STM32C0X && !SOC_SERIES_STM32WB0X && !SOC_SERIES_STM32WL3X # Default LVGL configuration, closely linked to how the display (LTDC) # interface is being configured diff --git a/soc/st/stm32/soc.yml b/soc/st/stm32/soc.yml index 0f5667c77508..496f1b226fab 100644 --- a/soc/st/stm32/soc.yml +++ b/soc/st/stm32/soc.yml @@ -282,3 +282,8 @@ family: - name: stm32wle5xx - name: stm32wl54xx - name: stm32wl55xx + - name: stm32wl3x + socs: + - name: stm32wl30xx + - name: stm32wl31xx + - name: stm32wl33xx diff --git a/soc/st/stm32/stm32wl3x/CMakeLists.txt b/soc/st/stm32/stm32wl3x/CMakeLists.txt new file mode 100644 index 000000000000..1a27eea157da --- /dev/null +++ b/soc/st/stm32/stm32wl3x/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2026 Anders Frandsen +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources(soc.c) +zephyr_include_directories(.) +zephyr_linker_sources(RAM_SECTIONS ram_sections.ld) +set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/st/stm32/stm32wl3x/Kconfig b/soc/st/stm32/stm32wl3x/Kconfig new file mode 100644 index 000000000000..ddfd03c22de3 --- /dev/null +++ b/soc/st/stm32/stm32wl3x/Kconfig @@ -0,0 +1,17 @@ +# STMicroelectronics STM32WL3 MCU line + +# Copyright (c) 2026 Anders Frandsen +# SPDX-License-Identifier: Apache-2.0 + +config SOC_SERIES_STM32WL3X + # Architecture options & CPU model selection + select ARM + select CPU_CORTEX_M0PLUS + # Hardware options + select CPU_CORTEX_M_HAS_SYSTICK + select CPU_CORTEX_M_HAS_VTOR + select CPU_HAS_ARM_MPU + # Software options + select SOC_EARLY_INIT_HOOK + # STM32-specific options + select HAS_STM32CUBE diff --git a/soc/st/stm32/stm32wl3x/Kconfig.defconfig b/soc/st/stm32/stm32wl3x/Kconfig.defconfig new file mode 100644 index 000000000000..2d4760408eaf --- /dev/null +++ b/soc/st/stm32/stm32wl3x/Kconfig.defconfig @@ -0,0 +1,14 @@ +# STMicroelectronics STM32WL3 MCU series + +# Copyright (c) 2026 Anders Frandsen +# SPDX-License-Identifier: Apache-2.0 + +if SOC_SERIES_STM32WL3X + +# STM32WL30x/31x/33x must be exposed as STM32WL3XX at HAL level. +# FIXME: not currently possible. "stm32wl33" is used temporarily +# instead as it is recognized as STM32WL3XX due to a legacy alias. +configdefault STM32CUBE_SOC_NAME_OVERRIDE + default "stm32wl33" if SOC_STM32WL30XX || SOC_STM32WL31XX || SOC_STM32WL33XX + +endif # SOC_SERIES_STM32WL3X diff --git a/soc/st/stm32/stm32wl3x/Kconfig.soc b/soc/st/stm32/stm32wl3x/Kconfig.soc new file mode 100644 index 000000000000..f101dc846b29 --- /dev/null +++ b/soc/st/stm32/stm32wl3x/Kconfig.soc @@ -0,0 +1,28 @@ +# STMicroelectronics STM32WL3 MCU line + +# Copyright (c) 2026 Anders Frandsen +# SPDX-License-Identifier: Apache-2.0 + +config SOC_SERIES_STM32WL3X + bool + select SOC_FAMILY_STM32 + +config SOC_SERIES + default "stm32wl3x" if SOC_SERIES_STM32WL3X + +config SOC_STM32WL30XX + bool + select SOC_SERIES_STM32WL3X + +config SOC_STM32WL31XX + bool + select SOC_SERIES_STM32WL3X + +config SOC_STM32WL33XX + bool + select SOC_SERIES_STM32WL3X + +config SOC + default "stm32wl30xx" if SOC_STM32WL30XX + default "stm32wl31xx" if SOC_STM32WL31XX + default "stm32wl33xx" if SOC_STM32WL33XX diff --git a/soc/st/stm32/stm32wl3x/ram_sections.ld b/soc/st/stm32/stm32wl3x/ram_sections.ld new file mode 100644 index 000000000000..cc3891514aab --- /dev/null +++ b/soc/st/stm32/stm32wl3x/ram_sections.ld @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Refer to `soc.c` for more information about these areas. */ +SECTION_PROLOGUE(stm32wl3_RAM_VR, 0x20000000 (NOLOAD), ) +{ + /* For historical reasons, leave the first word of + * SRAM0 unused, even though it could store data. + * The structure MUST start at address 0x2000_0004. + */ + . += 4; + + KEEP(*(stm32wl3_RAM_VR)); +} GROUP_LINK_IN(RAMABLE_REGION) diff --git a/soc/st/stm32/stm32wl3x/soc.c b/soc/st/stm32/stm32wl3x/soc.c new file mode 100644 index 000000000000..65599de55ca7 --- /dev/null +++ b/soc/st/stm32/stm32wl3x/soc.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief System/hardware module for STM32WL3 processor + */ + +#include +#include +#include + +/** + * CMSIS System Core Clock: global variable holding the system core clock, + * which is the frequency supplied to the SysTick timer and processor core. + * + * On STM32WL3 series, after RESET, the system clock frequency is 16 MHz. + */ +uint32_t SystemCoreClock = 16000000U; + +/** + * RAM Virtual Register: special structure located at the start + * of SRAM0; used by the ROM bootloader. + * Data type definition comes from @ref system_stm32wl3x.h + */ +Z_GENERIC_SECTION(stm32wl3_RAM_VR) +__used RAM_VR_TypeDef RAM_VR; + +void soc_early_init_hook(void) +{ + /** + * Save application exception vector address in RAM_VR. + * By now, SCB->VTOR should point to _vector_table, + * so use that value instead of _vector_table directly. + */ + RAM_VR.AppBase = SCB->VTOR; +} diff --git a/soc/st/stm32/stm32wl3x/soc.h b/soc/st/stm32/stm32wl3x/soc.h new file mode 100644 index 000000000000..0819c9f53ce6 --- /dev/null +++ b/soc/st/stm32/stm32wl3x/soc.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file SoC configuration macros for the STM32WL3 family processors. + * + * Based on reference manual: + * RM0511 STM32WL33xx Arm®-based wireless MCUs with sub-GHz radio solution + * + * Chapter 2.2: Memory organization + */ + + +#ifndef _STM32WL3_SOC_H_ +#define _STM32WL3_SOC_H_ + +#ifndef _ASMLANGUAGE + +#include + +#endif /* !_ASMLANGUAGE */ + +#endif /* _STM32WL3_SOC_H_ */ diff --git a/west.yml b/west.yml index ce9a9e7b4919..a5dece1843e2 100644 --- a/west.yml +++ b/west.yml @@ -259,7 +259,7 @@ manifest: groups: - hal - name: hal_stm32 - revision: 33576ef05e529cad803f210cc95b52b607757c96 + revision: 02baa06ccea3304d1f65ec6550c68e5e300fcc58 path: modules/hal/stm32 groups: - hal From eb6d989f64a3bca42e27f161253da7d994d4e626 Mon Sep 17 00:00:00 2001 From: Anders Frandsen Date: Mon, 3 Aug 2026 07:56:25 +0200 Subject: [PATCH 053/455] drivers: clock_control: add STM32WL3x RCC driver Add RCC driver for the STM32WL3x series with CLK_SYS sources (HSI, RC64MPLL PLL and HSE), peripheral clock gates and the reset controller, together with the st,stm32wl3-rcc binding. Signed-off-by: Anders Frandsen --- drivers/clock_control/CMakeLists.txt | 2 + drivers/clock_control/clock_stm32_ll_wl3.c | 414 ++++++++++++++++++ dts/bindings/clock/st,stm32wl3-rcc.yaml | 61 +++ .../clock_control/stm32_clock_control.h | 2 + 4 files changed, 479 insertions(+) create mode 100644 drivers/clock_control/clock_stm32_ll_wl3.c create mode 100644 dts/bindings/clock/st,stm32wl3-rcc.yaml diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 93e7f9e845a1..1cd5d8bf5f41 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -34,6 +34,8 @@ if(CONFIG_CLOCK_CONTROL_STM32_CUBE) zephyr_library_sources(clock_stm32_ll_wb0.c) elseif(CONFIG_SOC_SERIES_STM32WBAX) zephyr_library_sources(clock_stm32_ll_wba.c) + elseif(CONFIG_SOC_SERIES_STM32WL3X) + zephyr_library_sources(clock_stm32_ll_wl3.c) else() # zephyr-keep-sorted-start zephyr_library_sources(clock_stm32_ll_common.c) diff --git a/drivers/clock_control/clock_stm32_ll_wl3.c b/drivers/clock_control/clock_stm32_ll_wl3.c new file mode 100644 index 000000000000..ec88f47afd1d --- /dev/null +++ b/drivers/clock_control/clock_stm32_ll_wl3.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Driver definitions */ +#define RCC_REG(offset) (DT_REG_ADDR(STM32_CLOCK_CONTROL_NODE) + (offset)) + +/* RF HSE capacitor bank tuning. */ +#define STM32_WL3_HSE_CAPACITOR_TUNE 32 + +/* Device tree node definitions */ +#define DT_RCC_SLOWCLK_NODE DT_PHANDLE(STM32_CLOCK_CONTROL_NODE, slow_clock) + +/* Device tree properties definitions */ +#define STM32_WL3_CLKSYS_PRESCALER \ + DT_PROP(STM32_CLOCK_CONTROL_NODE, clksys_prescaler) + +#if DT_NODE_HAS_PROP(STM32_CLOCK_CONTROL_NODE, slow_clock) + +# if !DT_NODE_HAS_STATUS_OKAY(DT_RCC_SLOWCLK_NODE) +# error slow-clock source is not enabled +# endif + +# if DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_lsi)) +# define STM32_WL3_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_LSI +# elif DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_lse)) +# define STM32_WL3_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_LSE +# elif DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_16mhz_div512)) +# define STM32_WL3_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_HSI64M_DIV2048 +# else +# error Invalid device selected as slow-clock +# endif + +#endif /* DT_NODE_HAS_PROP(STM32_CLOCK_CONTROL_NODE, slow_clock) */ + +#if defined(STM32_SYSCLK_SRC_PLL) +#define RC64MPLL_FREQ (STM32_HSE_FREQ * DT_PROP(DT_NODELABEL(pll), clock_mult) \ + / DT_PROP(DT_NODELABEL(pll), clock_div)) +#else +#define RC64MPLL_FREQ STM32_HSI_FREQ +#endif + +/* Verify device tree properties are correct */ +#if defined(STM32_SYSCLK_SRC_HSE) +#define STM32_WL3_CLKROOT_FREQ STM32_HSE_FREQ +#define LL_PRESCALER(x) _CONCAT(LL_RCC_DIRECT_HSE_DIV_, x) +#elif defined(STM32_SYSCLK_SRC_HSI) || defined(STM32_SYSCLK_SRC_PLL) +#define STM32_WL3_CLKROOT_FREQ RC64MPLL_FREQ +#define LL_PRESCALER(x) _CONCAT(LL_RCC_RC64MPLL_DIV_, x) +BUILD_ASSERT(IS_POWER_OF_TWO(STM32_WL3_CLKSYS_PRESCALER), + "clksys-prescaler must be a power of two when SYSCLK source is the RC64MPLL block"); +#else +#error Invalid device selected as SYSCLK source: use clk_hsi, pll or clk_hse +#endif + +BUILD_ASSERT(STM32_HCLK_FREQUENCY * STM32_WL3_CLKSYS_PRESCALER == STM32_WL3_CLKROOT_FREQ, + "clock-frequency must be the SYSCLK source frequency divided by clksys-prescaler"); + +#if defined(STM32_SYSCLK_SRC_PLL) || defined(STM32_SYSCLK_SRC_HSE) +BUILD_ASSERT(IS_ENABLED(STM32_HSE_ENABLED), + "STM32WL3 PLL and Direct HSE modes require HSE to be enabled"); +#endif + +#if defined(STM32_SYSCLK_SRC_PLL) +BUILD_ASSERT(DT_SAME_NODE(DT_CLOCKS_CTLR(DT_NODELABEL(pll)), DT_NODELABEL(clk_hse)), + "the RC64MPLL block can only be driven by HSE in PLL mode"); +#endif + +/** @brief Verifies if provided domain clock is currently active */ +static int enabled_clock(uint32_t src_clk) +{ + switch (src_clk) { + case STM32_SRC_CLK_ROOT: + case STM32_SRC_CLKSLOWMUX: + case STM32_SRC_CLK_ROOT_DIV: + case STM32_SRC_CLKSYS: + return 0; + case STM32_SRC_LSE: + if (!IS_ENABLED(STM32_LSE_ENABLED)) { + return -ENOTSUP; + } + return 0; + case STM32_SRC_LSI: + if (!IS_ENABLED(STM32_LSI_ENABLED)) { + return -ENOTSUP; + } + return 0; + default: + return -ENOTSUP; + } +} + +static int stm32_clock_control_on(const struct device *dev, clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + const mem_addr_t reg = RCC_REG(pclken->bus); + volatile uint32_t temp; + + ARG_UNUSED(dev); + if (!IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) { + /* Attempting to change domain clock */ + return -ENOTSUP; + } + + sys_set_bits(reg, pclken->enr); + + /* Read back register to be blocked by RCC + * until peripheral clock enabling is complete + */ + temp = sys_read32(reg); + UNUSED(temp); + + return 0; +} + +static int stm32_clock_control_off(const struct device *dev, clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + const mem_addr_t reg = RCC_REG(pclken->bus); + + ARG_UNUSED(dev); + if (!IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) { + /* Attempting to change domain clock */ + return -ENOTSUP; + } + + sys_clear_bits(reg, pclken->enr); + + return 0; +} + +static int stm32_clock_control_configure(const struct device *dev, + clock_control_subsys_t sub_system, + void *data) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + uint32_t enr = pclken->enr; + uint32_t reg = STM32_DT_CLKSEL_REG_GET(enr); + uint32_t shift = STM32_DT_CLKSEL_SHIFT_GET(enr); + int err; + + ARG_UNUSED(dev); + ARG_UNUSED(data); + + err = enabled_clock(pclken->bus); + if (err < 0) { + /* Attempting to configure an unavailable or invalid clock */ + return err; + } + + if (pclken->enr == NO_SEL) { + /* Domain clock is fixed. Nothing to set. Exit */ + return 0; + } + + stm32_reg_modify_bits((uint32_t *)(DT_REG_ADDR(DT_NODELABEL(rcc)) + reg), + STM32_DT_CLKSEL_MASK_GET(enr) << shift, + STM32_DT_CLKSEL_VAL_GET(enr) << shift); + + return 0; +} + +/** @brief Returns the CLK_ROOT frequency. */ +static uint32_t get_clk_root_freq(void) +{ + return LL_RCC_DIRECT_HSE_IsEnabled() ? STM32_HSE_FREQ : RC64MPLL_FREQ; +} + +/** @brief Returns the CLK_SYS frequency (CPU/AHB/APB) by reading RCC_CFGR->CLKSYSDIV */ +static uint32_t get_clk_sys_freq(void) +{ + /* Both LL prescaler getters return the raw CLKSYSDIV field, still shifted */ + uint32_t clksysdiv = LL_RCC_GetRC64MPLLPrescaler() >> RCC_CFGR_CLKSYSDIV_Pos; + + if (IS_ENABLED(STM32_SYSCLK_SRC_HSE)) { + /* DIRECT_HSE dividers are not powers of two */ + static const uint8_t hse_div[] = {1, 2, 3, 6, 12, 24, 48}; + + if (clksysdiv >= ARRAY_SIZE(hse_div)) { + return 0; + } + + return STM32_HSE_FREQ / hse_div[clksysdiv]; + } + + return RC64MPLL_FREQ >> clksysdiv; +} + +/** @brief Returns the slow-clock frequency (RTC/WDG/LCSC/LCDC) by reading RCC_CFGR->CLKSLOWSEL */ +static uint32_t get_slow_clk_freq(void) +{ + switch (LL_RCC_LSCO_GetSource()) { + case LL_RCC_LSCO_CLKSOURCE_LSE: + return STM32_LSE_FREQ; + case LL_RCC_LSCO_CLKSOURCE_LSI: + return STM32_LSI_FREQ; + case LL_RCC_LSCO_CLKSOURCE_HSI64M_DIV2048: + return RC64MPLL_FREQ / 2048U; + default: + return 0; + } +} + +static int stm32_clock_control_get_subsys_rate(const struct device *dev, + clock_control_subsys_t sub_system, + uint32_t *rate) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + + ARG_UNUSED(dev); + + switch (pclken->bus) { + case STM32_SRC_CLK_ROOT: + *rate = get_clk_root_freq(); + break; + case STM32_SRC_LSE: + *rate = STM32_LSE_FREQ; + break; + case STM32_SRC_LSI: + *rate = STM32_LSI_FREQ; + break; + case STM32_SRC_CLKSLOWMUX: + *rate = get_slow_clk_freq(); + break; + case STM32_SRC_CLK_ROOT_DIV: + *rate = MHZ(16); + break; + case STM32_SRC_CLKSYS: + *rate = get_clk_sys_freq(); + break; + case STM32_CLOCK_BUS_AHB0: + case STM32_CLOCK_BUS_APB0: + case STM32_CLOCK_BUS_APB1: + *rate = get_clk_sys_freq(); + break; + /* APB2 (radio subsystem) is not supported yet. */ + default: + return -ENOTSUP; + } + + if (pclken->div) { + *rate /= (pclken->div + 1); + } + + return 0; +} + +static enum clock_control_status stm32_clock_control_get_status(const struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + + ARG_UNUSED(dev); + + if (IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) { + /* Bus / gated clock */ + if ((sys_read32(RCC_REG(pclken->bus)) & pclken->enr) == pclken->enr) { + return CLOCK_CONTROL_STATUS_ON; + } else { + return CLOCK_CONTROL_STATUS_OFF; + } + } else { + /* Domain clock */ + if (enabled_clock(pclken->bus) == 0) { + return CLOCK_CONTROL_STATUS_ON; + } else { + return CLOCK_CONTROL_STATUS_OFF; + } + } +} + +static DEVICE_API(clock_control, stm32_clock_control_api) = { + .on = stm32_clock_control_on, + .off = stm32_clock_control_off, + .get_rate = stm32_clock_control_get_subsys_rate, + .get_status = stm32_clock_control_get_status, + .configure = stm32_clock_control_configure, +}; + +static void set_up_fixed_clock_sources(void) +{ + if (IS_ENABLED(STM32_HSE_ENABLED)) { + /* Crystal oscillator settings. These match the HSE setup + * set by HAL_RCC_OscConfig() in stm32wl3x_hal_rcc.c. + */ + LL_RCC_HSE_SetCapacitorTuning(STM32_WL3_HSE_CAPACITOR_TUNE); + LL_RCC_HSE_SetStartupCurrent(0); + LL_RCC_HSE_SetAmplitudeThreshold(0); + LL_RCC_HSE_SetCurrentControl(40); + + LL_RCC_HSE_Enable(); + while (!LL_RCC_HSE_IsReady()) { + /* Wait for HSE ready */ + } + } + + if (IS_ENABLED(STM32_LSI_ENABLED)) { + LL_RCC_LSI_Enable(); + while (!LL_RCC_LSI_IsReady()) { + /* Wait for LSI ready */ + } + } + + if (IS_ENABLED(STM32_LSE_ENABLED)) { +#if STM32_LSE_DRIVING + /* Configure driving capability */ + LL_RCC_LSE_SetDriveCapability(STM32_LSE_DRIVING << RCC_CSSWCR_LSEDRV_Pos); +#endif + /* Unconditionally disable pull-up & pull-down on LSE pins */ + LL_PWR_SetNoPullB(LL_PWR_GPIO_BIT_12 | LL_PWR_GPIO_BIT_13); + + if (IS_ENABLED(STM32_LSE_BYPASS)) { + /* Configure LSE bypass */ + LL_RCC_LSE_EnableBypass(); + } + + /* Enable LSE Oscillator (32.768 kHz) */ + LL_RCC_LSE_Enable(); + while (!LL_RCC_LSE_IsReady()) { + /* Wait for LSE ready */ + } + } +} + +static int stm32_clock_control_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Set flash latency according to target CLK_SYS frequency: + * - 1 wait state when CLK_SYS > 32MHz (i.e., 64MHz configuration) + * - 0 wait states otherwise (CLK_SYS <= 32MHz) + */ + if (STM32_HCLK_FREQUENCY > MHZ(32)) { + LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); + } else { + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + } + + /* Enable SYSCFG clock. */ + LL_APB0_GRP1_EnableClock(LL_APB0_GRP1_PERIPH_SYSCFG); + + /* Set up individual enabled clocks */ + set_up_fixed_clock_sources(); + + /* Set up the slow clock mux */ +#if defined(STM32_WL3_SLOWCLK_SRC) + LL_RCC_LSCO_SetSource(STM32_WL3_SLOWCLK_SRC); +#endif + +#if defined(STM32_SYSCLK_SRC_HSE) + /* Set HSE prescaler */ + LL_RCC_SetDirectHSEPrescaler(LL_PRESCALER(STM32_WL3_CLKSYS_PRESCALER)); + + /* Select Direct HSE as SYSCLK source */ + LL_RCC_DIRECT_HSE_Enable(); + + while (!LL_RCC_DIRECT_HSE_IsEnabled()) { + /* Wait until Direct HSE is ready */ + } +#else + if (IS_ENABLED(STM32_SYSCLK_SRC_PLL)) { + /* Turn on the PLL part of RC64MPLL block */ + LL_RCC_RC64MPLL_Enable(); + while (!LL_RCC_RC64MPLL_IsReady()) { + /* Wait until PLL is locked */ + } + } else { + /* Leave the RC64MPLL block free-running on its internal RC */ + LL_RCC_RC64MPLL_Disable(); + } + + LL_RCC_SetRC64MPLLPrescaler(LL_PRESCALER(STM32_WL3_CLKSYS_PRESCALER)); + + /* Select the RC64MPLL block as SYSCLK source */ + LL_RCC_DIRECT_HSE_Disable(); + + while (LL_RCC_DIRECT_HSE_IsEnabled()) { + /* Wait until the switch away from Direct HSE has completed */ + } +#endif /* STM32_SYSCLK_SRC_HSE */ + + if (STM32_HCLK_FREQUENCY <= MHZ(32)) { + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + } + + SystemCoreClock = STM32_HCLK_FREQUENCY; + + return 0; +} + +DEVICE_DT_DEFINE(STM32_CLOCK_CONTROL_NODE, + &stm32_clock_control_init, + NULL, NULL, NULL, + PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &stm32_clock_control_api); diff --git a/dts/bindings/clock/st,stm32wl3-rcc.yaml b/dts/bindings/clock/st,stm32wl3-rcc.yaml new file mode 100644 index 000000000000..0bedb383ce7f --- /dev/null +++ b/dts/bindings/clock/st,stm32wl3-rcc.yaml @@ -0,0 +1,61 @@ +# Copyright (c) 2024 STMicroelectronics +# Copyright (c) 2026 Anders Frandsen +# SPDX-License-Identifier: Apache-2.0 + +description: | + STM32WL3x RCC (Reset and Clock Controller). + + This node is in charge of the system clock ('SYSCLK') source + selection and generation. + + The SYSCLK source is selected with the 'clocks' property pointing to one of: + + - clk_hsi: RC64MPLL in RC mode (64 MHz internal RC). + - pll: RC64MPLL in PLL mode (Equal to HSE x 4/3). + - clk_hse: Direct HSE. + + Sub-GHz radio operation requires either 'pll' or 'clk_hse' as SYSCLK source. + +compatible: "st,stm32wl3-rcc" + +include: [clock-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#clock-cells": + const: 2 + + clock-frequency: + required: true + type: int + description: | + Resulting CLK_SYS frequency in Hz. + Must equal the CLK_ROOT source frequency divided by 'clksys-prescaler'. + The driver verifies this at build time. + + clksys-prescaler: + type: int + required: true + enum: [1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64] + description: | + CLK_SYS prescaler. Defines the actual core clock frequency (CLK_SYS) + based on the system clock (CLK_ROOT). + + Available division factors depend on the SYSCLK source: + - clk_hsi / pll (RC64MPLL): 1, 2, 4, 8, 16, 32, 64 + - clk_hse (Direct HSE): 1, 2, 3, 6, 12, 24, 48 + + slow-clock: + type: phandle + description: | + Slow clock source selection. + On STM32WL3, all slow clock devices are clocked from the same + slow clock source which is selected by this property. + + The slow clock can be either clk_lsi, clk_lse or CLK_ROOT_DIV / 512. + +clock-cells: + - bus + - bits diff --git a/include/zephyr/drivers/clock_control/stm32_clock_control.h b/include/zephyr/drivers/clock_control/stm32_clock_control.h index 29ac2306a4c4..ab1204f127a1 100644 --- a/include/zephyr/drivers/clock_control/stm32_clock_control.h +++ b/include/zephyr/drivers/clock_control/stm32_clock_control.h @@ -91,6 +91,8 @@ #include #elif defined(CONFIG_SOC_SERIES_STM32WBAX) #include +#elif defined(CONFIG_SOC_SERIES_STM32WL3X) +#include #else #include #endif From 942e6fec0174d5bc7a0db98d7fe2300c249226a0 Mon Sep 17 00:00:00 2001 From: Anders Frandsen Date: Mon, 3 Aug 2026 07:59:45 +0200 Subject: [PATCH 054/455] boards: st: add NUCLEO-WL33CC1 board Add support for the NUCLEO-WL33CC1 board, with console over the ST-LINK VCP (USART1 on PA1/PA15), three LEDs and three user buttons. hello_world, blinky and button have been verified on hardware. The MR_SubG radio, low-power modes and the ADC/SPI/I2C/timer peripherals are not yet supported. Signed-off-by: Anders Frandsen --- .../st/nucleo_wl33cc1/Kconfig.nucleo_wl33cc1 | 5 + boards/st/nucleo_wl33cc1/board.cmake | 7 ++ boards/st/nucleo_wl33cc1/board.yml | 6 ++ .../doc/img/nucleo_wl33cc1.webp | Bin 0 -> 46952 bytes boards/st/nucleo_wl33cc1/doc/index.rst | 75 +++++++++++++ boards/st/nucleo_wl33cc1/nucleo_wl33cc1.dts | 102 ++++++++++++++++++ boards/st/nucleo_wl33cc1/nucleo_wl33cc1.yaml | 13 +++ .../nucleo_wl33cc1/nucleo_wl33cc1_defconfig | 8 ++ 8 files changed, 216 insertions(+) create mode 100644 boards/st/nucleo_wl33cc1/Kconfig.nucleo_wl33cc1 create mode 100644 boards/st/nucleo_wl33cc1/board.cmake create mode 100644 boards/st/nucleo_wl33cc1/board.yml create mode 100644 boards/st/nucleo_wl33cc1/doc/img/nucleo_wl33cc1.webp create mode 100644 boards/st/nucleo_wl33cc1/doc/index.rst create mode 100644 boards/st/nucleo_wl33cc1/nucleo_wl33cc1.dts create mode 100644 boards/st/nucleo_wl33cc1/nucleo_wl33cc1.yaml create mode 100644 boards/st/nucleo_wl33cc1/nucleo_wl33cc1_defconfig diff --git a/boards/st/nucleo_wl33cc1/Kconfig.nucleo_wl33cc1 b/boards/st/nucleo_wl33cc1/Kconfig.nucleo_wl33cc1 new file mode 100644 index 000000000000..560511999313 --- /dev/null +++ b/boards/st/nucleo_wl33cc1/Kconfig.nucleo_wl33cc1 @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Anders Frandsen +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_NUCLEO_WL33CC1 + select SOC_STM32WL33XX diff --git a/boards/st/nucleo_wl33cc1/board.cmake b/boards/st/nucleo_wl33cc1/board.cmake new file mode 100644 index 000000000000..66e4e04c7102 --- /dev/null +++ b/boards/st/nucleo_wl33cc1/board.cmake @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +# keep first +board_runner_args(stm32cubeprogrammer "--port=swd" "--reset-mode=sw" "--start-address=0x10000000") + +# keep first +include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake) diff --git a/boards/st/nucleo_wl33cc1/board.yml b/boards/st/nucleo_wl33cc1/board.yml new file mode 100644 index 000000000000..f130a61f8486 --- /dev/null +++ b/boards/st/nucleo_wl33cc1/board.yml @@ -0,0 +1,6 @@ +board: + name: nucleo_wl33cc1 + full_name: Nucleo WL33CC1 + vendor: st + socs: + - name: stm32wl33xx diff --git a/boards/st/nucleo_wl33cc1/doc/img/nucleo_wl33cc1.webp b/boards/st/nucleo_wl33cc1/doc/img/nucleo_wl33cc1.webp new file mode 100644 index 0000000000000000000000000000000000000000..59431a394e010fe780d9716972f538b68301a809 GIT binary patch literal 46952 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 P{E=b0`{=WZ>!}O+i|Oh zGb=U&{~=j))UiCBowE=C SbKNDrxtP(4+?D_U0001ja&?OU literal 0 HcmV?d00001 diff --git a/boards/st/nucleo_wl33cc1/doc/index.rst b/boards/st/nucleo_wl33cc1/doc/index.rst new file mode 100644 index 000000000000..51b3f432f2ca --- /dev/null +++ b/boards/st/nucleo_wl33cc1/doc/index.rst @@ -0,0 +1,75 @@ +.. zephyr:board:: nucleo_wl33cc1 + +Overview +******** + +The NUCLEO-WL33CC1 board is based on the MB1801 mezzanine board and features an +MB2029 MCU RF board embedding an ultra-low-power STM32WL33CCV6 +Arm® Cortex®-M0+ wireless microcontroller with an integrated sub-GHz +radio operating in the 826-958 MHz frequency band. + +Key features: + +- STM32WL33CCV6 (256 KB flash, 32 KB RAM, Cortex-M0+) +- Sub-GHz radio supporting OOK, ASK, 2(G)FSK, 4(G)FSK, D-BPSK, and DSSS modulations +- Three user LEDs (LD1 blue, LD2 green, LD3 red) +- Three user push-buttons +- On-board STLINK-V3EC debugger/programmer with USB VCP +- ARDUINO® Uno V3 and ST morpho expansion connectors (MB1801) + +More information about the board can be found on the +`NUCLEO-WL33CC1 product page`_, in the board user manual (`UM3418`_), and in +the SoC reference manual (`RM0511`_). + +Supported Features +****************** + +The following hardware features are currently supported: + +- GPIO +- USART (console via the ST-LINK Virtual COM Port) + +Not yet supported: the sub-GHz radio (MR_SubG), ADC, SPI, I2C, timers, RTC, +watchdog and low-power modes. + +.. zephyr:board-supported-hw:: + +Connections and IOs +******************* + +Default board configuration: + +- USART1 TX/RX : PA1/PA15 (ST-LINK Virtual COM Port) +- LD1 (blue) : PA14 +- LD2 (green) : PB4 +- LD3 (red) : PB5 +- User button B1 : PA0 +- User button B2 : PA11 +- User button B3 : PB15 + +Programming and Debugging +************************* + +Applications are flashed and debugged through the on-board STLINK-V3EC using +the ``stm32cubeprogrammer`` runner (default): + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: nucleo_wl33cc1 + :goals: build flash + +Connect a serial terminal to the ST-LINK VCP at 115200 baud to see console output. + +References +********** + +.. target-notes:: + +.. _NUCLEO-WL33CC1 product page: + https://www.st.com/en/evaluation-tools/nucleo-wl33cc1.html + +.. _RM0511: + https://www.st.com/resource/en/reference_manual/rm0511-stm32wl30xx31xx33xx-armbased-wireless-mcus-with-subghz-radio-solution-stmicroelectronics.pdf + +.. _UM3418: + https://www.st.com/resource/en/user_manual/um3418-stm32wl33-nucleo64-boards-mb1801-and-mb2029-stmicroelectronics.pdf diff --git a/boards/st/nucleo_wl33cc1/nucleo_wl33cc1.dts b/boards/st/nucleo_wl33cc1/nucleo_wl33cc1.dts new file mode 100644 index 000000000000..e2559c3276f5 --- /dev/null +++ b/boards/st/nucleo_wl33cc1/nucleo_wl33cc1.dts @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2026 Anders Frandsen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include +#include + +/ { + model = "STMicroelectronics NUCLEO-WL33CC1 board"; + compatible = "st,nucleo-wl33cc1"; + + chosen { + zephyr,console = &usart1; + zephyr,shell-uart = &usart1; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + }; + + leds: leds { + compatible = "gpio-leds"; + + blue_led_1: led_0 { + gpios = <&gpioa 14 GPIO_ACTIVE_LOW>; /* LD1 (blue), PA14 */ + label = "LD1"; + }; + + green_led_1: led_1 { + gpios = <&gpiob 4 GPIO_ACTIVE_LOW>; /* LD2 (green), PB4 */ + label = "LD2"; + }; + + red_led_1: led_2 { + gpios = <&gpiob 5 GPIO_ACTIVE_LOW>; /* LD3 (red), PB5 */ + label = "LD3"; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + + user_button_1: button_0 { + label = "USER1"; + gpios = <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* B1, PA0 */ + zephyr,code = ; + }; + + user_button_2: button_1 { + label = "USER2"; + gpios = <&gpioa 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* B2, PA11 */ + zephyr,code = ; + }; + + user_button_3: button_2 { + label = "USER3"; + gpios = <&gpiob 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* B3, PB15 */ + zephyr,code = ; + }; + }; + + aliases { + led0 = &blue_led_1; + led1 = &green_led_1; + led2 = &red_led_1; + sw0 = &user_button_1; + sw1 = &user_button_2; + sw2 = &user_button_3; + }; +}; + +&clk_lse { + status = "okay"; +}; + +&clk_hse { + status = "okay"; +}; + +&clk_hsi { + status = "okay"; +}; + +&pll { + status = "okay"; +}; + +&rcc { + clocks = <&pll>; + clock-frequency = ; + clksys-prescaler = <1>; + slow-clock = <&clk_lse>; +}; + +&usart1 { + pinctrl-0 = <&usart1_tx_pa1 &usart1_rx_pa15>; + pinctrl-names = "default"; + current-speed = <115200>; + status = "okay"; +}; diff --git a/boards/st/nucleo_wl33cc1/nucleo_wl33cc1.yaml b/boards/st/nucleo_wl33cc1/nucleo_wl33cc1.yaml new file mode 100644 index 000000000000..d4c31956bbe8 --- /dev/null +++ b/boards/st/nucleo_wl33cc1/nucleo_wl33cc1.yaml @@ -0,0 +1,13 @@ +identifier: nucleo_wl33cc1 +name: ST NUCLEO-WL33CC1 +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb +ram: 32 +flash: 256 +supported: + - gpio + - serial +vendor: st diff --git a/boards/st/nucleo_wl33cc1/nucleo_wl33cc1_defconfig b/boards/st/nucleo_wl33cc1/nucleo_wl33cc1_defconfig new file mode 100644 index 000000000000..03801a9bb6a2 --- /dev/null +++ b/boards/st/nucleo_wl33cc1/nucleo_wl33cc1_defconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y From bfa86d7af6048f8b375c9e2b76e68d11d035e144 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 17:01:37 +0300 Subject: [PATCH 055/455] net: tcp: Pass the TCP header to tcp_endpoint_set() tcp_endpoint_set() derived the TCP header itself, once in each of its two address-family branches. Every caller already has the header in hand, or is one step away from a caller that does, so the derivation is redundant. Deriving it is not free: th_get() rewinds the packet cursor to the head of the buffer chain and walks the IP header again on every call, then checks that the TCP header is contiguous. On the SYN path tcp_conn_new() paid for that twice. Take the header as an argument instead and move the NULL check to the top of the function, where it covers both families. tcp_conn_new() takes it as well, since its only caller has already derived and checked it. The remaining caller, tcp_endpoint_cmp(), keeps deriving its own for now so that this commit changes no behaviour on the receive path; the connection lookup is dealt with separately. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index e4130086aca1..6c8b6973eac6 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -215,20 +215,18 @@ static size_t tcp_endpoint_len(net_sa_family_t af) } static int tcp_endpoint_set(union tcp_endpoint *ep, struct net_pkt *pkt, - enum pkt_addr src) + struct tcphdr *th, enum pkt_addr src) { int ret = 0; + if (th == NULL) { + return -ENOBUFS; + } + switch (net_pkt_family(pkt)) { case NET_AF_INET: if (IS_ENABLED(CONFIG_NET_IPV4)) { struct net_ipv4_hdr *ip = NET_IPV4_HDR(pkt); - struct tcphdr *th; - - th = th_get(pkt); - if (!th) { - return -ENOBUFS; - } memset(ep, 0, sizeof(*ep)); @@ -247,12 +245,6 @@ static int tcp_endpoint_set(union tcp_endpoint *ep, struct net_pkt *pkt, case NET_AF_INET6: if (IS_ENABLED(CONFIG_NET_IPV6)) { struct net_ipv6_hdr *ip = NET_IPV6_HDR(pkt); - struct tcphdr *th; - - th = th_get(pkt); - if (!th) { - return -ENOBUFS; - } memset(ep, 0, sizeof(*ep)); @@ -2309,7 +2301,7 @@ static bool tcp_endpoint_cmp(union tcp_endpoint *ep, struct net_pkt *pkt, { union tcp_endpoint ep_tmp; - if (tcp_endpoint_set(&ep_tmp, pkt, which) < 0) { + if (tcp_endpoint_set(&ep_tmp, pkt, th_get(pkt), which) < 0) { return false; } @@ -2342,7 +2334,7 @@ static struct tcp *tcp_conn_search(struct net_pkt *pkt) return found ? conn : NULL; } -static struct tcp *tcp_conn_new(struct net_pkt *pkt); +static struct tcp *tcp_conn_new(struct net_pkt *pkt, struct tcphdr *th); static enum net_verdict tcp_recv(struct net_conn *net_conn, struct net_pkt *pkt, @@ -2379,7 +2371,7 @@ static enum net_verdict tcp_recv(struct net_conn *net_conn, goto out; } - conn = tcp_conn_new(pkt); + conn = tcp_conn_new(pkt, th); if (!conn) { NET_ERR("Cannot allocate a new TCP connection"); goto in; @@ -2521,7 +2513,7 @@ static uint32_t tcp_init_isn(struct net_sockaddr *saddr, struct net_sockaddr *da /* Create a new tcp connection, as a part of it, create and register * net_context */ -static struct tcp *tcp_conn_new(struct net_pkt *pkt) +static struct tcp *tcp_conn_new(struct net_pkt *pkt, struct tcphdr *th) { struct tcp *conn = NULL; struct net_context *context = NULL; @@ -2542,13 +2534,13 @@ static struct tcp *tcp_conn_new(struct net_pkt *pkt) net_context_set_family(conn->context, net_pkt_family(pkt)); - if (tcp_endpoint_set(&conn->dst, pkt, TCP_EP_SRC) < 0) { + if (tcp_endpoint_set(&conn->dst, pkt, th, TCP_EP_SRC) < 0) { net_context_put(context); conn = NULL; goto err; } - if (tcp_endpoint_set(&conn->src, pkt, TCP_EP_DST) < 0) { + if (tcp_endpoint_set(&conn->src, pkt, th, TCP_EP_DST) < 0) { net_context_put(context); conn = NULL; goto err; @@ -4388,8 +4380,8 @@ static enum net_verdict tcp_input(struct net_conn *net_conn, net_tcp_get(context); net_context_set_family(context, net_pkt_family(pkt)); conn = context->tcp; - tcp_endpoint_set(&conn->dst, pkt, TCP_EP_SRC); - tcp_endpoint_set(&conn->src, pkt, TCP_EP_DST); + tcp_endpoint_set(&conn->dst, pkt, th, TCP_EP_SRC); + tcp_endpoint_set(&conn->src, pkt, th, TCP_EP_DST); /* Make an extra reference, the sanity check suite * will delete the connection explicitly */ @@ -4518,8 +4510,14 @@ enum net_verdict tp_input(struct net_conn *net_conn, net_context_set_family(context, net_pkt_family(pkt)); conn = context->tcp; - tcp_endpoint_set(&conn->dst, pkt, TCP_EP_SRC); - tcp_endpoint_set(&conn->src, pkt, TCP_EP_DST); + /* This is a UDP packet carrying the test + * protocol; only the port fields are read, + * and those alias the TCP header layout. + */ + tcp_endpoint_set(&conn->dst, pkt, th_get(pkt), + TCP_EP_SRC); + tcp_endpoint_set(&conn->src, pkt, th_get(pkt), + TCP_EP_DST); conn->iface = pkt->iface; tcp_conn_ref(conn); } From 3467596989f1b1885043b97c5be80d0fa8ce7f49 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 17:02:50 +0300 Subject: [PATCH 056/455] net: tcp: Derive the TCP header once per connection lookup Looking up the connection for a received segment cost two full TCP header derivations for every connection examined, not two in total. tcp_conn_cmp() called tcp_endpoint_cmp() twice, each of which built a temporary endpoint from the packet with tcp_endpoint_set(), and each of those derived the header again. th_get() rewinds the packet cursor to the head of the buffer chain, walks the IP header and checks the TCP header is contiguous, so this was O(connections) walks of the same packet just to find its owner. The temporary endpoint was rebuilt just as often, and it is the same value every time: it depends only on the packet. Build both of the packet's endpoints once, before the loop, and reduce tcp_endpoint_cmp() to the comparison it is named after. The comparison still takes its length from the connection's endpoint, so a connection of one family still cannot match a packet of another. tcp_conn_cmp() had no purpose left and is folded into the loop. A run with an empty connection list now builds two endpoints where it previously built none. That list always holds at least the listener in practice, and the two fills replace a pair of buffer walks per entry. The two endpoints live on tcp_conn_search()'s stack rather than in the deeper tcp_endpoint_cmp() frame, so peak usage on the receive thread grows by roughly the size of one union tcp_endpoint. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 6c8b6973eac6..fa65c77182c1 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -2296,34 +2296,36 @@ int net_tcp_get(struct net_context *context) return ret; } -static bool tcp_endpoint_cmp(union tcp_endpoint *ep, struct net_pkt *pkt, - enum pkt_addr which) +static bool tcp_endpoint_cmp(const union tcp_endpoint *ep, + const union tcp_endpoint *ep_pkt) { - union tcp_endpoint ep_tmp; - - if (tcp_endpoint_set(&ep_tmp, pkt, th_get(pkt), which) < 0) { - return false; - } - - return !memcmp(ep, &ep_tmp, tcp_endpoint_len(ep->sa.sa_family)); -} - -static bool tcp_conn_cmp(struct tcp *conn, struct net_pkt *pkt) -{ - return tcp_endpoint_cmp(&conn->src, pkt, TCP_EP_DST) && - tcp_endpoint_cmp(&conn->dst, pkt, TCP_EP_SRC); + /* The length comes from the connection's endpoint, as before, so a + * connection of one address family never matches a packet of another: + * the family byte is part of the compared region. + */ + return !memcmp(ep, ep_pkt, tcp_endpoint_len(ep->sa.sa_family)); } -static struct tcp *tcp_conn_search(struct net_pkt *pkt) +static struct tcp *tcp_conn_search(struct net_pkt *pkt, struct tcphdr *th) { + union tcp_endpoint src_ep, dst_ep; bool found = false; struct tcp *conn; struct tcp *tmp; + /* Build the packet's endpoints once instead of rebuilding them for + * every connection examined below. + */ + if (tcp_endpoint_set(&src_ep, pkt, th, TCP_EP_SRC) < 0 || + tcp_endpoint_set(&dst_ep, pkt, th, TCP_EP_DST) < 0) { + return NULL; + } + k_mutex_lock(&tcp_lock, K_FOREVER); SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tcp_conns, conn, tmp, next) { - found = tcp_conn_cmp(conn, pkt); + found = tcp_endpoint_cmp(&conn->src, &dst_ep) && + tcp_endpoint_cmp(&conn->dst, &src_ep); if (found) { break; } @@ -2354,7 +2356,7 @@ static enum net_verdict tcp_recv(struct net_conn *net_conn, goto out; } - conn = tcp_conn_search(pkt); + conn = tcp_conn_search(pkt, th); if (conn) { goto in; } @@ -4372,7 +4374,7 @@ static enum net_verdict tcp_input(struct net_conn *net_conn, enum net_verdict verdict = NET_DROP; if (th && (th_off(th) >= 5)) { - struct tcp *conn = tcp_conn_search(pkt); + struct tcp *conn = tcp_conn_search(pkt, th); if (conn == NULL && SYN == th_flags(th)) { struct net_context *context = @@ -4454,7 +4456,10 @@ enum net_verdict tp_input(struct net_conn *net_conn, { struct net_udp_hdr *uh = net_udp_get_hdr(pkt, NULL); size_t data_len = net_ntohs(uh->len) - sizeof(*uh); - struct tcp *conn = tcp_conn_search(pkt); + /* The test protocol rides on UDP; th_get() lands on the UDP header, + * whose port fields alias the TCP ones, which is all that is read. + */ + struct tcp *conn = tcp_conn_search(pkt, th_get(pkt)); size_t json_len = 0; struct tp *tp; struct tp_new *tp_new; From 28e97db922c6dce8adc92c32129e385231045e33 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 17:04:09 +0300 Subject: [PATCH 057/455] net: tcp: Reuse the received TCP header in tcp_in() tcp_recv() derives the TCP header to validate the segment and to find its connection, then throws the pointer away. tcp_in(), called immediately afterwards on the same packet, derives it again and gets an identical result. Each derivation rewinds the packet cursor to the head of the buffer chain and walks the IP header again. Pass the header from tcp_recv() instead, and fold its NULL check into the parameter check tcp_in() already performs. Holding the pointer across the call is not a new practice here: tcp_in() already keeps it for the whole state machine, through tcp_options_check(), tcp_data_get() and tcp_pkt_trim_data(). This extends that lifetime by one frame, over tcp_conn_search() and tcp_conn_new(), neither of which alters the buffer chain. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index fa65c77182c1..efff64436887 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -80,7 +80,8 @@ K_MEM_SLAB_DEFINE_STATIC_TYPE(tcp_conns_slab, struct tcp, static struct k_work_q tcp_work_q; static K_KERNEL_STACK_DEFINE(work_q_stack, CONFIG_NET_TCP_WORKQ_STACK_SIZE); -static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt); +static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt, + struct tcphdr *th); static bool is_destination_local(struct net_pkt *pkt); static void tcp_out(struct tcp *conn, uint8_t flags); static const char *tcp_state_to_str(enum tcp_state state, bool prefix); @@ -2383,7 +2384,7 @@ static enum net_verdict tcp_recv(struct net_conn *net_conn, } in: if (conn) { - verdict = tcp_in(conn, pkt); + verdict = tcp_in(conn, pkt, th); } else { net_tcp_reply_rst(pkt); } @@ -2964,9 +2965,9 @@ static void tcp_check_sock_options(struct tcp *conn) } /* TCP state machine, everything happens here */ -static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) +static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt, + struct tcphdr *th) { - struct tcphdr *th; uint8_t next = 0, fl = 0; bool do_close = false; bool connection_ok = false; @@ -2980,17 +2981,11 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) int close_status = 0; enum net_verdict verdict = NET_DROP; - if (conn == NULL || pkt == NULL) { + if (conn == NULL || pkt == NULL || th == NULL) { NET_ERR("Invalid parameters"); return NET_DROP; } - th = th_get(pkt); - if (th == NULL) { - NET_ERR("Failed to get TCP header"); - return NET_DROP; - } - tcp_options_len = (th_off(th) - 5) * 4; /* Currently we ignore ECN and CWR flags */ @@ -4392,7 +4387,7 @@ static enum net_verdict tcp_input(struct net_conn *net_conn, if (conn) { conn->iface = pkt->iface; - verdict = tcp_in(conn, pkt); + verdict = tcp_in(conn, pkt, th); } } From 66381eedb5256da9f43fad1ed8e32603ac8d558b Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 17:05:12 +0300 Subject: [PATCH 058/455] net: tcp: Pass the TCP header to tcp_data_len() tcp_data_len() derived the TCP header only to read its offset field, so every call rewound the packet cursor and walked the IP header again. tcp_in() calls it for each received segment and already holds the header; net_tcp_reply_rst() holds it too. Take it as an argument. The packet is still needed for the length and the IP header sizes, so only the derivation goes away. The debug string builder and the test protocol hook keep deriving their own, since neither has a header to hand. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index efff64436887..07901fe08e17 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -352,9 +352,8 @@ static const char *tcp_flags(uint8_t flags) return buf; } -static size_t tcp_data_len(struct net_pkt *pkt) +static size_t tcp_data_len(struct net_pkt *pkt, struct tcphdr *th) { - struct tcphdr *th = th_get(pkt); size_t tcp_options_len = (th_off(th) - 5) * 4; int len = net_pkt_get_len(pkt) - net_pkt_ip_hdr_len(pkt) - net_pkt_ip_opts_len(pkt) - sizeof(*th) - tcp_options_len; @@ -403,7 +402,7 @@ static const char *tcp_th(struct net_pkt *pkt, uint32_t *seq_ptr, uint32_t *ack_ } len += snprintk(buf + len, BUF_SIZE - len, - " Len=%ld", (long)tcp_data_len(pkt)); + " Len=%ld", (long)tcp_data_len(pkt, th)); end: #undef BUF_SIZE return buf; @@ -1545,7 +1544,8 @@ void net_tcp_reply_rst(struct net_pkt *pkt) UNALIGNED_PUT(RST, &th_rst->th_flags); UNALIGNED_PUT(th_pkt->th_ack, UNALIGNED_MEMBER_ADDR(th_rst, th_seq)); } else { - uint32_t ack = net_ntohl(th_pkt->th_seq) + tcp_data_len(pkt); + uint32_t ack = net_ntohl(th_pkt->th_seq) + + tcp_data_len(pkt, th_pkt); if (th_flags(th_pkt) & SYN) { ack++; @@ -3005,7 +3005,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt, NET_DBG("[%p] %s", conn, tcp_conn_state(conn, pkt)); - len = tcp_data_len(pkt); + len = tcp_data_len(pkt, th); /* first validate the seqnum */ if (!tcp_validate_seq(conn, th, len)) { @@ -4396,7 +4396,7 @@ static enum net_verdict tcp_input(struct net_conn *net_conn, static size_t tp_tcp_recv_cb(struct tcp *conn, struct net_pkt *pkt) { - ssize_t len = tcp_data_len(pkt); + ssize_t len = tcp_data_len(pkt, th_get(pkt)); struct net_pkt *up = tcp_pkt_clone(pkt); NET_DBG("[%p] pkt: %p, len: %zu", conn, pkt, net_pkt_get_len(pkt)); From e93271581ac4c407e90df37f6e530d59cc3c7d0b Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 17:07:00 +0300 Subject: [PATCH 059/455] net: tcp: Pass the TCP header to the debug helpers tcp_th() derived the TCP header itself, and tcp_conn_state() reaches it for every received segment through the log statement in tcp_in(). Since tcp_th() also calls tcp_data_len(), which used to derive the header too, a debug build walked the packet twice more per segment than a normal one. Pass the header down instead. Callers that log without a packet pass NULL for both, as they already did for the packet. Log arguments are not evaluated below their level, so this costs nothing in a default build; the point is that a build with TCP debugging turned on no longer pays for two extra walks per segment on top of everything else it pays for. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 07901fe08e17..5af55de8cbb8 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -361,12 +361,12 @@ static size_t tcp_data_len(struct net_pkt *pkt, struct tcphdr *th) return len > 0 ? (size_t)len : 0; } -static const char *tcp_th(struct net_pkt *pkt, uint32_t *seq_ptr, uint32_t *ack_ptr) +static const char *tcp_th(struct net_pkt *pkt, struct tcphdr *th, + uint32_t *seq_ptr, uint32_t *ack_ptr) { #define BUF_SIZE 80 static char buf[BUF_SIZE]; int len = 0; - struct tcphdr *th = th_get(pkt); uint32_t seq, ack; buf[0] = '\0'; @@ -1062,14 +1062,15 @@ static const char *tcp_state_to_str(enum tcp_state state, bool prefix) return prefix ? s : (s + 4); } -static const char *tcp_conn_state(struct tcp *conn, struct net_pkt *pkt) +static const char *tcp_conn_state(struct tcp *conn, struct net_pkt *pkt, + struct tcphdr *th) { #define BUF_SIZE 160 static char buf[BUF_SIZE]; uint32_t seq = conn->isn, ack = conn->isn_peer; snprintk(buf, BUF_SIZE, "%s [%s Seq=%u{%u} Ack=%u{%u}]", - pkt ? tcp_th(pkt, &seq, &ack) : "", + pkt ? tcp_th(pkt, th, &seq, &ack) : "", tcp_state_to_str(conn->state, false), conn->seq - seq, conn->seq, conn->ack - ack, conn->ack); @@ -1569,7 +1570,7 @@ void net_tcp_reply_rst(struct net_pkt *pkt) goto err; } - NET_DBG("%s", tcp_th(rst, NULL, NULL)); + NET_DBG("%s", tcp_th(rst, th_get(rst), NULL, NULL)); tcp_send(rst); @@ -2038,7 +2039,7 @@ static void tcp_timewait_timeout(struct k_work *work) struct tcp *conn = CONTAINER_OF(dwork, struct tcp, timewait_timer); /* no need to acquire the conn->lock as there is nothing scheduled here */ - NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL)); + NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL, NULL)); (void)tcp_conn_close(conn, -ETIMEDOUT); } @@ -2046,7 +2047,7 @@ static void tcp_timewait_timeout(struct k_work *work) static void tcp_establish_timeout(struct tcp *conn) { NET_DBG("[%p] Did not receive %s in %dms", conn, "ACK", ACK_TIMEOUT_MS); - NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL)); + NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL, NULL)); (void)tcp_conn_close(conn, -ETIMEDOUT); } @@ -2063,7 +2064,7 @@ static void tcp_fin_timeout(struct k_work *work) } NET_DBG("[%p] Did not receive %s in %dms", conn, "FIN", tcp_max_timeout_ms); - NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL)); + NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL, NULL)); (void)tcp_conn_close(conn, -ETIMEDOUT); } @@ -2096,7 +2097,7 @@ static void tcp_last_ack_timeout(struct k_work *work) struct tcp *conn = CONTAINER_OF(dwork, struct tcp, fin_timer); NET_DBG("[%p] Did not receive %s in %dms", conn, "last ACK", LAST_ACK_TIMEOUT_MS); - NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL)); + NET_DBG("[%p] %s", conn, tcp_conn_state(conn, NULL, NULL)); (void)tcp_conn_close(conn, -ETIMEDOUT); } @@ -3003,7 +3004,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt, return NET_DROP; } - NET_DBG("[%p] %s", conn, tcp_conn_state(conn, pkt)); + NET_DBG("[%p] %s", conn, tcp_conn_state(conn, pkt, th)); len = tcp_data_len(pkt, th); @@ -3753,7 +3754,7 @@ int net_tcp_put(struct net_context *context, bool force_close) k_mutex_lock(&conn->lock, K_FOREVER); - NET_DBG("[%p] %s", conn, conn ? tcp_conn_state(conn, NULL) : ""); + NET_DBG("[%p] %s", conn, conn ? tcp_conn_state(conn, NULL, NULL) : ""); NET_DBG("[%p] context %p %s", conn, context, ({ const char *state = net_context_state(context); state ? state : ""; })); From fbac9c55c1bb1fd8f6d6cc7852d3edd217cfd872 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 17:07:58 +0300 Subject: [PATCH 060/455] net: tcp: Refresh the TCP header after trimming a segment tcp_pkt_trim_data() drops the part of a segment that was already received, and it does so by building a replacement buffer chain and swapping it into the packet. Any pointer into the old chain is stale afterwards, including the TCP header the state machine is holding. Today that is harmless: the jump to the receive path does not touch the header again. It stops being harmless the moment anything on that path does, and nothing in the code said so. Re-derive the header after a successful trim, and describe the lifetime rule where the pointer is produced, listing the operations that end it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 5af55de8cbb8..31305195612f 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -184,6 +184,19 @@ static int tcp_pkt_linearize(struct net_pkt *pkt, size_t pos, size_t len) return ret; } +/* Locate the TCP header inside a received packet. + * + * The returned pointer aims into pkt->buffer, at ip_hdr_len + ip_opts_len, and + * stays valid only while that fragment keeps its data pointer and its place in + * the chain. It does not survive anything that pulls, trims, splices or + * replaces buffers, nor handing the packet to another owner. In the receive + * path that means tcp_pkt_trim_data(), tcp_pkt_pull(), the k_fifo_put() in + * tcp_data_get() and the final net_pkt_unref(); re-derive after those rather + * than carrying the old pointer across. + * + * Note the packet is left in overwrite mode with the cursor parked on the TCP + * header, and neither is restored. + */ static struct tcphdr *th_get(struct net_pkt *pkt) { size_t ip_len = net_pkt_ip_hdr_len(pkt) + net_pkt_ip_opts_len(pkt); @@ -3464,6 +3477,16 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt, int32_t new_len = tcp_compute_new_length(conn, th, len, false); if (tcp_pkt_trim_data(conn, pkt, len, (size_t)(len - new_len)) == 0) { + /* Trimming replaces pkt->buffer, so the header + * derived on entry no longer points into this + * packet. + */ + th = th_get(pkt); + if (th == NULL) { + verdict = NET_DROP; + break; + } + len = new_len; goto data_recv; } else { From 8a4eb71fe0c5fb2107b69b6e32526f894ce1aaf5 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 19 Aug 2026 09:38:05 +0300 Subject: [PATCH 061/455] net: tcp: Reject a short or missing header in the length helpers tcp_data_len() works out the option length as (th_off - 5) * 4 without first checking that th_off is at least 5. A segment carrying a smaller offset makes that underflow to a very large size_t, and the payload length derived from it is then whatever the subtraction happens to wrap to. tcp_recv() rejects such a segment at ingress, but that is not the only way in. connection.c calls net_tcp_reply_rst() for a segment that matched no connection, which is the path a segment addressed to a closed port takes when CONFIG_NET_TCP_REJECT_CONN_WITH_RST is set. That path checks the header only for NULL, so a remote peer can reach the underflow and steer the acknowledgment number of the RST sent back to it. Check the offset where it is used instead, and treat a header that is absent or too short as carrying no data. Give tcp_th() the same tolerance for a NULL header: it takes the header from its caller now, and net_tcp_reply_rst() hands it one straight from th_get() without looking at it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 31305195612f..0faf408b23fe 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -367,8 +367,19 @@ static const char *tcp_flags(uint8_t flags) static size_t tcp_data_len(struct net_pkt *pkt, struct tcphdr *th) { - size_t tcp_options_len = (th_off(th) - 5) * 4; - int len = net_pkt_get_len(pkt) - net_pkt_ip_hdr_len(pkt) - + size_t tcp_options_len; + int len; + + /* An offset below the header size would make the option length + * underflow, and the payload length derived from it is then whatever + * the subtraction wraps to. Such a segment carries no usable data. + */ + if (th == NULL || th_off(th) < 5) { + return 0; + } + + tcp_options_len = (th_off(th) - 5) * 4; + len = net_pkt_get_len(pkt) - net_pkt_ip_hdr_len(pkt) - net_pkt_ip_opts_len(pkt) - sizeof(*th) - tcp_options_len; return len > 0 ? (size_t)len : 0; @@ -384,6 +395,11 @@ static const char *tcp_th(struct net_pkt *pkt, struct tcphdr *th, buf[0] = '\0'; + if (th == NULL) { + len += snprintk(buf + len, BUF_SIZE - len, "no header"); + goto end; + } + if (th_off(th) < 5) { len += snprintk(buf + len, BUF_SIZE - len, "bogus th_off: %hu", (uint16_t)th_off(th)); From 3f5a761db64c367ae3d16ce982d8f7abcdfbb0f6 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 18 Aug 2026 19:00:48 +0300 Subject: [PATCH 062/455] net: tcp: Take the packet length as an argument when pulling tcp_pkt_pull() worked out the packet's length itself, which walks the whole buffer chain, purely to reject a pull that is longer than the packet. All three callers already knew the answer. The one that matters is the acknowledgement path, which pulls the acked bytes off the send queue. That queue holds the whole unacked window, so the walk is proportional to the window and it runs on every ACK that covers new data. The caller had already compared the same pull length against conn->send_data_total on the line above, so the walk was establishing something it had just been told. The other two callers hold the length in a local because they needed it to work out the header size in the first place. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jukka Rissanen --- subsys/net/ip/tcp.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 0faf408b23fe..462a3b8af37e 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -1695,9 +1695,8 @@ static void tcp_out(struct tcp *conn, uint8_t flags) (void)tcp_out_ext(conn, flags, 0 /* no data */, conn->seq + conn->unacked_len); } -static int tcp_pkt_pull(struct net_pkt *pkt, size_t len) +static int tcp_pkt_pull(struct net_pkt *pkt, size_t len, size_t total) { - int total = net_pkt_get_len(pkt); int ret = 0; if (len > total) { @@ -1744,7 +1743,7 @@ static int tcp_pkt_trim_data(struct tcp *conn, struct net_pkt *pkt, size_t data_ } /* Last, append the valid data part to the new_pkt */ - ret = tcp_pkt_pull(pkt, hdrlen + trim_len); + ret = tcp_pkt_pull(pkt, hdrlen + trim_len, total); if (ret < 0) { goto out; } @@ -2938,15 +2937,17 @@ static void tcp_out_of_order_data(struct tcp *conn, struct net_pkt *pkt, size_t data_len, uint32_t seq) { size_t headers_len; + size_t total_len; if (data_len == 0) { return; } - headers_len = net_pkt_get_len(pkt) - data_len; + total_len = net_pkt_get_len(pkt); + headers_len = total_len - data_len; /* Get rid of protocol headers from the data */ - if (tcp_pkt_pull(pkt, headers_len) < 0) { + if (tcp_pkt_pull(pkt, headers_len, total_len) < 0) { return; } @@ -3381,8 +3382,8 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt, NET_DBG("[%p] len_acked=%u", conn, len_acked); if ((conn->send_data_total < len_acked) || - (tcp_pkt_pull(&conn->send_data, - len_acked) < 0)) { + (tcp_pkt_pull(&conn->send_data, len_acked, + conn->send_data_total) < 0)) { NET_ERR("[%p] Invalid len_acked=%u " "(total=%zu)", conn, len_acked, conn->send_data_total); From 5a95bb9cffed10c226c2f04d61d63d5a41ca8df0 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 15 Jul 2026 21:32:14 +0530 Subject: [PATCH 063/455] arch: riscv: Add S-mode only support - When launching Zephyr from a bootloader (eg: u-boot), the image binary launches in S-mode. Thus we cannot call M-mode only functions, or perform M to S mode transition. - SMP is currently broken in this setup. MPU, FPU not tested yet either since PolarFire soc does not enable it by default. - Tested on beaglev_fire with PolarFire soc. Signed-off-by: Ayush Singh --- arch/riscv/Kconfig | 37 +++++++++++++++++++++++++++++----- arch/riscv/core/CMakeLists.txt | 2 +- arch/riscv/core/reset.S | 17 ++++++++++++++-- arch/riscv/core/sbi.S | 4 ---- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c92a79573ab5..89ee8a61b434 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -679,17 +679,44 @@ config RISCV_S_MODE bool "Supervisor mode (S-mode)" depends on $(dt_compat_all_has_prop,riscv,$(RISCV_PRIV_MODES_PROP),s) help - Indicates that the kernel runs in Supervisor mode (S-mode). When - enabled, the boot code performs an M-mode to S-mode transition - before entering the C runtime, and a minimal SBI runtime is kept - resident in M-mode to service timer and ecall requests. + Indicates that the kernel runs in Supervisor mode (S-mode). endchoice # RISCV_PRIVILEGE_MODE +choice RISCV_S_MODE_SBI + prompt "RISC-V SBI Implementation" + depends on RISCV_S_MODE + default RISCV_S_MODE_INTERNAL_SBI + help + Select the Supervisor Binary Interface (SBI) implementation to use + when running in Supervisor mode (S-mode). + +config RISCV_S_MODE_INTERNAL_SBI + bool "Use built-in minimal SBI runtime" + help + Enable this to have the boot code handle the transition from + Machine mode (M-mode) to Supervisor mode (S-mode) internally. + A minimal SBI runtime will remain resident in M-mode to service + timer interrupts and ecall requests. + +config RISCV_S_MODE_EXTERNAL_SBI + bool "Enter S-mode directly, relying on an external SBI (e.g. OpenSBI)" + # TODO: Remove once SMP is fixed with this configuration + depends on !SMP + help + Skip the in-tree M-mode boot sequence (PMP/medeleg/mideleg/ + mcounteren/mtvec setup and the mret drop to S-mode). Use this when + Zephyr is launched as a payload by a bootloader (e.g. U-Boot) that + is already running in S-mode under an external M-mode SBI + implementation such as OpenSBI, which has already performed the + M-mode setup and delegation. + +endchoice # RISCV_S_MODE_SBI + config RISCV_M_MODE_STACK_SIZE int "M-mode SBI handler stack size (bytes)" default 256 - depends on RISCV_S_MODE + depends on RISCV_S_MODE_INTERNAL_SBI help Size in bytes of the dedicated stack used by the in-tree M-mode SBI runtime handler. Increase this if the handler is extended with diff --git a/arch/riscv/core/CMakeLists.txt b/arch/riscv/core/CMakeLists.txt index 04a60a92e38f..68f33d68f93f 100644 --- a/arch/riscv/core/CMakeLists.txt +++ b/arch/riscv/core/CMakeLists.txt @@ -28,7 +28,7 @@ zephyr_library_sources_ifdef(CONFIG_PM_S2RAM pm_s2ram.c pm_s2ram.S) zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c) zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD irq_offload.c) zephyr_library_sources_ifdef(CONFIG_USE_ISR_WRAPPER isr.S) -zephyr_library_sources_ifdef(CONFIG_RISCV_S_MODE sbi.S) +zephyr_library_sources_ifdef(CONFIG_RISCV_S_MODE_INTERNAL_SBI sbi.S) zephyr_library_sources_ifdef(CONFIG_RISCV_PMP pmp.c pmp.S) zephyr_linker_sources_ifdef(CONFIG_RISCV_PMP ROM_SECTIONS pmp.ld) zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c) diff --git a/arch/riscv/core/reset.S b/arch/riscv/core/reset.S index 43759a677a28..52ac53eaa35a 100644 --- a/arch/riscv/core/reset.S +++ b/arch/riscv/core/reset.S @@ -29,7 +29,7 @@ GTEXT(z_prep_c) GDATA(riscv_cpu_wake_flag) GDATA(riscv_cpu_sp) GTEXT(arch_secondary_cpu_init) -#ifdef CONFIG_RISCV_S_MODE +#ifdef CONFIG_RISCV_S_MODE_INTERNAL_SBI GTEXT(__m_mode_sbi_handler) #endif @@ -80,7 +80,14 @@ tls_skip\@: * the C domain */ SECTION_FUNC(TEXT, __initialize) +#ifndef CONFIG_RISCV_S_MODE_EXTERNAL_SBI + /* + * When entered directly in S-mode by an external bootloader/SBI, a0 + * already holds the hart id per the RISC-V S-mode boot protocol + * (a0=hartid, a1=dtb), and mhartid would trap, so only read it here. + */ csrr a0, mhartid +#endif /* CONFIG_RISCV_S_MODE_EXTERNAL_SBI */ li t0, CONFIG_RV_BOOT_HART beq a0, t0, boot_first_core j boot_secondary_core @@ -92,7 +99,11 @@ boot_first_core: * Enable floating-point. */ li t0, MSTATUS_FS_INIT +#ifdef CONFIG_RISCV_S_MODE_EXTERNAL_SBI + csrs sstatus, t0 +#else csrs mstatus, t0 +#endif /* CONFIG_RISCV_S_MODE_EXTERNAL_SBI */ /* * Floating-point rounding mode set to IEEE-754 default, and clear @@ -135,7 +146,7 @@ aa_loop: riscv_tls_init_early #endif -#ifdef CONFIG_RISCV_SMRNMI_ENABLE_NMI_DELIVERY +#if defined(CONFIG_RISCV_SMRNMI_ENABLE_NMI_DELIVERY) && !defined(CONFIG_RISCV_S_MODE_EXTERNAL_SBI) csrs CSR_MNSTATUS, MNSTATUS_NMIE #endif @@ -144,6 +155,7 @@ aa_loop: #endif #ifdef CONFIG_RISCV_S_MODE +#ifdef CONFIG_RISCV_S_MODE_INTERNAL_SBI /* Set M-mode trap vector to our SBI handler */ la t0, __m_mode_sbi_handler csrw mtvec, t0 @@ -188,6 +200,7 @@ aa_loop: la t0, s_mode_entry csrw mepc, t0 mret +#endif /* CONFIG_RISCV_S_MODE_INTERNAL_SBI */ s_mode_entry: #endif /* CONFIG_RISCV_S_MODE */ diff --git a/arch/riscv/core/sbi.S b/arch/riscv/core/sbi.S index cf4b49e1e217..26481b779039 100644 --- a/arch/riscv/core/sbi.S +++ b/arch/riscv/core/sbi.S @@ -13,8 +13,6 @@ #include #include "asm_macros.inc" -#ifdef CONFIG_RISCV_S_MODE - #ifdef CONFIG_64BIT #define REGBYTES 8 #else @@ -221,5 +219,3 @@ m_mode_return: csrrw sp, mscratch, sp mret - -#endif /* CONFIG_RISCV_S_MODE */ From aa17c16d1194e46fe1bfe2f563cdee9e263c9aa1 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 28 Jul 2026 11:35:18 +0530 Subject: [PATCH 064/455] dts: riscv: microchip: mpfs: Add s-mode - Add property to allow using all u54 cores in S-mode in Zephyr. - Tested using beaglev_fire. Signed-off-by: Ayush Singh --- dts/riscv/microchip/mpfs.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dts/riscv/microchip/mpfs.dtsi b/dts/riscv/microchip/mpfs.dtsi index 708885a6e4fa..bdfecbed0abb 100644 --- a/dts/riscv/microchip/mpfs.dtsi +++ b/dts/riscv/microchip/mpfs.dtsi @@ -39,6 +39,7 @@ reg = <0x1>; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr", "zifencei"; + riscv,privilege-modes = "m", "s"; hlic1: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -55,6 +56,7 @@ reg = <0x2>; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr", "zifencei"; + riscv,privilege-modes = "m", "s"; hlic2: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -71,6 +73,7 @@ reg = <0x3>; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr", "zifencei"; + riscv,privilege-modes = "m", "s"; hlic3: interrupt-controller { compatible = "riscv,cpu-intc"; @@ -87,6 +90,7 @@ reg = <0x4>; riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr", "zifencei"; + riscv,privilege-modes = "m", "s"; hlic4: interrupt-controller { compatible = "riscv,cpu-intc"; From 4d232014de132d77e3b42783acd21de6444d4f77 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 29 Jul 2026 12:06:14 +0530 Subject: [PATCH 065/455] boards: qemu: riscv64: Add opensbi board target - Allows testing CONFIG_RISCV_S_MODE_EXTERNAL_SBI - The firmware address needs to change since opensbi is loaded at the top of RAM. Signed-off-by: Ayush Singh --- boards/qemu/riscv64/board.cmake | 8 +++- boards/qemu/riscv64/board.yml | 1 + ...qemu_riscv64_qemu_virt_riscv64_opensbi.dts | 37 +++++++++++++++++++ ...emu_riscv64_qemu_virt_riscv64_opensbi.yaml | 15 ++++++++ ...iscv64_qemu_virt_riscv64_opensbi_defconfig | 14 +++++++ 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.dts create mode 100644 boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.yaml create mode 100644 boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi_defconfig diff --git a/boards/qemu/riscv64/board.cmake b/boards/qemu/riscv64/board.cmake index 392d083c4375..729fde51a860 100644 --- a/boards/qemu/riscv64/board.cmake +++ b/boards/qemu/riscv64/board.cmake @@ -24,9 +24,15 @@ if(CONFIG_INPUT_VIRTIO) endif() endif() +if(CONFIG_RISCV_S_MODE_EXTERNAL_SBI) + set(qemu_bios default) +else() + set(qemu_bios none) +endif() + set(QEMU_BOARD_FLAGS -machine virt - -bios none + -bios ${qemu_bios} -m 256 -cpu ${qemu_riscv_cpu} ${QEMU_VIRTIO_INPUT_FLAGS} diff --git a/boards/qemu/riscv64/board.yml b/boards/qemu/riscv64/board.yml index 9fd3946f1302..e95793a7706b 100644 --- a/boards/qemu/riscv64/board.yml +++ b/boards/qemu/riscv64/board.yml @@ -7,3 +7,4 @@ board: variants: - name: smp - name: smode + - name: opensbi diff --git a/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.dts b/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.dts new file mode 100644 index 000000000000..647695460bc3 --- /dev/null +++ b/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.dts @@ -0,0 +1,37 @@ +/* Copyright (c) 2026 Alexios Lyrakis */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/dts-v1/; + +#include +#include + +/delete-node/ &ram0; + +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,sram = &sram; + }; + + cpus { + riscv,privilege-modes = "m", "s", "u"; + }; + + /* + * When launched as an S-mode payload under an external SBI (OpenSBI), the + * M-mode firmware occupies the bottom of RAM (0x80000000 onwards). Move the + * Zephyr image up by 2 MiB so it does not overlap OpenSBI, and shrink the + * region by the same amount so it still ends at the top of the 256 MiB the + * QEMU virt machine provides (0x90000000). + */ + sram: memory@80200000 { + device_type = "memory"; + reg = <0x80200000 0x0fe00000>; + }; +}; + +&uart0 { + status = "okay"; +}; diff --git a/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.yaml b/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.yaml new file mode 100644 index 000000000000..fd357cea4b7d --- /dev/null +++ b/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi.yaml @@ -0,0 +1,15 @@ +identifier: qemu_riscv64/qemu_virt_riscv64/opensbi +name: QEMU Emulation for RISC-V 64-bit S-mode with external SBI +type: qemu +simulation: + - name: qemu +arch: riscv +toolchain: + - zephyr +ram: 260096 +flash: 32768 +testing: + default: true + ignore_tags: + - net + - bluetooth diff --git a/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi_defconfig b/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi_defconfig new file mode 100644 index 000000000000..5ef0247eff16 --- /dev/null +++ b/boards/qemu/riscv64/qemu_riscv64_qemu_virt_riscv64_opensbi_defconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_UART_CONSOLE=y +CONFIG_STACK_SENTINEL=y +CONFIG_XIP=n +CONFIG_RISCV_S_MODE=y +CONFIG_RISCV_S_MODE_EXTERNAL_SBI=y +# The PLIC driver configures PLIC context 0 (M-mode) for hart 0. In S-mode +# Zephyr the correct context is 1 (supervisor mode), so external device +# interrupts via the PLIC do not reach the CPU. Disable interrupt-driven +# UART TX so the shell backend falls back to polling, which works correctly. +CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n From d07638ea9b512af6b68dbe81fbd9b75ba6c9ed08 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 30 Jul 2026 13:44:53 +0530 Subject: [PATCH 066/455] tests: Fix tests for riscv smode only targets Some functionality is not present in RISCV smode setups. Hence add filter for these tests. Signed-off-by: Ayush Singh --- tests/arch/common/interrupt/tests.yaml | 14 ++++++-------- tests/arch/common/stack_unwind/tests.yaml | 8 ++++---- tests/subsys/debug/coredump_threads/tests.yaml | 10 ++++------ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/tests/arch/common/interrupt/tests.yaml b/tests/arch/common/interrupt/tests.yaml index f01ff74aea1d..50e2d3a7569d 100644 --- a/tests/arch/common/interrupt/tests.yaml +++ b/tests/arch/common/interrupt/tests.yaml @@ -49,9 +49,6 @@ tests: - cdns_swerv/s400/pmp/whisper - cdns_swerv/s420/whisper - cdns_swerv/s420/64/whisper - # In S-mode only SSIP (IRQ 1) can be triggered from software; the - # shared interrupt test requires two independent triggerable IRQ lines. - - qemu_riscv64/qemu_virt_riscv64/smode # No trigger_irq implementation for the BCM2836 L1 + BCM2835 ARMC # interrupt controller pair in interrupt_util.h yet. - rpi_zero_2w @@ -62,7 +59,9 @@ tests: - mips extra_configs: - CONFIG_SHARED_INTERRUPTS=y - filter: not CONFIG_TRUSTED_EXECUTION_NONSECURE + # In S-mode only SSIP (IRQ 1) can be triggered from software; the + # shared interrupt test requires two independent triggerable IRQ lines. + filter: not CONFIG_TRUSTED_EXECUTION_NONSECURE and not CONFIG_RISCV_S_MODE arch.shared_interrupt.lto: &shared-interrupt-lto platform_exclude: # excluded because of failures during test_prevent_interruption @@ -77,9 +76,6 @@ tests: - cdns_swerv/s400/pmp/whisper - cdns_swerv/s420/whisper - cdns_swerv/s420/64/whisper - # In S-mode only SSIP (IRQ 1) can be triggered from software; the - # shared interrupt test requires two independent triggerable IRQ lines. - - qemu_riscv64/qemu_virt_riscv64/smode # On it8xxx2_evb, current trigger_irq implementation of RISC-V architecture # does not trigger interrupts - it8xxx2_evb @@ -99,9 +95,11 @@ tests: - CONFIG_ISR_TABLES_LOCAL_DECLARATION=y - CONFIG_LTO=y # CONFIG_CODE_DATA_RELOCATION causes a build error (issue #69730) + # In S-mode only SSIP (IRQ 1) can be triggered from software; the + # shared interrupt test requires two independent triggerable IRQ lines. filter: > not CONFIG_TRUSTED_EXECUTION_NONSECURE and CONFIG_ISR_TABLES_LOCAL_DECLARATION_SUPPORTED - and not CONFIG_CODE_DATA_RELOCATION + and not CONFIG_CODE_DATA_RELOCATION and not CONFIG_RISCV_S_MODE arch.shared_interrupt.lto.speed: <<: *shared-interrupt-lto extra_configs: diff --git a/tests/arch/common/stack_unwind/tests.yaml b/tests/arch/common/stack_unwind/tests.yaml index 4e97d7902856..855fd9f9b0bc 100644 --- a/tests/arch/common/stack_unwind/tests.yaml +++ b/tests/arch/common/stack_unwind/tests.yaml @@ -71,10 +71,6 @@ tests: # these platforms disabled here since CONFIG_ARM_MMU=n by default - fvp_baser_aemv8r/fvp_aemv8r_aarch64 - fvp_baser_aemv8r/fvp_aemv8r_aarch64/smp - # In S-mode z_riscv_fatal_error() is called directly (no ecall), adding - # two extra func1 frames at the start; the regex expects the alternating - # func1/func2 pattern but gets func1/func1/func2 instead. - - qemu_riscv64/qemu_virt_riscv64/smode extra_configs: - CONFIG_FRAME_POINTER=y - CONFIG_SYMTAB=y @@ -86,3 +82,7 @@ tests: - "\\[func1\\+0x\\w+\\]" - "\\[func2\\+0x\\w+\\]" - "\\[func1\\+0x\\w+\\]" + # In S-mode z_riscv_fatal_error() is called directly (no ecall), adding + # two extra func1 frames at the start; the regex expects the alternating + # func1/func2 pattern but gets func1/func1/func2 instead. + filter: not CONFIG_RISCV_S_MODE diff --git a/tests/subsys/debug/coredump_threads/tests.yaml b/tests/subsys/debug/coredump_threads/tests.yaml index be6861df3519..21146ce6ce4c 100644 --- a/tests/subsys/debug/coredump_threads/tests.yaml +++ b/tests/subsys/debug/coredump_threads/tests.yaml @@ -14,12 +14,10 @@ common: - qemu_cortex_a53/qemu_cortex_a53/smp tests: debug.coredump.threads: - platform_exclude: - # In S-mode ARCH_EXCEPT calls z_riscv_fatal_error() with esf=NULL; - # arch_coredump_info_dump() returns immediately when esf is NULL so - # the coredump output is never produced. - - qemu_riscv64/qemu_virt_riscv64/smode - filter: CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_THREADS + # In S-mode ARCH_EXCEPT calls z_riscv_fatal_error() with esf=NULL; + # arch_coredump_info_dump() returns immediately when esf is NULL so + # the coredump output is never produced. + filter: CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_THREADS and not CONFIG_RISCV_S_MODE harness: console harness_config: type: multi_line From 489328f78f34161ee326cfeea82e327cbcd8269d Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 15:22:43 +1000 Subject: [PATCH 067/455] net: lib: dns: dispatcher: ensure debug output on RX Ensure that there is some form of logging from the DNS dispatcher module at `LOG_LEVEL_DBG` when a DNS message is received and processed. Failure to do so can lead to the misleading impression that no response was received at all. Signed-off-by: Jordan Yates --- subsys/net/lib/dns/dispatcher.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/subsys/net/lib/dns/dispatcher.c b/subsys/net/lib/dns/dispatcher.c index 1a86ed5aa593..b50074a6f645 100644 --- a/subsys/net/lib/dns/dispatcher.c +++ b/subsys/net/lib/dns/dispatcher.c @@ -64,17 +64,21 @@ static int dns_dispatch(struct dns_socket_dispatcher *dispatcher, /* Make sure that we can read DNS id, flags and rcode */ if (dns_msg.msg_size < (sizeof(uint16_t) + sizeof(uint16_t))) { + NET_WARN("Invalid message size: %d < %zd", dns_msg.msg_size, + (sizeof(uint16_t) + sizeof(uint16_t))); ret = -EINVAL; goto done; } if (dns_header_rcode(dns_msg.msg) == DNS_HEADER_REFUSED) { + NET_WARN("DNS_HEADER_REFUSED"); ret = -EINVAL; goto done; } is_query = (dns_header_qr(dns_msg.msg) == DNS_QUERY); if (is_query) { + NET_DBG("Received %d byte DNS query message", dns_msg.msg_size); if (dispatcher->type == DNS_SOCKET_RESPONDER) { /* Call the responder callback */ ret = dispatcher->cb(dispatcher, sock, @@ -93,6 +97,7 @@ static int dns_dispatch(struct dns_socket_dispatcher *dispatcher, } else { /* So this was an answer to a query that was made by resolver. */ + NET_DBG("Received %d byte DNS answer message", dns_msg.msg_size); if (dispatcher->type == DNS_SOCKET_RESOLVER) { /* Call the resolver callback */ ret = dispatcher->cb(dispatcher, sock, From ab3fc268987e4ce5cf3bdf46c908c89ea2b4b91b Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 14:08:08 +1000 Subject: [PATCH 068/455] samples: net: cellular_modem: continue on DNS failures Since the default configuration of the sample will always result in a DNS query failure (since `test-endpoint.com` is not a valid URL), continue with the rest of the sample when that happens, instead of leaving the modem powered up and idling. Signed-off-by: Jordan Yates --- samples/net/cellular_modem/src/main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/samples/net/cellular_modem/src/main.c b/samples/net/cellular_modem/src/main.c index 0309694c4710..15597f5985af 100644 --- a/samples/net/cellular_modem/src/main.c +++ b/samples/net/cellular_modem/src/main.c @@ -443,6 +443,7 @@ NET_MGMT_REGISTER_EVENT_HANDLER(l4_events, L4_EVENT_MASK, l4_event_handler, NULL int main(void) { + bool valid_dns = false; uint16_t *port; int ret; @@ -487,7 +488,9 @@ int main(void) ret = sample_dns_request(); if (ret < 0) { printk("DNS query failed\n"); - return -1; + goto power_cycle; + } else { + valid_dns = true; } { @@ -529,13 +532,15 @@ int main(void) return -1; } - printk("Restart modem\n"); +power_cycle: + printk("Shutting down modem\n"); ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND); if (ret != 0) { printk("Failed to power down modem\n"); return -1; } + printk("Restarting modem\n"); pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME); printk("Waiting for L4 connected\n"); @@ -549,8 +554,11 @@ int main(void) /* Wait a bit to avoid (unsuccessfully) trying to send the first echo packet too quickly. */ k_sleep(K_SECONDS(5)); - ret = sample_echo_packet(net_sad(&sample_test_dns_addrinfo.ai_addr_storage), - sample_test_dns_addrinfo.ai_addrlen, port); + if (valid_dns) { + /* Only run the second echo if the original DNS succeeded */ + ret = sample_echo_packet(net_sad(&sample_test_dns_addrinfo.ai_addr_storage), + sample_test_dns_addrinfo.ai_addrlen, port); + } if (ret < 0) { printk("Failed to send echos after restart\n"); From 4d82e0835a4cf476b43219edeaab2dc5ca4b308b Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 15:40:39 +1000 Subject: [PATCH 069/455] samples: net: cellular_modem: improve DNS handling Instead of blocking for the maximum timeout until the DNS query returns a success, block until the DNS query completes and handle whether it succeeded immediately. For the default build where `ENDPOINT_HOSTNAME = "test-endpoint.com"` and the DNS query always fails, this prevents the sample from sitting around for 19 seconds doing nothing after the query has already failed. Signed-off-by: Jordan Yates --- samples/net/cellular_modem/src/main.c | 37 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/samples/net/cellular_modem/src/main.c b/samples/net/cellular_modem/src/main.c index 15597f5985af..c86c804a6d56 100644 --- a/samples/net/cellular_modem/src/main.c +++ b/samples/net/cellular_modem/src/main.c @@ -36,7 +36,7 @@ const struct device *modem = DEVICE_DT_GET(DT_ALIAS(modem)); static uint8_t sample_test_packet[SAMPLE_TEST_PACKET_SIZE]; static uint8_t sample_recv_buffer[SAMPLE_TEST_PACKET_SIZE]; -static bool sample_test_dns_in_progress; +static bool sample_test_dns_success; static struct dns_addrinfo sample_test_dns_addrinfo; struct net_if *ppp_iface; K_EVENT_DEFINE(l4_event); @@ -231,17 +231,23 @@ static void modem_event_cb(const struct device *dev, enum cellular_event evt, co static void sample_dns_request_result(enum dns_resolve_status status, struct dns_addrinfo *info, void *user_data) { - if (sample_test_dns_in_progress == false) { - return; - } - - if (status != DNS_EAI_INPROGRESS) { - return; + switch (status) { + case DNS_EAI_INPROGRESS: + sample_test_dns_success = true; + sample_test_dns_addrinfo = *info; + break; + case DNS_EAI_ALLDONE: + k_sem_give(&dns_query_sem); + break; + case DNS_EAI_AGAIN: + case DNS_EAI_FAIL: + case DNS_EAI_NONAME: + printk("DNS query failed: %d\n", status); + k_sem_give(&dns_query_sem); + break; + default: + printk("Unhandled DNS status: %d\n", status); } - - sample_test_dns_in_progress = false; - sample_test_dns_addrinfo = *info; - k_sem_give(&dns_query_sem); } static int sample_dns_request(void) @@ -249,7 +255,6 @@ static int sample_dns_request(void) static uint16_t dns_id; int ret; - sample_test_dns_in_progress = true; ret = dns_get_addr_info(SAMPLE_TEST_ENDPOINT_HOSTNAME, DNS_QUERY_TYPE_A, &dns_id, @@ -260,7 +265,14 @@ static int sample_dns_request(void) return -EAGAIN; } + /* Wait for DNS query to complete */ if (k_sem_take(&dns_query_sem, K_SECONDS(20)) < 0) { + printk("DNS query timed out\n"); + return -EAGAIN; + } + + /* Validate whether the query succeeded */ + if (!sample_test_dns_success) { return -EAGAIN; } @@ -487,7 +499,6 @@ int main(void) printk("Performing DNS lookup of %s\n", SAMPLE_TEST_ENDPOINT_HOSTNAME); ret = sample_dns_request(); if (ret < 0) { - printk("DNS query failed\n"); goto power_cycle; } else { valid_dns = true; From 36145a6f1164e2e0a18c8c83d5b1be6e7d3f6e23 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 22:50:19 -0700 Subject: [PATCH 070/455] Bluetooth: OTS: encode a checksum when one was calculated oacp_ind_send() appended the Checksum Value based on the requested op code alone, but validation only fills the response buffer on success. Every error path therefore pulled four bytes from an empty buffer, panicking with assertions enabled and reading stale stack without them. Signed-off-by: Flavio Ceolin --- subsys/bluetooth/services/ots/ots_oacp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/subsys/bluetooth/services/ots/ots_oacp.c b/subsys/bluetooth/services/ots/ots_oacp.c index a372c30316fc..04f222456653 100644 --- a/subsys/bluetooth/services/ots/ots_oacp.c +++ b/subsys/bluetooth/services/ots/ots_oacp.c @@ -648,7 +648,9 @@ static void oacp_ind_send(const struct bt_gatt_attr *oacp_attr, oacp_res[oacp_res_len++] = oacp_proc.type; oacp_res[oacp_res_len++] = oacp_status; - if (oacp_proc.type == BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC) { + /* The Checksum Value field is only present when the procedure succeeded. */ + if (oacp_proc.type == BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC && + oacp_status == BT_GATT_OTS_OACP_RES_SUCCESS) { sys_put_le32(net_buf_simple_pull_le32(resp_param), (oacp_res + oacp_res_len)); oacp_res_len += sizeof(uint32_t); } From 9e7d607a394cb7cfa956957f378138589b52a6a5 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 22:50:19 -0700 Subject: [PATCH 071/455] Bluetooth: OTS: Decode Checksum only if it is supported oacp_command_decode() accepted the op code unconditionally while oacp_proc_validate() handles it under a Kconfig guard. Both answer Op Code Not Supported, so guard the decode arm to match, the way Create, Delete and Write already are. Signed-off-by: Flavio Ceolin --- subsys/bluetooth/services/ots/ots_oacp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subsys/bluetooth/services/ots/ots_oacp.c b/subsys/bluetooth/services/ots/ots_oacp.c index 04f222456653..e760fef86104 100644 --- a/subsys/bluetooth/services/ots/ots_oacp.c +++ b/subsys/bluetooth/services/ots/ots_oacp.c @@ -395,6 +395,7 @@ static int oacp_command_decode(const uint8_t *buf, uint16_t len, return 0; #endif +#if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT) case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC: if (net_buf.len != BT_GATT_OTS_OACP_CS_CALC_PARAMS_SIZE) { return -EBADMSG; @@ -405,6 +406,7 @@ static int oacp_command_decode(const uint8_t *buf, uint16_t len, net_buf_simple_pull_le32(&net_buf); return 0; +#endif case BT_GATT_OTS_OACP_PROC_EXECUTE: if (net_buf.len != 0) { return -EBADMSG; From 8766f09bcd45b08a6d3b0d4269fa2a55ed30d6e2 Mon Sep 17 00:00:00 2001 From: Rijesh Augustine Date: Thu, 21 May 2026 09:46:56 -0600 Subject: [PATCH 072/455] docs: added ide for zephyr docs This PR adds information to inform users about the ide for zephyr vscode extension. Signed-off-by: Rijesh Augustine --- .../tools/ide_for_zephyr_vscode_ext.rst | 143 ++++++++++++++++++ .../img/ide-for-zephyr_main_vscode_ext.webp | Bin 0 -> 78424 bytes ...for_zephyr_workspace_setup_vscode_ext.webp | Bin 0 -> 37732 bytes doc/develop/tools/index.rst | 1 + 4 files changed, 144 insertions(+) create mode 100644 doc/develop/tools/ide_for_zephyr_vscode_ext.rst create mode 100644 doc/develop/tools/img/ide-for-zephyr_main_vscode_ext.webp create mode 100644 doc/develop/tools/img/ide_for_zephyr_workspace_setup_vscode_ext.webp diff --git a/doc/develop/tools/ide_for_zephyr_vscode_ext.rst b/doc/develop/tools/ide_for_zephyr_vscode_ext.rst new file mode 100644 index 000000000000..b470d563cba0 --- /dev/null +++ b/doc/develop/tools/ide_for_zephyr_vscode_ext.rst @@ -0,0 +1,143 @@ +.. _ide_for_zephyr_vscode_ext: + +IDE for Zephyr (VS Code extension) +################################## + +`IDE for Zephyr`_ is a Visual Studio Code (VS Code) extension for Zephyr RTOS development. +It supports **host tool management**, **west workspace setup**, **SDK management**, **project +creation**, **build/flash**, and **debugging**. + +.. figure:: img/ide-for-zephyr_main_vscode_ext.webp + :align: center + :alt: IDE for Zephyr main page showing UI with memory report + +Key features +************ + +- Integrate with Cortex-Debug for ST-Link, J-Link, OpenOCD, Black Magic Probe, and other + probes via the built-in ``zephyr-ide-cortex`` and ``zephyr-ide-west`` debugger types that + auto-resolve ELF, GDB, and runner paths +- Integrate with clangd or C/C++ for IntelliSense +- Explore memory usage, Kconfig, and devicetree from the Build Dashboard; view ROM and RAM + as a sunburst chart without leaving VS Code +- Edit Kconfig options interactively with the built-in editor +- Add, run, and reconfigure Twister tests from the project panel +- Install native host tools (CMake, Python 3, Ninja, DTC, GCC, etc.) automatically on + Linux, macOS, and Windows +- Install and manage Zephyr SDK versions and per-architecture toolchains +- Add projects from existing applications or Zephyr samples, with multiple builds and + per-build board and configuration overrides +- Store project configuration in a version-controllable :file:`.vscode/zephyr-ide.json`, which + can specify SDKs, packages, and blobs + +Compatibility +************* + +- Windows +- Linux +- macOS + +Getting started +*************** + +#. Install the extension + + Install IDE for Zephyr from the `VS Code Marketplace`_ or the `Open VSX Registry`_. + + An extension pack bundling Cortex-Debug, C/C++, Serial Monitor, Devicetree LSP, and + CMake support is also available on the `VS Code Marketplace (Extension Pack)`_ and + the `Open VSX Registry (Extension Pack)`_. + +#. Open the Overview Page and Install Host Tools + + Click the :guilabel:`Host Tools` card. The extension verifies that the required build + dependencies (CMake, Python 3, Ninja, DTC, GCC, etc.) are on the PATH and can install any + missing tools automatically. + + +#. Configure a West workspace + + Click the :guilabel:`Workspace` card and choose a setup method: + + - **IDE for Zephyr Workspace from Git** — clone a repository that already contains a + pre-configured IDE for Zephyr workspace. + - **West Workspace from Git** — clone an existing west-based Zephyr repository. + - **Standard Workspace** — create a fresh workspace with a Python virtual environment, + west installation, and Zephyr repository initialization. + - **Open Current Directory** — adopt an existing :file:`.west` folder or link to an + external Zephyr installation via :envvar:`ZEPHYR_BASE`. + + This step also prompts you to install a Zephyr SDK if needed. You can manage SDKs later + from the :guilabel:`Zephyr SDK` card on the Overview page. + + .. figure:: img/ide_for_zephyr_workspace_setup_vscode_ext.webp + :align: center + :alt: Workspace setup options in IDE for Zephyr + +#. Add a project and a build + + In the Project panel, click :guilabel:`Add Project` to add an existing application + or copy a Zephyr sample as a starting point. + + After adding a project, click :guilabel:`Add Build` to create a build configuration. + Select the target board and, optionally, a runner profile. Each project can have + multiple builds targeting different boards or configurations. + +#. Build and flash the application + + Use the status bar buttons or the :guilabel:`Project Build` panel to build, flash, or + run a pristine build. Build output is shown in the integrated terminal. + +#. Configure and run a debug session + + IDE for Zephyr ships a built-in ``zephyr-ide-west`` debugger type that reads + :file:`runners.yaml` from the active build and translates it into a Cortex-Debug + session automatically. No :file:`.vscode/launch.json` entry is required to get + started. The ``zephyr-ide-west`` provider accepts the arguments you would pass to + ``west debugserver``, while the ``zephyr-ide-cortex`` provider accepts the arguments you + would pass to ``cortex-debug`` directly. You can also set up your own launch + configuration and bind it to a build. The extension provides commands that can be + resolved on launch. + + To add a launch configuration manually, use the minimal form: + + .. code-block:: json + + { + "name": "Zephyr IDE: Debug", + "type": "zephyr-ide-west", + "request": "launch" + } + + The built-in provider selects the runner from :file:`runners.yaml`, resolves the ELF + and GDB paths, and forwards the session to Cortex-Debug. + +Sharing project configuration +***************************** + +Project settings, builds, runner profiles, Kconfig overlays, devicetree overlays, and +per-build west and CMake arguments are stored in :file:`.vscode/zephyr-ide.json`. The +file is human-readable and can be committed to version control so team members share the +same workspace configuration. + +Useful links +************ + +- Explore the `Extension repository`_ +- Read the `Full documentation`_ +- Try the `Sample project`_ + +.. _IDE for Zephyr: + https://marketplace.visualstudio.com/items?itemName=mylonics.zephyr-ide +.. _VS Code Marketplace: + https://marketplace.visualstudio.com/items?itemName=mylonics.zephyr-ide +.. _Open VSX Registry: + https://open-vsx.org/extension/mylonics/zephyr-ide +.. _VS Code Marketplace (Extension Pack): + https://marketplace.visualstudio.com/items?itemName=mylonics.zephyr-ide-extension-pack +.. _Open VSX Registry (Extension Pack): + https://open-vsx.org/extension/mylonics/zephyr-ide-extension-pack +.. _Extension repository: https://github.com/mylonics/zephyr-ide +.. _Full documentation: https://zephyr-ide.mylonics.com/ +.. _Getting started video: https://www.youtube.com/watch?v=Asfolnh9kqM +.. _Sample project: https://github.com/mylonics/zephyr-ide-sample-project diff --git a/doc/develop/tools/img/ide-for-zephyr_main_vscode_ext.webp b/doc/develop/tools/img/ide-for-zephyr_main_vscode_ext.webp new file mode 100644 index 0000000000000000000000000000000000000000..4c1d2ff94d92e6bb58d563a1387e39075fe0810d GIT binary patch literal 78424 zcmaHyQVIm>7O8_L4|<7K8U zLGwVSocoN6f<(va9CP8)GH;_7Ki$K*>c#2N-|%+&4CCviIK8}HdfV#r!jBiwa^?m& z0sI|9?a%e92z&^D@5O+7K0|>^Z=e9c^UImT?aV1)zkehkPQX@~m+vavAW}1LjTfnEwgb^iKWkze#@#m;jUjzAijI1#SW^KI6ZBywrd6 z9{`qog1;TU5S|`h004$bkBNY4fYSHN^T}w8EI+mUlBU z;2qEmNdIWPs(&PS&hH2SHkcLY`=0*x_zd{y`~#FL>CnJAMPT1KPf3mby}Z)2 zmDb8Ax&;c}eNk?xQSwmsP4f5+=S~F4&gxf@N7OstMQz{}fjEt%9ciF2R8ut}-q{>u z$9YRSO^P+q^(&xk_F#Q3N&^E)nI)yx8i>Tc5_~=-YuPKhTe2rzsmy=@J4fjo_I_1SOHsx?<^rq# zpil~k40ci54K-WNg@h3V@KCD0zBI0SPl-(KaP#;Rcq1}v?ZGZQNJTi(7-D9c?zd*?8;MNLMokH8X1nqj zmOt;j1A67+fJep60<@@8P(x*kJN;Hzt(rzJ3i;;}HD_)f$~Ew?7tEeuzgW_RKBf(34D~t8A@6JdY-ow-73^_3oaL{+qkNcT{I@0Iu4;Eprz! zS5G8~K$(-1%WA0ws;u@NwPM|^4$ko4h#ne#Tg5@1WwX=s?S4y?k@?T(PLtH!7O>aJ zAIF2M1%4inn(-je+#OMf_|AuNhc{c424s1Uog9bvZl({s6tcgP5C#GIaBG>`Ic6kM z@edE-1{r$Rs~&Y0SJCjLrtRKSGTVWUp`9?- zDJAx}_YNggN8bc0?6)@GdVt$g9b{28c(WL-+nzO=VG0?9mKS!mRcpUmN)6*?A?`a$rKnTQ&2mg7 z9lWJAGj+!xg>4gU;MlXvJ?_VL6;h$JJHmUj+t^nK!BBH>SUi>b7RIIGD!;Ypc_f5> z+8OM_>P|ZJ1=Gcs9@|b+NM!)^dLqBjw_zhzEK*c4M4U4DQ{nt3Of3Ch?_~-` zRo#b%u6J9}^q=SaN_Nu6aZ}+mnR>*VTVd+t$*}^(by3;UQt~$*5#SK@6JYgEj%n<$ zdWDxf*y`q1Qk6m05Q|4xxJxlwtS>jh;dyDafD>v*lQ=9Vx`;CMJKOE!_CKavP#$J+ z`t!!w)5#km!t-f-9M+snntl;wwl2%E4kmDYxl{>q3%6PE-LAPRy>;R8FfZg?IUI7k z6#eRvY3-63AMeZ6G+-xY&-GiSnZx3r%yWw2u2ktH(STBDcJ?eE9%13fC(69NgPn

M3@>OK&Y$#D!?tOeWT_&!2(H$Cxxh)pxOJ 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 literal 0 HcmV?d00001 diff --git a/doc/develop/tools/img/ide_for_zephyr_workspace_setup_vscode_ext.webp b/doc/develop/tools/img/ide_for_zephyr_workspace_setup_vscode_ext.webp new file mode 100644 index 0000000000000000000000000000000000000000..dfbc192be7731fe9f4000c47365660c8287d4dc1 GIT binary patch literal 37732 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 QDu4h0 Date: Fri, 17 Jul 2026 18:55:44 +0200 Subject: [PATCH 073/455] arch: arm64: implement icache invalidate ops These were ENOTSUP stubs, so freshly written code could run stale lines. Signed-off-by: Jonathan E. Peace --- include/zephyr/arch/arm64/cache.h | 60 ++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/include/zephyr/arch/arm64/cache.h b/include/zephyr/arch/arm64/cache.h index 5e174c2c7861..38830fda10ed 100644 --- a/include/zephyr/arch/arm64/cache.h +++ b/include/zephyr/arch/arm64/cache.h @@ -278,11 +278,37 @@ static ALWAYS_INLINE void arch_dcache_disable(void) #if defined(CONFIG_ICACHE) +#define CTR_EL0_IMINLINE_MASK BIT_MASK(4) + +#define ic_va_ops(op, val) \ +({ \ + __asm__ volatile ("ic " op ", %0" :: "r" (val) : "memory"); \ +}) + +static size_t icache_line_size; + static ALWAYS_INLINE size_t arch_icache_line_size_get(void) { - return -ENOTSUP; + uint64_t ctr_el0; + uint32_t iminline; + + if (icache_line_size) { + return icache_line_size; + } + + ctr_el0 = read_sysreg(CTR_EL0); + + iminline = ctr_el0 & CTR_EL0_IMINLINE_MASK; + + icache_line_size = 4 << iminline; + + return icache_line_size; } +/* An I-cache holds no dirty data: "flush" is meaningless, so only the + * invalidate operations exist. Callers modifying code must clean the + * D-side to the point of unification before invalidating here. + */ static ALWAYS_INLINE int arch_icache_flush_all(void) { return -ENOTSUP; @@ -290,12 +316,16 @@ static ALWAYS_INLINE int arch_icache_flush_all(void) static ALWAYS_INLINE int arch_icache_invd_all(void) { - return -ENOTSUP; + __asm__ volatile ("ic ialluis" ::: "memory"); + barrier_dsync_fence_full(); + barrier_isync_fence_full(); + + return 0; } static ALWAYS_INLINE int arch_icache_flush_and_invd_all(void) { - return -ENOTSUP; + return arch_icache_invd_all(); } static ALWAYS_INLINE int arch_icache_flush_range(void *addr, size_t size) @@ -307,16 +337,28 @@ static ALWAYS_INLINE int arch_icache_flush_range(void *addr, size_t size) static ALWAYS_INLINE int arch_icache_invd_range(void *addr, size_t size) { - ARG_UNUSED(addr); - ARG_UNUSED(size); - return -ENOTSUP; + size_t line_size; + uintptr_t start_addr = (uintptr_t)addr; + uintptr_t end_addr = start_addr + size; + + line_size = arch_icache_line_size_get(); + + start_addr &= ~(line_size - 1); + + while (start_addr < end_addr) { + ic_va_ops("ivau", start_addr); + start_addr += line_size; + } + + barrier_dsync_fence_full(); + barrier_isync_fence_full(); + + return 0; } static ALWAYS_INLINE int arch_icache_flush_and_invd_range(void *addr, size_t size) { - ARG_UNUSED(addr); - ARG_UNUSED(size); - return -ENOTSUP; + return arch_icache_invd_range(addr, size); } static ALWAYS_INLINE void arch_icache_enable(void) From 78fff367ce318a6969be8bc8a62425e993a8c927 Mon Sep 17 00:00:00 2001 From: "Jonathan E. Peace" Date: Fri, 17 Jul 2026 18:55:44 +0200 Subject: [PATCH 074/455] llext: clean dcache before icache invalidate on exec regions Invalidating before the clean lets the ifetch refill stale data. Signed-off-by: Jonathan E. Peace --- subsys/llext/llext_mem.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/subsys/llext/llext_mem.c b/subsys/llext/llext_mem.c index eecc87a3b281..4cfdcd4324a7 100644 --- a/subsys/llext/llext_mem.c +++ b/subsys/llext/llext_mem.c @@ -328,7 +328,6 @@ void llext_adjust_mmu_permissions(struct llext *ext) #ifdef CONFIG_LLEXT_VENEERS case LLEXT_MEM_VENEER: #endif - sys_cache_instr_invd_range(addr, size); flags = K_MEM_PERM_EXEC; break; case LLEXT_MEM_DATA: @@ -342,6 +341,12 @@ void llext_adjust_mmu_permissions(struct llext *ext) continue; } sys_cache_data_flush_range(addr, size); + if ((flags & K_MEM_PERM_EXEC) != 0) { + /* new code must reach PoU (flush above) before the + * stale instruction lines are dropped + */ + sys_cache_instr_invd_range(addr, size); + } k_mem_update_flags(addr, size, flags); } From d4ce3e76c3742c8d833f952ecb3a8f4cf6604f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 11:52:05 +0200 Subject: [PATCH 075/455] cmake: extensions: write linker snippet files only when they change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zephyr_linker_sources() re-read and rewrote a snippet file on every call. Rewriting a file updates its mtime even when the content is identical, which dirties every ninja edge depending on it: all 14 snippets-*.ld files were rewritten on every re-configure with byte-identical content. Accumulate the lines in global properties instead and write each file once, from a flush deferred to the end of the configure stage. file(GENERATE) leaves a file untouched when its content is unchanged, so a re-configure no longer perturbs the build graph. The dirtied edges are otherwise masked by the syscalls trigger file being touched at configure time. With that fixed as well, the rebuild after a no-change re-configure drops from 0.44 s to 0.04 s for hello_world and from 0.69 s to 0.05 s for echo_server. Calling zephyr_linker_sources() after the flush cannot take effect and is now a fatal error rather than a silently dropped snippet. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- cmake/modules/extensions.cmake | 77 ++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index 2b08e5a0b646..a581da4c374c 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -1374,24 +1374,36 @@ function(zephyr_linker_sources location) set(itcm_path "${snippet_base}/snippets-itcm-section.ld") set(dtcm_path "${snippet_base}/snippets-dtcm-section.ld") - # Clear destination files if this is the first time the function is called. - get_property(cleared GLOBAL PROPERTY snippet_files_cleared) - if(NOT DEFINED cleared) - file(WRITE ${sections_path} "") - file(WRITE ${rom_sections_path} "") - file(WRITE ${ram_sections_path} "") - file(WRITE ${data_sections_path} "") - file(WRITE ${text_sections_path} "") - file(WRITE ${rom_start_path} "") - file(WRITE ${bss_path} "") - file(WRITE ${noinit_path} "") - file(WRITE ${rwdata_path} "") - file(WRITE ${rodata_path} "") - file(WRITE ${ramfunc_path} "") - file(WRITE ${nocache_path} "") - file(WRITE ${itcm_path} "") - file(WRITE ${dtcm_path} "") - set_property(GLOBAL PROPERTY snippet_files_cleared true) + # The destination files are only written by zephyr_linker_sources_flush(), so + # calling this function after the flush would silently drop the snippet. + get_property(flushed GLOBAL PROPERTY snippet_files_flushed) + if(flushed) + message(FATAL_ERROR "zephyr_linker_sources() called after the linker " + "snippet files have been written.") + endif() + + # Register the destination files if this is the first time the function is + # called, and schedule the flush for the end of the configure stage. + get_property(registered GLOBAL PROPERTY snippet_files) + if(NOT DEFINED registered) + set_property(GLOBAL PROPERTY snippet_files + ${sections_path} + ${rom_sections_path} + ${ram_sections_path} + ${data_sections_path} + ${text_sections_path} + ${rom_start_path} + ${bss_path} + ${noinit_path} + ${rwdata_path} + ${rodata_path} + ${ramfunc_path} + ${nocache_path} + ${itcm_path} + ${dtcm_path} + ) + cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} + CALL zephyr_linker_sources_flush) endif() # Choose destination file, based on the argument. @@ -1466,22 +1478,33 @@ function(zephyr_linker_sources location) # Remove line from other snippet file, if already used get_property(old_path GLOBAL PROPERTY "snippet_files_used_${relpath}") if(DEFINED old_path) - file(STRINGS ${old_path} lines) + get_property(lines GLOBAL PROPERTY "snippet_lines_${old_path}") list(FILTER lines EXCLUDE REGEX ${relpath}) - string(REPLACE ";" "\n;" lines "${lines}") # Add newline to each line. - file(WRITE ${old_path} ${lines} "\n") + set_property(GLOBAL PROPERTY "snippet_lines_${old_path}" ${lines}) endif() set_property(GLOBAL PROPERTY "snippet_files_used_${relpath}" ${snippet_path}) - # Add new line to existing lines, sort them, and write them back. - file(STRINGS ${snippet_path} lines) # Get current lines (without newlines). - list(APPEND lines ${include_str}) - list(SORT lines) - string(REPLACE ";" "\n;" lines "${lines}") # Add newline to each line. - file(WRITE ${snippet_path} ${lines} "\n") + set_property(GLOBAL APPEND PROPERTY "snippet_lines_${snippet_path}" "${include_str}") endforeach() endfunction(zephyr_linker_sources) +# Write the linker snippet files gathered by zephyr_linker_sources(). +# +# This is deferred to the end of the configure stage so that each file is +# written exactly once. file(GENERATE) then leaves an unchanged file untouched, +# which keeps a re-configuration from dirtying everything that #includes it. +function(zephyr_linker_sources_flush) + get_property(snippet_files GLOBAL PROPERTY snippet_files) + foreach(snippet_path IN LISTS snippet_files) + get_property(lines GLOBAL PROPERTY "snippet_lines_${snippet_path}") + list(SORT lines) + list(TRANSFORM lines APPEND "\n") + list(JOIN lines "" content) + file(GENERATE OUTPUT ${snippet_path} CONTENT "${content}") + endforeach() + set_property(GLOBAL PROPERTY snippet_files_flushed true) +endfunction(zephyr_linker_sources_flush) + # Helper macro for conditionally calling zephyr_code_relocate() when a # specific Kconfig symbol is enabled. See zephyr_code_relocate() description # for supported arguments. From 397cb0478c4ce34b504181b3bd08dba1cfc56eda Mon Sep 17 00:00:00 2001 From: Filip Kokosinski Date: Thu, 20 Aug 2026 13:16:15 +0200 Subject: [PATCH 076/455] soc/cdns: discard executable stack information sections This commit adds missing DISCARDs for the `.note.GNU-stack` section, which carries information about whether the ELF requires an executable stack. Since Zephyr largely operates without an ELF loader, this section can be safely discarded, and already is for the vast majority of platforms: % grep -R ".note.GNU-stack" * | wc -l 51 Signed-off-by: Filip Kokosinski --- soc/cdns/dc233c/include/xtensa-dc233c.ld | 2 ++ .../sample_controller32/include/xtensa-sample-controller32.ld | 2 ++ 2 files changed, 4 insertions(+) diff --git a/soc/cdns/dc233c/include/xtensa-dc233c.ld b/soc/cdns/dc233c/include/xtensa-dc233c.ld index 5acfdacdc7dc..d42bb0203cb8 100644 --- a/soc/cdns/dc233c/include/xtensa-dc233c.ld +++ b/soc/cdns/dc233c/include/xtensa-dc233c.ld @@ -369,4 +369,6 @@ SECTIONS { KEEP (*(.debug.xt.callgraph .debug.xt.callgraph.* .gnu.linkonce.xt.callgraph.*)) } + + /DISCARD/ : { *(.note.GNU-stack) } } diff --git a/soc/cdns/sample_controller32/include/xtensa-sample-controller32.ld b/soc/cdns/sample_controller32/include/xtensa-sample-controller32.ld index 4794c5fb3f99..ed4d778ef428 100644 --- a/soc/cdns/sample_controller32/include/xtensa-sample-controller32.ld +++ b/soc/cdns/sample_controller32/include/xtensa-sample-controller32.ld @@ -585,4 +585,6 @@ SECTIONS { KEEP (*(.debug.xt.callgraph .debug.xt.callgraph.* .gnu.linkonce.xt.callgraph.*)) } + + /DISCARD/ : { *(.note.GNU-stack) } } From 08e13d28c3aed050b9742ea28d5d573f260c13a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Fri, 21 Aug 2026 11:14:36 +0200 Subject: [PATCH 077/455] MAINTAINERS.yml: make devicetree maintained again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the devicetree maintained again, by stepping up as maintainer. Signed-off-by: Fin Maaß --- MAINTAINERS.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 3cfedb0056d4..c176b099c6c8 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -1358,12 +1358,13 @@ Device Driver Model: - init Devicetree: - status: odd fixes + status: maintained + maintainers: + - maass-hamburg collaborators: - mbolivar - rruuaanng - kylebonnici - - maass-hamburg files-regex: - ^dts/bindings/.*zephyr.* - ^dts/bindings/[^,]+$ From 056fd5aa54ded6036e432f7d0f395b194afac8fd Mon Sep 17 00:00:00 2001 From: Bartosz Meus Date: Wed, 18 Mar 2026 11:59:17 +0100 Subject: [PATCH 078/455] drivers: sensor: bmi270: add FIFO streaming via sensor_stream() Add sensor_stream()/RTIO support so the BMI270 collects accelerometer and gyroscope samples in its hardware FIFO and raises an interrupt only at the watermark threshold. The host SoC stays in sleep between batches instead of waking per sample, reducing power consumption proportionally to the batch size. Implementation adds three new files: - bmi270_stream.c: FIFO configuration, watermark/full trigger mapping, INT pin management, and optional poll fallback - bmi270_decoder.c/h: sensor_decoder_api converting raw FIFO batches (header and headerless modes) to Q31 Trigger handling in bmi270_trigger.c is extended to route FIFO interrupts to a dedicated work queue with permanent-latched mode for reliable GPIO edge detection. New Kconfig options control streaming (BMI270_STREAM), FIFO mode (BMI270_FIFO_HEADERLESS), watermark depth, INT pin routing (BMI270_FIFO_ON_INT2), poll fallback, and buffer sizing. Additional Kconfig BMI270_LOW_POWER_MODE forces power-optimized filter at all ODRs, trading noise for lower current draw. Also fixed in existing code: - BMI270_CMD_FIFO_FLUSH typo. - write_config_file() return type narrowed to int8_t; now int. - Init path lacked LOG_ERR on most failure branches. Signed-off-by: Bartosz Meus --- drivers/sensor/bosch/bmi270/CMakeLists.txt | 2 + drivers/sensor/bosch/bmi270/Kconfig | 96 ++++ drivers/sensor/bosch/bmi270/bmi270.c | 110 +++-- drivers/sensor/bosch/bmi270/bmi270.h | 63 ++- drivers/sensor/bosch/bmi270/bmi270_decoder.c | 384 +++++++++++++++ drivers/sensor/bosch/bmi270/bmi270_decoder.h | 34 ++ drivers/sensor/bosch/bmi270/bmi270_spi.c | 6 +- drivers/sensor/bosch/bmi270/bmi270_stream.c | 483 +++++++++++++++++++ drivers/sensor/bosch/bmi270/bmi270_trigger.c | 147 +++++- 9 files changed, 1269 insertions(+), 56 deletions(-) create mode 100644 drivers/sensor/bosch/bmi270/bmi270_decoder.c create mode 100644 drivers/sensor/bosch/bmi270/bmi270_decoder.h create mode 100644 drivers/sensor/bosch/bmi270/bmi270_stream.c diff --git a/drivers/sensor/bosch/bmi270/CMakeLists.txt b/drivers/sensor/bosch/bmi270/CMakeLists.txt index b6d64f0dc5ab..8a2db8d136a9 100644 --- a/drivers/sensor/bosch/bmi270/CMakeLists.txt +++ b/drivers/sensor/bosch/bmi270/CMakeLists.txt @@ -8,6 +8,8 @@ zephyr_library() zephyr_library_sources(bmi270.c) +zephyr_library_sources(bmi270_decoder.c) zephyr_library_sources_ifdef(CONFIG_BMI270_BUS_I2C bmi270_i2c.c) zephyr_library_sources_ifdef(CONFIG_BMI270_BUS_SPI bmi270_spi.c) zephyr_library_sources_ifdef(CONFIG_BMI270_TRIGGER bmi270_trigger.c) +zephyr_library_sources_ifdef(CONFIG_BMI270_STREAM bmi270_stream.c) diff --git a/drivers/sensor/bosch/bmi270/Kconfig b/drivers/sensor/bosch/bmi270/Kconfig index b75d69e61916..19383c367616 100644 --- a/drivers/sensor/bosch/bmi270/Kconfig +++ b/drivers/sensor/bosch/bmi270/Kconfig @@ -64,4 +64,100 @@ config BMI270_THREAD_STACK_SIZE help Stack size of thread used by the driver to handle interrupts. +config BMI270_LOW_POWER_MODE + bool "Force low-power (power-optimized) filter mode at all ODRs" + default n + help + When set, the accelerometer and gyroscope always use the + power-optimized filter mode (acc_filter_perf=0, gyr_filter_perf=0, + gyr_noise_perf=0) regardless of ODR. By default the driver switches + to performance mode at ODR >= 100 Hz. Enabling this saves power at + the cost of higher noise (no digital filtering / OSR). + +config BMI270_STREAM + bool "FIFO streaming via sensor_stream()" + depends on BMI270_TRIGGER + depends on $(dt_compat_any_has_prop,$(DT_COMPAT_BOSCH_BMI270),irq-gpios) + select SENSOR_ASYNC_API + help + Enable FIFO watermark/full streaming using the standard Zephyr + sensor_stream() API and SENSOR_TRIG_FIFO_WATERMARK trigger. + +config BMI270_FIFO_ON_INT2 + bool "Route FIFO watermark/full interrupt to INT2 instead of INT1" + depends on BMI270_STREAM + default n + help + By default, FIFO watermark and full interrupts are mapped to INT1 + and the single irq-gpios pin is assigned to INT1. Enable this only + if your board routes the FIFO interrupt to the sensor's INT2 pin. + +config BMI270_FIFO_POLL_FALLBACK + bool "Poll-based FIFO fallback alongside GPIO interrupt" + depends on BMI270_STREAM + default n + help + When set, a periodic poll checks the FIFO fill level via SPI/I2C + and triggers a read when the watermark is reached, as a fallback + for hardware where the GPIO interrupt line is not connected. + The poll period is set by BMI270_FIFO_POLL_PERIOD_MS. + +config BMI270_FIFO_POLL_PERIOD_MS + int "FIFO poll fallback period (ms)" + depends on BMI270_FIFO_POLL_FALLBACK + default 100 + range 10 1000 + help + How often (in milliseconds) the poll fallback checks the FIFO fill + level. Lower values reduce latency but increase SPI/I2C bus traffic. + +config BMI270_FIFO_WORKQ_STACK_SIZE + int "Stack size for FIFO handler work queue thread" + depends on BMI270_STREAM + default 4096 + help + The FIFO watermark handler runs in a dedicated work queue thread to avoid + stack overflow in the trigger thread. Increase if you see crashes when + the interrupt fires. + +config BMI270_FIFO_HEADERLESS + bool "Use FIFO headerless mode (no per-frame headers)" + depends on BMI270_STREAM + default n + help + When set, FIFO is configured with FIFO_CONFIG_1.fifo_header_en=0. + Requires identical ODR for accelerometer and gyroscope. + Only regular frames are stored (no skip/sensortime/config); + watermark = WATERMARK_SAMPLES*12 gives exactly that many samples per interrupt. + +config BMI270_FIFO_WATERMARK_SAMPLES + int "FIFO watermark in number of samples (acc+gyr frames)" + depends on BMI270_STREAM + default 10 + help + Desired number of accelerometer and gyroscope data samples per watermark interrupt. + In headerless mode the byte watermark is SAMPLES*12 (exact). + +config BMI270_FIFO_WATERMARK_CONTROL_MARGIN + int "Extra bytes added to FIFO byte-watermark for control frames" + depends on BMI270_STREAM && !BMI270_FIFO_HEADERLESS + default 0 + help + In header mode the FIFO may insert control/skip frames between data + frames. This margin is added to (WATERMARK_SAMPLES * frame_bytes) when + programming the hardware watermark register, so the interrupt fires + after at least WATERMARK_SAMPLES data frames. Set to 0 if you do not + need extra headroom for control frames. + +config BMI270_FIFO_STREAM_BLOCK_SIZE + int "Stream RTIO mempool block size (bytes)" + depends on BMI270_STREAM + default 384 + help + Must match the block size used in RTIO_DEFINE_WITH_MEMPOOL for the + sensor stream. Each FIFO read is capped to fit in one block (minus + encoded header). Size needed: WATERMARK_SAMPLES*13 + CONTROL_MARGIN + + ~32 bytes encoded header. Default 384 supports 10 samples in + header mode (10*13+40+32=202) with headroom. + endif # BMI270 diff --git a/drivers/sensor/bosch/bmi270/bmi270.c b/drivers/sensor/bosch/bmi270/bmi270.c index c83e9b345ba0..1b518d0e79bc 100644 --- a/drivers/sensor/bosch/bmi270/bmi270.c +++ b/drivers/sensor/bosch/bmi270/bmi270.c @@ -18,6 +18,7 @@ #include "bmi270.h" #include "bmi270_config_file.h" +#include "bmi270_decoder.h" LOG_MODULE_REGISTER(bmi270, CONFIG_SENSOR_LOG_LEVEL); @@ -65,6 +66,22 @@ int bmi270_reg_write_with_delay(const struct device *dev, return ret; } +int bmi270_adv_power_save_enable(const struct device *dev) +{ + uint8_t pwr = BMI270_PWR_CONF_ADV_PWR_SAVE_EN; + + return bmi270_reg_write_with_delay(dev, BMI270_REG_PWR_CONF, &pwr, 1, + BMI270_TRANSC_DELAY_SUSPEND); +} + +int bmi270_adv_power_save_disable(const struct device *dev) +{ + uint8_t pwr = BMI270_PWR_CONF_ADV_PWR_SAVE_DIS; + + return bmi270_reg_write_with_delay(dev, BMI270_REG_PWR_CONF, &pwr, 1, + BMI270_TRANSC_DELAY_SUSPEND); +} + static void channel_accel_convert(struct sensor_value *val, int64_t raw_val, uint8_t range) { @@ -154,23 +171,20 @@ static int set_accel_odr_osr(const struct device *dev, const struct sensor_value pwr_ctrl &= ~BMI270_PWR_CTRL_ACC_EN; } - /* If the Sampling frequency (odr) >= 100Hz, enter performance - * mode else, power optimized. This also has a consequence - * for the OSR - */ - if (odr_bits >= BMI270_ACC_ODR_100_HZ) { - acc_conf = BMI270_SET_BITS(acc_conf, BMI270_ACC_FILT, - BMI270_ACC_FILT_PERF_OPT); + if (IS_ENABLED(CONFIG_BMI270_LOW_POWER_MODE) || odr_bits < BMI270_ACC_ODR_100_HZ) { + acc_conf = + BMI270_SET_BITS(acc_conf, BMI270_ACC_FILT, BMI270_ACC_FILT_PWR_OPT); } else { acc_conf = BMI270_SET_BITS(acc_conf, BMI270_ACC_FILT, - BMI270_ACC_FILT_PWR_OPT); + BMI270_ACC_FILT_PERF_OPT); } data->acc_odr = odr_bits; } if (osr) { - if (data->acc_odr >= BMI270_ACC_ODR_100_HZ) { + if (!IS_ENABLED(CONFIG_BMI270_LOW_POWER_MODE) && + data->acc_odr >= BMI270_ACC_ODR_100_HZ) { /* Performance mode */ /* osr->val2 should be unused */ switch (osr->val1) { @@ -342,24 +356,16 @@ static int set_gyro_odr_osr(const struct device *dev, const struct sensor_value pwr_ctrl &= ~BMI270_PWR_CTRL_GYR_EN; } - /* If the Sampling frequency (odr) >= 100Hz, enter performance - * mode else, power optimized. This also has a consequence for - * the OSR - */ - if (odr_bits >= BMI270_GYR_ODR_100_HZ) { - gyr_conf = BMI270_SET_BITS(gyr_conf, - BMI270_GYR_FILT, + if (IS_ENABLED(CONFIG_BMI270_LOW_POWER_MODE) || odr_bits < BMI270_GYR_ODR_100_HZ) { + gyr_conf = + BMI270_SET_BITS(gyr_conf, BMI270_GYR_FILT, BMI270_GYR_FILT_PWR_OPT); + gyr_conf = BMI270_SET_BITS(gyr_conf, BMI270_GYR_FILT_NOISE, + BMI270_GYR_FILT_NOISE_PWR); + } else { + gyr_conf = BMI270_SET_BITS(gyr_conf, BMI270_GYR_FILT, BMI270_GYR_FILT_PERF_OPT); - gyr_conf = BMI270_SET_BITS(gyr_conf, - BMI270_GYR_FILT_NOISE, + gyr_conf = BMI270_SET_BITS(gyr_conf, BMI270_GYR_FILT_NOISE, BMI270_GYR_FILT_NOISE_PERF); - } else { - gyr_conf = BMI270_SET_BITS(gyr_conf, - BMI270_GYR_FILT, - BMI270_GYR_FILT_PWR_OPT); - gyr_conf = BMI270_SET_BITS(gyr_conf, - BMI270_GYR_FILT_NOISE, - BMI270_GYR_FILT_NOISE_PWR); } data->gyr_odr = odr_bits; @@ -445,10 +451,10 @@ static int set_gyro_range(const struct device *dev, const struct sensor_value *r return ret; } -static int8_t write_config_file(const struct device *dev) +static int write_config_file(const struct device *dev) { const struct bmi270_config *cfg = dev->config; - int8_t ret = 0; + int ret = 0; uint16_t index = 0; uint8_t addr_array[2] = { 0 }; @@ -672,7 +678,7 @@ static int bmi270_init(const struct device *dev) ret = bmi270_bus_check(dev); if (ret < 0) { - LOG_ERR("Could not initialize bus"); + LOG_ERR("Bus check failed: %d", ret); return ret; } @@ -690,24 +696,25 @@ static int bmi270_init(const struct device *dev) ret = bmi270_bus_init(dev); if (ret != 0) { - LOG_ERR("Could not initiate bus communication"); + LOG_ERR("Bus init failed: %d", ret); return ret; } ret = bmi270_reg_read(dev, BMI270_REG_CHIP_ID, &chip_id, 1); if (ret != 0) { + LOG_ERR("CHIP_ID read failed: %d", ret); return ret; } if (chip_id != BMI270_CHIP_ID) { - LOG_ERR("Unexpected chip id (%x). Expected (%x)", - chip_id, BMI270_CHIP_ID); + LOG_ERR("Unexpected chip id (0x%02x). Expected (0x%02x)", chip_id, BMI270_CHIP_ID); return -EIO; } soft_reset_cmd = BMI270_CMD_SOFT_RESET; ret = bmi270_reg_write(dev, BMI270_REG_CMD, &soft_reset_cmd, 1); if (ret != 0) { + LOG_ERR("Soft reset write failed: %d", ret); return ret; } @@ -722,6 +729,7 @@ static int bmi270_init(const struct device *dev) ret = bmi270_reg_read(dev, BMI270_REG_PWR_CONF, &adv_pwr_save, 1); if (ret != 0) { + LOG_ERR("PWR_CONF read failed: %d", ret); return ret; } @@ -738,18 +746,20 @@ static int bmi270_init(const struct device *dev) init_ctrl = BMI270_PREPARE_CONFIG_LOAD; ret = bmi270_reg_write(dev, BMI270_REG_INIT_CTRL, &init_ctrl, 1); if (ret != 0) { + LOG_ERR("INIT_CTRL prepare failed: %d", ret); return ret; } ret = write_config_file(dev); - if (ret != 0) { + LOG_ERR("Config file write failed: %d", ret); return ret; } init_ctrl = BMI270_COMPLETE_CONFIG_LOAD; ret = bmi270_reg_write(dev, BMI270_REG_INIT_CTRL, &init_ctrl, 1); if (ret != 0) { + LOG_ERR("INIT_CTRL complete failed: %d", ret); return ret; } @@ -761,6 +771,7 @@ static int bmi270_init(const struct device *dev) for (tries = 0; tries <= BMI270_CONFIG_FILE_RETRIES; tries++) { ret = bmi270_reg_read(dev, BMI270_REG_INTERNAL_STATUS, &msg, 1); if (ret != 0) { + LOG_ERR("INTERNAL_STATUS read failed: %d", ret); return ret; } @@ -773,6 +784,7 @@ static int bmi270_init(const struct device *dev) } if (tries > BMI270_CONFIG_FILE_RETRIES) { + LOG_ERR("Config load timeout (INTERNAL_STATUS not INIT_OK)"); return -EIO; } @@ -809,9 +821,13 @@ static DEVICE_API(sensor, bmi270_driver_api) = { .sample_fetch = bmi270_sample_fetch, .channel_get = bmi270_channel_get, .attr_set = bmi270_attr_set, + .get_decoder = bmi270_get_decoder, #if defined(CONFIG_BMI270_TRIGGER) .trigger_set = bmi270_trigger_set, #endif +#if defined(CONFIG_BMI270_STREAM) + .submit = bmi270_submit_stream, +#endif }; static const struct bmi270_feature_config bmi270_feature_max_fifo = { @@ -834,9 +850,33 @@ static const struct bmi270_feature_config bmi270_feature_base = { &bmi270_feature_max_fifo) #if CONFIG_BMI270_TRIGGER -#define BMI270_CONFIG_INT(inst) \ - .int1 = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, irq_gpios, 0, {}),\ - .int2 = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, irq_gpios, 1, {}), +/* + * One irq-gpio: INT1 by default, INT2 when CONFIG_BMI270_FIFO_ON_INT2. + * Two irq-gpios: INT1=first, INT2=second. + * + * When FIFO_ON_INT2 is set with a single irq-gpio, INT1 is unavailable + * and SENSOR_TRIG_MOTION will return -ENOTSUP at runtime. + */ +#if defined(CONFIG_BMI270_FIFO_ON_INT2) +#define BMI270_SINGLE_IRQ_INT1(inst) \ + { \ + } +#define BMI270_SINGLE_IRQ_INT2(inst) GPIO_DT_SPEC_INST_GET_BY_IDX(inst, irq_gpios, 0) +#else +#define BMI270_SINGLE_IRQ_INT1(inst) GPIO_DT_SPEC_INST_GET_BY_IDX(inst, irq_gpios, 0) +#define BMI270_SINGLE_IRQ_INT2(inst) \ + { \ + } +#endif + +#define BMI270_CONFIG_INT_1(inst) \ + .int1 = BMI270_SINGLE_IRQ_INT1(inst), .int2 = BMI270_SINGLE_IRQ_INT2(inst), +#define BMI270_CONFIG_INT_2(inst) \ + .int1 = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, irq_gpios, 0), \ + .int2 = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, irq_gpios, 1), +#define BMI270_CONFIG_INT(inst) \ + COND_CODE_1(DT_INST_PROP_HAS_IDX(inst, irq_gpios, 1), \ + (BMI270_CONFIG_INT_2(inst)), (BMI270_CONFIG_INT_1(inst))) #else #define BMI270_CONFIG_INT(inst) #endif diff --git a/drivers/sensor/bosch/bmi270/bmi270.h b/drivers/sensor/bosch/bmi270/bmi270.h index 27b586f710bc..281659ca1dd4 100644 --- a/drivers/sensor/bosch/bmi270/bmi270.h +++ b/drivers/sensor/bosch/bmi270/bmi270.h @@ -26,6 +26,20 @@ #define BMI270_CONFIG_FILE_POLL_PERIOD_US 10000 #define BMI270_INTER_WRITE_DELAY_US 1000 +#if defined(CONFIG_BMI270_STREAM) +#include +#endif + +int bmi270_adv_power_save_enable(const struct device *dev); +int bmi270_adv_power_save_disable(const struct device *dev); + +#if defined(CONFIG_BMI270_STREAM) +void bmi270_stream_handle_fifo(const struct device *dev); +void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); +void bmi270_submit_fifo_work(const struct device *dev); +struct k_work_q *bmi270_get_fifo_work_q(void); +#endif + #define BMI270_REG_CHIP_ID 0x00 #define BMI270_REG_ERROR 0x02 #define BMI270_REG_STATUS 0x03 @@ -35,6 +49,7 @@ #define BMI270_REG_SENSORTIME_0 0x18 #define BMI270_REG_EVENT 0x1B #define BMI270_REG_INT_STATUS_0 0x1C +#define BMI270_REG_INT_STATUS_1 0x1D #define BMI270_REG_SC_OUT_0 0x1E #define BMI270_REG_WR_GEST_ACT 0x20 #define BMI270_REG_INTERNAL_STATUS 0x21 @@ -50,7 +65,9 @@ #define BMI270_REG_AUX_CONF 0x44 #define BMI270_REG_FIFO_DOWNS 0x45 #define BMI270_REG_FIFO_WTM_0 0x46 +#define BMI270_REG_FIFO_WTM_1 0x47 #define BMI270_REG_FIFO_CONFIG_0 0x48 +#define BMI270_REG_FIFO_CONFIG_1 0x49 #define BMI270_REG_SATURATION 0x4A #define BMI270_REG_AUX_DEV_ID 0x4B #define BMI270_REG_AUX_IF_CONF 0x4C @@ -112,6 +129,9 @@ #define BMI270_INT_IO_CTRL_OUTPUT_EN BIT(3) /* Output enabled */ #define BMI270_INT_IO_CTRL_INPUT_EN BIT(4) /* Input enabled */ +#define BMI270_INT_LATCH_NONE 0x00 +#define BMI270_INT_LATCH_PERMANENT 0x01 + /* Applies to INT1_MAP_FEAT, INT2_MAP_FEAT, INT_STATUS_0 */ #define BMI270_INT_MAP_SIG_MOTION BIT(0) #define BMI270_INT_MAP_STEP_COUNTER BIT(1) @@ -132,12 +152,45 @@ #define BMI270_INT_STATUS_ANY_MOTION BIT(6) +/* INT_STATUS_1 */ +#define BMI270_INT_STATUS_1_FFULL_INT BIT(0) +#define BMI270_INT_STATUS_1_FWM_INT BIT(1) +#define BMI270_INT_STATUS_1_ERR_INT BIT(2) +#define BMI270_INT_STATUS_1_ACC_DRDY_INT BIT(7) +#define BMI270_INT_STATUS_1_GYR_DRDY_INT BIT(6) +#define BMI270_INT_STATUS_1_AUX_DRDY_INT BIT(5) + +/* FIFO header mode (fh_mode<1:0>) - type of frame (regular or control)*/ +#define BMI270_FIFO_HEADER_REGULAR 0x02 +#define BMI270_FIFO_HEADER_CONTROL 0x01 + +/* FIFO header parameters (fh_parm<3:0>) - sensors in the payload or control opcode */ +#define BMI270_FIFO_FHPARM_ACC BIT(0) +#define BMI270_FIFO_FHPARM_GYR BIT(1) +#define BMI270_FIFO_FHPARM_AUX BIT(2) + +/* Regular frame payload: acc+gyr only = 6 + 6 bytes */ +#define BMI270_FIFO_FRAME_PAYLOAD_ACC_GYR_BYTES 12 +#define BMI270_FIFO_HEADER_BYTES 1 +#define BMI270_FIFO_FRAME_ACC_GYR_BYTES \ + (BMI270_FIFO_HEADER_BYTES + BMI270_FIFO_FRAME_PAYLOAD_ACC_GYR_BYTES) + +/* FIFO_CONFIG_0 (register 0x48) */ +#define BMI270_FIFO_CONFIG_0_STOP_ON_FULL_MSK BIT(0) +#define BMI270_FIFO_CONFIG_0_FIFO_TIME_EN_MSK BIT(1) + +/* FIFO_CONFIG_1 (register 0x49) */ +#define BMI270_FIFO_CONFIG_1_FIFO_HEADER_EN_MSK BIT(4) +#define BMI270_FIFO_CONFIG_1_FIFO_AUX_EN_MSK BIT(5) +#define BMI270_FIFO_CONFIG_1_FIFO_ACC_EN_MSK BIT(6) +#define BMI270_FIFO_CONFIG_1_FIFO_GYR_EN_MSK BIT(7) + #define BMI270_CHIP_ID 0x24 #define BMI270_CMD_G_TRIGGER 0x02 #define BMI270_CMD_USR_GAIN 0x03 #define BMI270_CMD_NVM_PROG 0xA0 -#define BMI270_CMD_FIFO_FLUSH OxB0 +#define BMI270_CMD_FIFO_FLUSH 0xB0 #define BMI270_CMD_SOFT_RESET 0xB6 #define BMI270_POWER_ON_TIME 500 @@ -297,6 +350,14 @@ struct bmi270_data { struct k_work trig_work; #endif #endif /* CONFIG_BMI270_TRIGGER */ + +#if defined(CONFIG_BMI270_STREAM) + struct rtio_iodev_sqe *streaming_sqe; + uint16_t fifo_watermark_bytes; + uint8_t int_status_1; + uint64_t timestamp; + struct k_work fifo_work; +#endif /* CONFIG_BMI270_STREAM */ }; struct bmi270_feature_reg { diff --git a/drivers/sensor/bosch/bmi270/bmi270_decoder.c b/drivers/sensor/bosch/bmi270/bmi270_decoder.c new file mode 100644 index 000000000000..db4d682bce14 --- /dev/null +++ b/drivers/sensor/bosch/bmi270/bmi270_decoder.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2026 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT bosch_bmi270 + +#include +#include + +#include +#include +#include +#include + +#include "bmi270_decoder.h" + +LOG_MODULE_REGISTER(bmi270_decoder, CONFIG_SENSOR_LOG_LEVEL); + +/* + * BMI270 FIFO header byte layout + * Bits [7:6] = fh_mode (10 = regular, 01 = control) + * Bits [5:2] = fh_parm (sensor presence for regular; opcode for control) + * Bits [1:0] = fh_ext (INT tag bits) + * + * fh_parm for regular frames: + * bit 0 = ACC, bit 1 = GYR, bit 2 = AUX, bit 3 = reserved + */ +#define BMI270_FIFO_HDR_MODE(h) (((h) >> 6) & 0x03) +#define BMI270_FIFO_HDR_PARM(h) (((h) >> 2) & 0x0F) +#define BMI270_FIFO_MODE_REGULAR 0x02 +#define BMI270_FIFO_MODE_CONTROL 0x01 +#define BMI270_FIFO_PARM_ACC BIT(0) +#define BMI270_FIFO_PARM_GYR BIT(1) +#define BMI270_FIFO_CTRL_PARM_SKIP_FRAME 0x00 +#define BMI270_FIFO_CTRL_PARM_SENSORTIME 0x01 +#define BMI270_FIFO_CTRL_PARM_CONFIG_CHANGE 0x02 + +#define BMI270_FIFO_SENSOR_BYTES 6 +#define BMI270_FIFO_PAYLOAD_ACC_GYR 12 +#define BMI270_FIFO_CTRL_LEN_SKIP_FRAME 2 +#define BMI270_FIFO_CTRL_LEN_SENSORTIME 4 +#define BMI270_FIFO_CTRL_LEN_CONFIG_CHANGE 5 + +#define BMI270_ACC_SHIFT_BASE 5 +#define BMI270_GYR_SHIFT_BASE 6 + +static inline uint8_t bmi270_fifo_control_frame_size(uint8_t parm) +{ + switch (parm) { + case BMI270_FIFO_CTRL_PARM_SKIP_FRAME: + return BMI270_FIFO_CTRL_LEN_SKIP_FRAME; + case BMI270_FIFO_CTRL_PARM_SENSORTIME: + return BMI270_FIFO_CTRL_LEN_SENSORTIME; + case BMI270_FIFO_CTRL_PARM_CONFIG_CHANGE: + return BMI270_FIFO_CTRL_LEN_CONFIG_CHANGE; + default: + return BMI270_FIFO_CTRL_LEN_SKIP_FRAME; + } +} + +static inline uint32_t bmi270_sample_period_ns(const struct bmi270_decoder_header *header, + enum sensor_channel chan) +{ + const uint16_t odr_hz = + (chan == SENSOR_CHAN_ACCEL_XYZ) ? header->acc_odr_hz : header->gyr_odr_hz; + + return (uint32_t)(1000000000ULL / odr_hz); +} + +/* Advance past one FIFO frame; increment *count if frame has a sample for the requested channel. */ +static const uint8_t *bmi270_fifo_advance_frame(const uint8_t *p, bool want_acc, uint16_t *count) +{ + uint8_t h = *p; + uint8_t mode = BMI270_FIFO_HDR_MODE(h); + uint8_t parm = BMI270_FIFO_HDR_PARM(h); + + if (mode == BMI270_FIFO_MODE_REGULAR) { + if (parm == 0) { + return p + 2; + } + uint8_t payload = (parm & BMI270_FIFO_PARM_ACC ? BMI270_FIFO_SENSOR_BYTES : 0) + + (parm & BMI270_FIFO_PARM_GYR ? BMI270_FIFO_SENSOR_BYTES : 0); + if (payload == 0) { + return p + 1; + } + if ((want_acc && (parm & BMI270_FIFO_PARM_ACC)) || + (!want_acc && (parm & BMI270_FIFO_PARM_GYR))) { + (*count)++; + } + return p + 1 + payload; + } + if (mode == BMI270_FIFO_MODE_CONTROL) { + return p + bmi270_fifo_control_frame_size(parm); + } + return p + 1; +} + +static int bmi270_decoder_get_frame_count(const uint8_t *buffer, struct sensor_chan_spec chan_spec, + uint16_t *frame_count) +{ + const struct bmi270_fifo_encoded_data *edata = + (const struct bmi270_fifo_encoded_data *)buffer; + + if (chan_spec.chan_idx != 0) { + return -EINVAL; + } + + if (!edata->header.is_fifo) { + return -ENODATA; + } + + if (chan_spec.chan_type != SENSOR_CHAN_ACCEL_XYZ && + chan_spec.chan_type != SENSOR_CHAN_GYRO_XYZ) { + return -ENOTSUP; + } + + if (edata->header.is_headerless) { + *frame_count = edata->fifo_byte_count / BMI270_FIFO_PAYLOAD_ACC_GYR; + return 0; + } + + uint16_t count = 0; + const uint8_t *p = edata->fifo_data; + const uint8_t *end = p + edata->fifo_byte_count; + bool want_acc = (chan_spec.chan_type == SENSOR_CHAN_ACCEL_XYZ); + + while (p < end) { + p = bmi270_fifo_advance_frame(p, want_acc, &count); + } + + *frame_count = count; + return 0; +} + +static int bmi270_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size, + size_t *frame_size) +{ + if (chan_spec.chan_idx != 0) { + return -EINVAL; + } + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_GYRO_XYZ: + *base_size = sizeof(struct sensor_three_axis_data); + *frame_size = sizeof(struct sensor_three_axis_sample_data); + return 0; + default: + return -ENOTSUP; + } +} + +/* Accel: raw -> m/s^2 in Q31 with shift. range in G (2,4,8,16) */ +static void decode_accel_frame(const uint8_t *payload, uint8_t range_g, int8_t shift, + struct sensor_three_axis_sample_data *out) +{ + int16_t x = (int16_t)sys_get_le16(&payload[0]); + int16_t y = (int16_t)sys_get_le16(&payload[2]); + int16_t z = (int16_t)sys_get_le16(&payload[4]); + + int64_t scale = (int64_t)SENSOR_G * range_g * (1LL << (31 - shift)) / INT16_MAX; + + out->timestamp_delta = 0; + out->x = (q31_t)((x * scale) / 1000000LL); + out->y = (q31_t)((y * scale) / 1000000LL); + out->z = (q31_t)((z * scale) / 1000000LL); +} + +/* Gyro: raw -> rad/s in Q31 with shift. range_dps in degrees/s */ +static void decode_gyro_frame(const uint8_t *payload, uint16_t range_dps, int8_t shift, + struct sensor_three_axis_sample_data *out) +{ + int16_t x = (int16_t)sys_get_le16(&payload[0]); + int16_t y = (int16_t)sys_get_le16(&payload[2]); + int16_t z = (int16_t)sys_get_le16(&payload[4]); + + int64_t scale = + (int64_t)range_dps * SENSOR_PI * (1LL << (31 - shift)) / (180LL * INT16_MAX); + + out->timestamp_delta = 0; + out->x = (q31_t)((x * scale) / 1000000LL); + out->y = (q31_t)((y * scale) / 1000000LL); + out->z = (q31_t)((z * scale) / 1000000LL); +} + +/* Accel range register value to G (2,4,8,16) */ +static uint8_t acc_range_reg_to_g(uint8_t reg) +{ + static const uint8_t g[] = {2, 4, 8, 16}; + + return reg < ARRAY_SIZE(g) ? g[reg] : 2; +} + +/* Gyro range register index to dps (2000,1000,500,250,125) */ +static uint16_t gyr_range_idx_to_dps(uint8_t idx) +{ + static const uint16_t dps[] = {2000, 1000, 500, 250, 125}; + + return idx < ARRAY_SIZE(dps) ? dps[idx] : 2000; +} + +/* Per-invocation decode state (one buffer, one channel) shared by FIFO decode helpers. */ +struct bmi270_fifo_decode_ctx { + struct sensor_three_axis_data *out; + uint32_t fit_base; + uint32_t sample_period_ns; + uint16_t chan_type; + uint8_t acc_g; + uint16_t gyr_dps; + int8_t acc_shift; + int8_t gyr_shift; +}; + +/* Headerless: fixed 12-byte frames, payload order GYR then ACC (same as header mode). */ +static uint16_t decode_fifo_headerless(const uint8_t *p, const uint8_t *end, uint16_t max_count, + const struct bmi270_fifo_decode_ctx *ctx) +{ + uint32_t skip = ctx->fit_base; + uint16_t decoded = 0; + + while (p + BMI270_FIFO_PAYLOAD_ACC_GYR <= end && decoded < max_count) { + if (skip > 0) { + skip--; + p += BMI270_FIFO_PAYLOAD_ACC_GYR; + continue; + } + if (ctx->chan_type == SENSOR_CHAN_ACCEL_XYZ) { + decode_accel_frame(&p[6], ctx->acc_g, ctx->acc_shift, + &ctx->out->readings[decoded]); + } else { + decode_gyro_frame(p, ctx->gyr_dps, ctx->gyr_shift, + &ctx->out->readings[decoded]); + } + ctx->out->readings[decoded].timestamp_delta = + (uint32_t)(ctx->fit_base + decoded) * ctx->sample_period_ns; + decoded++; + p += BMI270_FIFO_PAYLOAD_ACC_GYR; + } + + return decoded; +} + +/* + * One REGULAR FIFO frame at p: advance pointer; optionally decode into *decoded if it matches + * chan_type and skip count is satisfied. + */ +static const uint8_t *fifo_decode_regular_frame(const uint8_t *p, const uint8_t *end, + uint32_t *skip, uint16_t *decoded, + const struct bmi270_fifo_decode_ctx *ctx) +{ + uint8_t parm = BMI270_FIFO_HDR_PARM(*p); + + if (parm == 0) { + return p + 2; + } + + bool has_gyr = (parm & BMI270_FIFO_PARM_GYR) != 0; + bool has_acc = (parm & BMI270_FIFO_PARM_ACC) != 0; + int payload = + (has_gyr ? BMI270_FIFO_SENSOR_BYTES : 0) + (has_acc ? BMI270_FIFO_SENSOR_BYTES : 0); + + if (payload == 0 || p + 1 + payload > end) { + return p + 1; + } + + bool want_this = (ctx->chan_type == SENSOR_CHAN_ACCEL_XYZ) ? has_acc : has_gyr; + + if (!want_this) { + return p + 1 + payload; + } + if (*skip > 0) { + (*skip)--; + return p + 1 + payload; + } + + /* + * Payload order (datasheet): GYR (6B if present) then ACC (6B if present). + * ACC offset = 0 when GYR absent, 6 when GYR present. + */ + const uint8_t *frame = p + 1; + + if (ctx->chan_type == SENSOR_CHAN_ACCEL_XYZ) { + int acc_off = has_gyr ? BMI270_FIFO_SENSOR_BYTES : 0; + + decode_accel_frame(&frame[acc_off], ctx->acc_g, ctx->acc_shift, + &ctx->out->readings[*decoded]); + } else { + decode_gyro_frame(frame, ctx->gyr_dps, ctx->gyr_shift, + &ctx->out->readings[*decoded]); + } + ctx->out->readings[*decoded].timestamp_delta = + (uint32_t)(ctx->fit_base + *decoded) * ctx->sample_period_ns; + (*decoded)++; + return p + 1 + payload; +} + +static uint16_t decode_fifo_header_mode(const uint8_t *p, const uint8_t *end, uint16_t max_count, + const struct bmi270_fifo_decode_ctx *ctx) +{ + uint32_t skip = ctx->fit_base; + uint16_t decoded = 0; + + while (p < end && decoded < max_count) { + uint8_t mode = BMI270_FIFO_HDR_MODE(*p); + + if (mode == BMI270_FIFO_MODE_REGULAR) { + p = fifo_decode_regular_frame(p, end, &skip, &decoded, ctx); + } else if (mode == BMI270_FIFO_MODE_CONTROL) { + p += bmi270_fifo_control_frame_size(BMI270_FIFO_HDR_PARM(*p)); + } else { + p++; + } + } + + return decoded; +} + +static int bmi270_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec, + uint32_t *fit, uint16_t max_count, void *data_out) +{ + const struct bmi270_fifo_encoded_data *edata = + (const struct bmi270_fifo_encoded_data *)buffer; + struct sensor_three_axis_data *out = data_out; + const uint8_t *p = edata->fifo_data; + const uint8_t *end = p + edata->fifo_byte_count; + uint16_t decoded; + struct bmi270_fifo_decode_ctx ctx; + + if (!edata->header.is_fifo || chan_spec.chan_idx != 0) { + return -EINVAL; + } + + if (chan_spec.chan_type != SENSOR_CHAN_ACCEL_XYZ && + chan_spec.chan_type != SENSOR_CHAN_GYRO_XYZ) { + return -ENOTSUP; + } + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 0; + + ctx.out = out; + ctx.fit_base = *fit; + ctx.sample_period_ns = bmi270_sample_period_ns(&edata->header, chan_spec.chan_type); + ctx.chan_type = chan_spec.chan_type; + ctx.acc_g = acc_range_reg_to_g(edata->header.acc_range); + ctx.gyr_dps = gyr_range_idx_to_dps(edata->header.gyr_range_idx); + ctx.acc_shift = + BMI270_ACC_SHIFT_BASE + (edata->header.acc_range > 0 ? edata->header.acc_range : 0); + ctx.gyr_shift = BMI270_GYR_SHIFT_BASE; + + if (edata->header.is_headerless) { + decoded = decode_fifo_headerless(p, end, max_count, &ctx); + } else { + decoded = decode_fifo_header_mode(p, end, max_count, &ctx); + } + + *fit += decoded; + out->shift = (chan_spec.chan_type == SENSOR_CHAN_ACCEL_XYZ) ? ctx.acc_shift : ctx.gyr_shift; + out->header.reading_count = decoded; + return (int)decoded; +} + +static bool bmi270_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger) +{ + ARG_UNUSED(buffer); + ARG_UNUSED(trigger); + return false; +} + +static const struct sensor_decoder_api bmi270_decoder_api = { + .get_frame_count = bmi270_decoder_get_frame_count, + .get_size_info = bmi270_decoder_get_size_info, + .decode = bmi270_decoder_decode, + .has_trigger = bmi270_decoder_has_trigger, +}; + +int bmi270_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &bmi270_decoder_api; + return 0; +} diff --git a/drivers/sensor/bosch/bmi270/bmi270_decoder.h b/drivers/sensor/bosch/bmi270/bmi270_decoder.h new file mode 100644 index 000000000000..6980019b2e2f --- /dev/null +++ b/drivers/sensor/bosch/bmi270/bmi270_decoder.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_BMI270_BMI270_DECODER_H_ +#define ZEPHYR_DRIVERS_SENSOR_BMI270_BMI270_DECODER_H_ + +#include +#include + +/** Encoded FIFO buffer header for BMI270 */ +struct bmi270_decoder_header { + uint64_t timestamp; + uint8_t is_fifo: 1; + uint8_t is_headerless: 1; /* 1 = FIFO had no per-frame headers; fixed 12 B/frame */ + uint8_t acc_range: 2; /* 0=2G, 1=4G, 2=8G, 3=16G */ + uint8_t gyr_range_idx: 3; /* 0=2000dps .. 4=125dps */ + uint8_t reserved: 1; + uint16_t acc_odr_hz; + uint16_t gyr_odr_hz; +} __attribute__((__packed__)); + +/** Encoded FIFO read result: header + raw FIFO bytes */ +struct bmi270_fifo_encoded_data { + struct bmi270_decoder_header header; + uint16_t fifo_byte_count; + uint8_t fifo_data[]; +} __attribute__((__packed__)); + +int bmi270_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); + +#endif /* ZEPHYR_DRIVERS_SENSOR_BMI270_BMI270_DECODER_H_ */ diff --git a/drivers/sensor/bosch/bmi270/bmi270_spi.c b/drivers/sensor/bosch/bmi270/bmi270_spi.c index 0bab9029f332..4b7b5ee843c3 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_spi.c +++ b/drivers/sensor/bosch/bmi270/bmi270_spi.c @@ -16,7 +16,11 @@ LOG_MODULE_DECLARE(bmi270, CONFIG_SENSOR_LOG_LEVEL); static int bmi270_bus_check_spi(const union bmi270_bus *bus) { - return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV; + if (!spi_is_ready_dt(&bus->spi)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + return 0; } static int bmi270_reg_read_spi(const union bmi270_bus *bus, diff --git a/drivers/sensor/bosch/bmi270/bmi270_stream.c b/drivers/sensor/bosch/bmi270/bmi270_stream.c new file mode 100644 index 000000000000..59c983887ec8 --- /dev/null +++ b/drivers/sensor/bosch/bmi270/bmi270_stream.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2026 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT bosch_bmi270 + +#include +#include +#include +#include +#include + +#include "bmi270.h" +#include "bmi270_decoder.h" + +#if defined(CONFIG_BMI270_STREAM) + +LOG_MODULE_DECLARE(bmi270, CONFIG_SENSOR_LOG_LEVEL); + +static inline const struct gpio_dt_spec *fifo_pin(const struct bmi270_config *config) +{ + return IS_ENABLED(CONFIG_BMI270_FIFO_ON_INT2) ? &config->int2 : &config->int1; +} + +static inline uint8_t fifo_fwm_map_bit(void) +{ + return IS_ENABLED(CONFIG_BMI270_FIFO_ON_INT2) ? BMI270_INT_MAP_DATA_FWM_INT2 + : BMI270_INT_MAP_DATA_FWM_INT1; +} + +static inline uint8_t fifo_ffull_map_bit(void) +{ + return IS_ENABLED(CONFIG_BMI270_FIFO_ON_INT2) ? BMI270_INT_MAP_DATA_FFULL_INT2 + : BMI270_INT_MAP_DATA_FFULL_INT1; +} + +static inline uint8_t fifo_io_ctrl_reg(void) +{ + return IS_ENABLED(CONFIG_BMI270_FIFO_ON_INT2) ? BMI270_REG_INT2_IO_CTRL + : BMI270_REG_INT1_IO_CTRL; +} + +static inline uint16_t fifo_watermark_bytes(void) +{ +#if defined(CONFIG_BMI270_FIFO_HEADERLESS) + return CONFIG_BMI270_FIFO_WATERMARK_SAMPLES * BMI270_FIFO_FRAME_PAYLOAD_ACC_GYR_BYTES; +#else + return CONFIG_BMI270_FIFO_WATERMARK_SAMPLES * BMI270_FIFO_FRAME_ACC_GYR_BYTES + + CONFIG_BMI270_FIFO_WATERMARK_CONTROL_MARGIN; +#endif +} + +static inline uint16_t acc_odr_reg_to_hz(uint8_t odr_reg) +{ + switch (odr_reg) { + case BMI270_ACC_ODR_25_HZ: + return 25; + case BMI270_ACC_ODR_50_HZ: + return 50; + case BMI270_ACC_ODR_100_HZ: + return 100; + case BMI270_ACC_ODR_200_HZ: + return 200; + case BMI270_ACC_ODR_400_HZ: + return 400; + case BMI270_ACC_ODR_800_HZ: + return 800; + case BMI270_ACC_ODR_1600_HZ: + return 1600; + default: + return 0; + } +} + +/* Full-scale G (2, 4, 8, 16) to bmi270_decoder_header.acc_range field 0..3 */ +static inline uint8_t acc_fullscale_g_to_decoder_idx(uint8_t fs_g) +{ + switch (fs_g) { + case 2: + return 0U; + case 4: + return 1U; + case 8: + return 2U; + default: + return 3U; + } +} + +/* Gyro full-scale (dps) to bmi270_decoder_header.gyr_range_idx field 0..4 */ +static inline uint8_t gyr_fullscale_dps_to_decoder_idx(uint16_t range_dps) +{ + switch (range_dps) { + case 2000: + return 0U; + case 1000: + return 1U; + case 500: + return 2U; + case 250: + return 3U; + case 125: + return 4U; + default: + return 0U; + } +} + +static inline uint16_t gyr_odr_reg_to_hz(uint8_t odr_reg) +{ + switch (odr_reg) { + case BMI270_GYR_ODR_25_HZ: + return 25; + case BMI270_GYR_ODR_50_HZ: + return 50; + case BMI270_GYR_ODR_100_HZ: + return 100; + case BMI270_GYR_ODR_200_HZ: + return 200; + case BMI270_GYR_ODR_400_HZ: + return 400; + case BMI270_GYR_ODR_800_HZ: + return 800; + case BMI270_GYR_ODR_1600_HZ: + return 1600; + case BMI270_GYR_ODR_3200_HZ: + return 3200; + default: + return 0; + } +} + +static inline void stream_error(const struct device *dev, struct bmi270_data *data, + struct rtio_iodev_sqe *iodev_sqe, int err) +{ + int ps_ret = bmi270_adv_power_save_enable(dev); + + if (ps_ret) { + LOG_ERR("adv_power_save_enable failed: %d", ps_ret); + } + rtio_iodev_sqe_err(iodev_sqe, err); + data->streaming_sqe = NULL; +} + +static struct sensor_stream_trigger *get_read_config_trigger(const struct sensor_read_config *cfg, + enum sensor_trigger_type trig) +{ + for (size_t i = 0; i < cfg->count; i++) { + if (cfg->triggers[i].trigger == trig) { + return (struct sensor_stream_trigger *)&cfg->triggers[i]; + } + } + return NULL; +} + +static int configure_fifo(const struct device *dev, bool enable, uint16_t watermark_bytes) +{ + uint8_t val; + int ret; + + if (!enable) { + val = 0; + ret = bmi270_reg_write(dev, BMI270_REG_FIFO_CONFIG_1, &val, 1); + if (ret < 0) { + return ret; + } + return 0; + } + + /* FIFO_CONFIG_0: stop_on_full=0, fifo_time_en=0 (streaming, no sensortime) */ + val = 0; + ret = bmi270_reg_write(dev, BMI270_REG_FIFO_CONFIG_0, &val, 1); + if (ret < 0) { + return ret; + } + + /* FIFO_CONFIG_1: acc+gyr in FIFO; header bit depends on mode */ + val = BMI270_FIFO_CONFIG_1_FIFO_ACC_EN_MSK | BMI270_FIFO_CONFIG_1_FIFO_GYR_EN_MSK; +#if !defined(CONFIG_BMI270_FIFO_HEADERLESS) + val |= BMI270_FIFO_CONFIG_1_FIFO_HEADER_EN_MSK; +#endif + ret = bmi270_reg_write(dev, BMI270_REG_FIFO_CONFIG_1, &val, 1); + if (ret < 0) { + return ret; + } + + /* Watermark in bytes */ + uint8_t wtm[2] = { + watermark_bytes & 0xFF, + (watermark_bytes >> 8) & 0x1F, + }; + ret = bmi270_reg_write(dev, BMI270_REG_FIFO_WTM_0, wtm, 2); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int map_fifo_int(const struct device *dev, bool watermark, bool full) +{ + uint8_t int_map = 0; + + if (watermark) { + int_map |= fifo_fwm_map_bit(); + } + if (full) { + int_map |= fifo_ffull_map_bit(); + } + return bmi270_reg_write(dev, BMI270_REG_INT_MAP_DATA, &int_map, 1); +} + +#if defined(CONFIG_BMI270_FIFO_POLL_FALLBACK) + +#define FIFO_POLL_MS CONFIG_BMI270_FIFO_POLL_PERIOD_MS + +static const struct device *poll_dev; +static struct k_work_delayable poll_work; +static bool poll_work_inited; + +static void poll_work_fn(struct k_work *work) +{ + const struct device *dev = poll_dev; + struct bmi270_data *data; + uint16_t fifo_len; + + if (dev == NULL) { + return; + } + data = dev->data; + if (data->streaming_sqe == NULL) { + return; + } + bmi270_reg_read(dev, BMI270_REG_FIFO_LENGTH_0, (uint8_t *)&fifo_len, 2); + fifo_len = sys_get_le16((uint8_t *)&fifo_len) & 0x3FFF; + if (fifo_len >= data->fifo_watermark_bytes) { + bmi270_submit_fifo_work(dev); + } + k_work_schedule(&poll_work, K_MSEC(FIFO_POLL_MS)); +} +#endif /* CONFIG_BMI270_FIFO_POLL_FALLBACK */ + +static void adv_power_save_enable_log_failure(const struct device *dev) +{ + int ret = bmi270_adv_power_save_enable(dev); + + if (ret != 0) { + LOG_ERR("adv_power_save_enable failed: %d", ret); + } +} + +static void fifo_fill_encoded_header(struct bmi270_fifo_encoded_data *edata, + struct bmi270_data *data, uint16_t fifo_len) +{ + edata->header.is_fifo = true; +#if defined(CONFIG_BMI270_FIFO_HEADERLESS) + edata->header.is_headerless = true; +#else + edata->header.is_headerless = false; +#endif + edata->header.timestamp = data->timestamp; + edata->header.acc_range = acc_fullscale_g_to_decoder_idx(data->acc_range); + edata->header.acc_odr_hz = acc_odr_reg_to_hz(data->acc_odr); + edata->header.gyr_odr_hz = gyr_odr_reg_to_hz(data->gyr_odr); + edata->header.gyr_range_idx = gyr_fullscale_dps_to_decoder_idx(data->gyr_range); + edata->fifo_byte_count = fifo_len; +} + +static void fifo_drain_bytes(const struct device *dev, uint16_t remain) +{ + uint8_t discard[64]; + + while (remain > 0) { + size_t chunk = remain > sizeof(discard) ? sizeof(discard) : (size_t)remain; + + bmi270_reg_read(dev, BMI270_REG_FIFO_DATA, discard, chunk); + remain -= (uint16_t)chunk; + } +} + +void bmi270_stream_handle_fifo(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + uint8_t int_status_1; + uint16_t fifo_len; + uint64_t cycles; + uint8_t *buf; + uint32_t buf_len; + struct bmi270_fifo_encoded_data *edata; + int ret; + + if (iodev_sqe == NULL) { + return; + } + + LOG_DBG("FIFO INT: handling watermark"); + + if (sensor_clock_get_cycles(&cycles) == 0) { + data->timestamp = sensor_clock_cycles_to_ns(cycles); + } + + ret = bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &int_status_1, 1); + if (ret < 0) { + stream_error(dev, data, iodev_sqe, ret); + return; + } + + data->int_status_1 = int_status_1; + + if (!(int_status_1 & (BMI270_INT_STATUS_1_FWM_INT | BMI270_INT_STATUS_1_FFULL_INT))) { + adv_power_save_enable_log_failure(dev); + return; + } + + /* Read FIFO length (14-bit, LSB then MSB) */ + ret = bmi270_reg_read(dev, BMI270_REG_FIFO_LENGTH_0, (uint8_t *)&fifo_len, 2); + if (ret < 0) { + stream_error(dev, data, iodev_sqe, ret); + return; + } + + fifo_len = sys_get_le16((uint8_t *)&fifo_len) & 0x3FFF; + if (fifo_len == 0) { + adv_power_save_enable_log_failure(dev); + data->streaming_sqe = NULL; + rtio_iodev_sqe_ok(iodev_sqe, 0); + return; + } + + size_t max_fifo = + CONFIG_BMI270_FIFO_STREAM_BLOCK_SIZE - sizeof(struct bmi270_fifo_encoded_data); + uint16_t fifo_len_orig = fifo_len; + + if (fifo_len > max_fifo) { + LOG_WRN("FIFO len %u > max %zu, capping and draining remainder", fifo_len, + max_fifo); + fifo_len = (uint16_t)max_fifo; + } + + size_t min_len = sizeof(struct bmi270_fifo_encoded_data) + fifo_len; + + ret = rtio_sqe_rx_buf(iodev_sqe, min_len, min_len, &buf, &buf_len); + if (ret != 0 || buf_len < min_len) { + stream_error(dev, data, iodev_sqe, -ENOMEM); + return; + } + + edata = (struct bmi270_fifo_encoded_data *)buf; + fifo_fill_encoded_header(edata, data, fifo_len); + + /* Burst read from FIFO_DATA (address does not auto-increment; burst read does) */ + ret = bmi270_reg_read(dev, BMI270_REG_FIFO_DATA, edata->fifo_data, fifo_len); + if (ret < 0) { + stream_error(dev, data, iodev_sqe, ret); + return; + } + + /* If we capped, drain remainder so FIFO is empty for next watermark */ + if (fifo_len_orig > fifo_len) { + fifo_drain_bytes(dev, fifo_len_orig - fifo_len); + } + + /* + * Clear the FWM/FFULL latch now that the FIFO is drained below + * watermark. Latched interrupts only clear when INT_STATUS is read + * AND fill level is below the threshold, so this must come AFTER drain. + */ + bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &int_status_1, 1); + + adv_power_save_enable_log_failure(dev); + + data->streaming_sqe = NULL; + rtio_iodev_sqe_ok(iodev_sqe, (int)min_len); +} + +void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + struct bmi270_data *data = dev->data; + const struct bmi270_config *config = dev->config; + struct sensor_stream_trigger *fifo_wm = + get_read_config_trigger(cfg, SENSOR_TRIG_FIFO_WATERMARK); + struct sensor_stream_trigger *fifo_full = + get_read_config_trigger(cfg, SENSOR_TRIG_FIFO_FULL); + bool use_wm = (fifo_wm != NULL); + bool use_full = (fifo_full != NULL); + const struct gpio_dt_spec *fifo_pin_submit = fifo_pin(config); + uint8_t flush_cmd, stat1; + uint64_t cycles; + int ret; + + if (!fifo_pin_submit->port) { + LOG_ERR("FIFO stream requires %s (irq-gpios)", + IS_ENABLED(CONFIG_BMI270_FIFO_ON_INT2) ? "INT2" : "INT1"); + rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); + return; + } + + if (!use_wm && !use_full) { + rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); + return; + } + + if (sensor_clock_get_cycles(&cycles) == 0) { + data->timestamp = sensor_clock_cycles_to_ns(cycles); + } else { + data->timestamp = 0; + } + + /* + * Disable adv_power_save before the register burst. In suspend mode + * the BMI270 requires 450 µs between SPI transactions; normal mode + * only needs 2 µs. Re-enabled in bmi270_stream_handle_fifo() after + * all SPI work is done. + */ + ret = bmi270_adv_power_save_disable(dev); + if (ret < 0) { + LOG_ERR("adv_power_save_disable failed: %d", ret); + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + data->fifo_watermark_bytes = fifo_watermark_bytes(); + + ret = configure_fifo(dev, true, data->fifo_watermark_bytes); + if (ret < 0) { + LOG_ERR("FIFO config failed: %d", ret); + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + ret = map_fifo_int(dev, use_wm, use_full); + if (ret < 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + /* Flush FIFO so first interrupt starts from a clean state */ + flush_cmd = BMI270_CMD_FIFO_FLUSH; + ret = bmi270_reg_write(dev, BMI270_REG_CMD, &flush_cmd, 1); + if (ret < 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + /* + * FIFO was just flushed so fill level is 0 (below watermark). + * Reading INT_STATUS_1 clears the FWM/FFULL latch, which deasserts + * the INT pin so the GPIO sees a clean LOW before arming. + */ + bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &stat1, 1); + + ret = gpio_pin_interrupt_configure_dt(fifo_pin_submit, GPIO_INT_DISABLE); + if (ret != 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + data->streaming_sqe = iodev_sqe; + ret = gpio_pin_interrupt_configure_dt(fifo_pin_submit, GPIO_INT_EDGE_TO_ACTIVE); + if (ret != 0) { + data->streaming_sqe = NULL; + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } + + LOG_DBG("Stream submitted (wm %u bytes, pin=%d)", data->fifo_watermark_bytes, + gpio_pin_get_dt(fifo_pin_submit)); + +#if defined(CONFIG_BMI270_FIFO_POLL_FALLBACK) + if (!poll_work_inited) { + k_work_init_delayable(&poll_work, poll_work_fn); + poll_work_inited = true; + } + poll_dev = dev; + k_work_schedule(&poll_work, K_MSEC(FIFO_POLL_MS)); +#endif +} + +#endif /* CONFIG_BMI270_STREAM */ diff --git a/drivers/sensor/bosch/bmi270/bmi270_trigger.c b/drivers/sensor/bosch/bmi270/bmi270_trigger.c index 6b9e7825b068..b176948a4190 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_trigger.c +++ b/drivers/sensor/bosch/bmi270/bmi270_trigger.c @@ -5,11 +5,65 @@ */ #include +#include #include LOG_MODULE_DECLARE(bmi270); #include "bmi270.h" +#if defined(CONFIG_BMI270_STREAM) +/* Dedicated work queue so FIFO handler runs on a thread with large stack (SPI + RTIO). */ +static K_KERNEL_STACK_DEFINE(bmi270_fifo_work_stack, CONFIG_BMI270_FIFO_WORKQ_STACK_SIZE); +static struct k_work_q bmi270_fifo_work_q; +static bool bmi270_fifo_work_q_initialized; + +static inline const struct gpio_dt_spec *bmi270_fifo_irq_pin(const struct bmi270_config *cfg) +{ +#if defined(CONFIG_BMI270_FIFO_ON_INT2) + return &cfg->int2; +#else + return &cfg->int1; +#endif +} + +static void bmi270_fifo_work_handler(struct k_work *work) +{ + struct bmi270_data *data = CONTAINER_OF(work, struct bmi270_data, fifo_work); + + bmi270_stream_handle_fifo(data->dev); +} + +void bmi270_submit_fifo_work(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + + k_work_submit_to_queue(&bmi270_fifo_work_q, &data->fifo_work); +} + +struct k_work_q *bmi270_get_fifo_work_q(void) +{ + return &bmi270_fifo_work_q; +} + +static bool bmi270_try_submit_fifo_irq(const struct device *dev, const struct gpio_dt_spec *irq_pin, + const char *label) +{ + struct bmi270_data *data = dev->data; + + if (data->streaming_sqe == NULL) { + return false; + } + + if (irq_pin->port) { + gpio_pin_interrupt_configure_dt(irq_pin, GPIO_INT_DISABLE); + } + + LOG_DBG("%s: submit FIFO work", label); + k_work_submit_to_queue(&bmi270_fifo_work_q, &data->fifo_work); + return true; +} +#endif + enum { INT_FLAGS_INT1, INT_FLAGS_INT2, @@ -33,6 +87,7 @@ static void bmi270_int1_callback(const struct device *dev, { struct bmi270_data *data = CONTAINER_OF(cb, struct bmi270_data, int1_cb); + bmi270_raise_int_flag(data->dev, INT_FLAGS_INT1); } @@ -44,14 +99,20 @@ static void bmi270_int2_callback(const struct device *dev, bmi270_raise_int_flag(data->dev, INT_FLAGS_INT2); } - static void bmi270_thread_cb(const struct device *dev) { struct bmi270_data *data = dev->data; int ret; - /* INT1 is used for feature interrupts */ + /* INT1: FIFO by default, feature (motion) interrupts when using INT2 for FIFO */ if (atomic_test_and_clear_bit(&data->int_flags, INT_FLAGS_INT1)) { +#if defined(CONFIG_BMI270_STREAM) && !defined(CONFIG_BMI270_FIFO_ON_INT2) + const struct bmi270_config *cfg = dev->config; + + if (bmi270_try_submit_fifo_irq(dev, bmi270_fifo_irq_pin(cfg), "INT1")) { + return; + } +#endif uint16_t int_status; ret = bmi270_reg_read(dev, BMI270_REG_INT_STATUS_0, @@ -72,8 +133,15 @@ static void bmi270_thread_cb(const struct device *dev) k_mutex_unlock(&data->trigger_mutex); } - /* INT2 is used for data ready interrupts */ + /* INT2: FIFO when CONFIG_BMI270_FIFO_ON_INT2, else data ready only */ if (atomic_test_and_clear_bit(&data->int_flags, INT_FLAGS_INT2)) { +#if defined(CONFIG_BMI270_STREAM) && defined(CONFIG_BMI270_FIFO_ON_INT2) + const struct bmi270_config *cfg = dev->config; + + if (bmi270_try_submit_fifo_irq(dev, bmi270_fifo_irq_pin(cfg), "INT2")) { + return; + } +#endif k_mutex_lock(&data->trigger_mutex, K_FOREVER); if (data->drdy_handler != NULL) { @@ -168,6 +236,24 @@ static int bmi270_init_int_pin(const struct gpio_dt_spec *pin, return 0; } +static int bmi270_configure_int_io_ctrl(const struct device *dev, const struct gpio_dt_spec *pin, + uint8_t reg, const char *name) +{ + int ret; + uint8_t io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN | BMI270_INT_IO_CTRL_LVL; + + if (!pin->port) { + return 0; + } + + ret = bmi270_reg_write(dev, reg, &io_ctrl, 1); + if (ret < 0) { + LOG_ERR("failed configuring %s_IO_CTRL (%d)", name, ret); + return ret; + } + + return 0; +} int bmi270_init_interrupts(const struct device *dev) { @@ -194,30 +280,54 @@ int bmi270_init_interrupts(const struct device *dev) ret = bmi270_init_int_pin(&cfg->int2, &data->int2_cb, bmi270_int2_callback); if (ret) { - LOG_ERR("Failed to initialize INT2"); + LOG_ERR("Failed to initialize INT2 (required for FIFO and data ready)"); return -EINVAL; } - if (cfg->int1.port) { - uint8_t int1_io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN; +#if defined(CONFIG_BMI270_STREAM) + if (!bmi270_fifo_work_q_initialized) { + k_work_queue_init(&bmi270_fifo_work_q); + k_work_queue_start(&bmi270_fifo_work_q, bmi270_fifo_work_stack, + K_THREAD_STACK_SIZEOF(bmi270_fifo_work_stack), + CONFIG_BMI270_THREAD_PRIORITY - 1, NULL); + bmi270_fifo_work_q_initialized = true; + } + k_work_init(&data->fifo_work, bmi270_fifo_work_handler); +#endif - ret = bmi270_reg_write(dev, BMI270_REG_INT1_IO_CTRL, &int1_io_ctrl, 1); - if (ret < 0) { - LOG_ERR("failed configuring INT1_IO_CTRL (%d)", ret); - return ret; - } + ret = bmi270_configure_int_io_ctrl(dev, &cfg->int1, BMI270_REG_INT1_IO_CTRL, "INT1"); + if (ret < 0) { + return ret; } - if (cfg->int2.port) { - uint8_t int2_io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN; + ret = bmi270_configure_int_io_ctrl(dev, &cfg->int2, BMI270_REG_INT2_IO_CTRL, "INT2"); + if (ret < 0) { + return ret; + } - ret = bmi270_reg_write(dev, BMI270_REG_INT2_IO_CTRL, &int2_io_ctrl, 1); - if (ret < 0) { - LOG_ERR("failed configuring INT2_IO_CTRL (%d)", ret); - return ret; - } + /* + * Permanent latched: pin stays HIGH until INT_STATUS is read AND the + * interrupt condition is no longer active. Non-latched mode leaves + * INT1 permanently HIGH on this hardware even with no mapped sources. + * With latched mode the pin deasserts after the handler drains the + * FIFO and reads INT_STATUS_1, giving the nRF GPIO a clean edge. + */ + uint8_t int_latch = BMI270_INT_LATCH_PERMANENT; + + ret = bmi270_reg_write(dev, BMI270_REG_INT_LATCH, &int_latch, 1); + if (ret < 0) { + LOG_ERR("failed configuring INT_LATCH (%d)", ret); + return ret; } + /* + * Clear any stale latched status so INT pins deassert before the + * GPIO edge interrupt is armed. + */ + uint8_t dummy[2]; + + bmi270_reg_read(dev, BMI270_REG_INT_STATUS_0, dummy, 2); + return 0; } @@ -274,7 +384,6 @@ static int bmi270_anymo_config(const struct device *dev, bool enable) static int bmi270_drdy_config(const struct device *dev, bool enable) { int ret; - uint8_t int_map_data = 0; if (enable) { From a14e3675b92e4f83e547db3bfbdb9df8cdf1514d Mon Sep 17 00:00:00 2001 From: Sudarshan Iyengar Date: Thu, 14 May 2026 21:08:20 +0530 Subject: [PATCH 079/455] drivers: sensor: bmi270: convert RTIO path to fully async with SQE chaining Rework the RTIO data path to use fully asynchronous execution with SQE chaining, removing the dependency on the work queue. This simplifies the flow and aligns the implementation with RTIO design expectations by: Executing transactions directly via SQEs Eliminating intermediate work queue handling Improving consistency of async behavior FIFO handling is updated to support streaming use cases, with draining performed via an MPSC lock-free queue to ensure safe handoff between ISR and processing context. Note: Stream behavior, FIFO handling, and drain logic using the MPSC queue require further validation under concurrent and high-throughput scenarios. The new approach reduces latency and avoids context switching overhead introduced by the previous work queue based model. Signed-off-by: Sudarshan Iyengar Signed-off-by: Bartosz Meus Assisted-by: OpenAI:codex-5.5 --- drivers/sensor/bosch/bmi270/Kconfig | 11 +- drivers/sensor/bosch/bmi270/bmi270.c | 71 ++- drivers/sensor/bosch/bmi270/bmi270.h | 44 +- drivers/sensor/bosch/bmi270/bmi270_i2c.c | 52 ++ drivers/sensor/bosch/bmi270/bmi270_spi.c | 58 +++ drivers/sensor/bosch/bmi270/bmi270_stream.c | 472 +++++++++++++++++-- drivers/sensor/bosch/bmi270/bmi270_trigger.c | 39 +- 7 files changed, 647 insertions(+), 100 deletions(-) diff --git a/drivers/sensor/bosch/bmi270/Kconfig b/drivers/sensor/bosch/bmi270/Kconfig index 19383c367616..92089bb41227 100644 --- a/drivers/sensor/bosch/bmi270/Kconfig +++ b/drivers/sensor/bosch/bmi270/Kconfig @@ -9,7 +9,9 @@ menuconfig BMI270 default y depends on DT_HAS_BOSCH_BMI270_ENABLED select I2C if $(dt_compat_on_bus,$(DT_COMPAT_BOSCH_BMI270),i2c) + select I2C_RTIO if $(dt_compat_on_bus,$(DT_COMPAT_BOSCH_BMI270),i2c) select SPI if $(dt_compat_on_bus,$(DT_COMPAT_BOSCH_BMI270),spi) + select SPI_RTIO if $(dt_compat_on_bus,$(DT_COMPAT_BOSCH_BMI270),spi) help Enable driver for BMI270 I2C-based imu sensor @@ -111,15 +113,6 @@ config BMI270_FIFO_POLL_PERIOD_MS How often (in milliseconds) the poll fallback checks the FIFO fill level. Lower values reduce latency but increase SPI/I2C bus traffic. -config BMI270_FIFO_WORKQ_STACK_SIZE - int "Stack size for FIFO handler work queue thread" - depends on BMI270_STREAM - default 4096 - help - The FIFO watermark handler runs in a dedicated work queue thread to avoid - stack overflow in the trigger thread. Increase if you see crashes when - the interrupt fires. - config BMI270_FIFO_HEADERLESS bool "Use FIFO headerless mode (no per-frame headers)" depends on BMI270_STREAM diff --git a/drivers/sensor/bosch/bmi270/bmi270.c b/drivers/sensor/bosch/bmi270/bmi270.c index 1b518d0e79bc..f49e5850023c 100644 --- a/drivers/sensor/bosch/bmi270/bmi270.c +++ b/drivers/sensor/bosch/bmi270/bmi270.c @@ -66,6 +66,44 @@ int bmi270_reg_write_with_delay(const struct device *dev, return ret; } +#if defined(CONFIG_BMI270_STREAM) +int bmi270_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, size_t len, + uint8_t flags) +{ +#if CONFIG_BMI270_BUS_SPI + const struct bmi270_config *cfg = dev->config; + + if (cfg->bus_io == &bmi270_bus_io_spi) { + return bmi270_spi_prep_reg_read_async(dev, reg, buf, len, flags); + } +#endif + +#if CONFIG_BMI270_BUS_I2C + return bmi270_i2c_prep_reg_read_async(dev, reg, buf, len, flags); +#else + return -ENOTSUP; +#endif +} + +int bmi270_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t len, uint8_t flags) +{ +#if CONFIG_BMI270_BUS_SPI + const struct bmi270_config *cfg = dev->config; + + if (cfg->bus_io == &bmi270_bus_io_spi) { + return bmi270_spi_prep_reg_write_async(dev, reg, buf, len, flags); + } +#endif + +#if CONFIG_BMI270_BUS_I2C + return bmi270_i2c_prep_reg_write_async(dev, reg, buf, len, flags); +#else + return -ENOTSUP; +#endif +} +#endif + int bmi270_adv_power_save_enable(const struct device *dev) { uint8_t pwr = BMI270_PWR_CONF_ADV_PWR_SAVE_EN; @@ -692,6 +730,11 @@ static int bmi270_init(const struct device *dev) data->gyr_odr = BMI270_GYR_ODR_200_HZ; data->gyr_range = 2000; +#if defined(CONFIG_BMI270_STREAM) + mpsc_init(&data->fifo_jobs); + bmi270_stream_init(dev); +#endif + k_usleep(BMI270_POWER_ON_TIME); ret = bmi270_bus_init(dev); @@ -892,9 +935,35 @@ static const struct bmi270_feature_config bmi270_feature_base = { .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \ .bus_io = &bmi270_bus_io_i2c, +#if defined(CONFIG_BMI270_STREAM) +#define BMI270_RTIO_SPI_DEFINE(inst) \ + SPI_DT_IODEV_DEFINE(bmi270_iodev_##inst, DT_DRV_INST(inst), \ + BMI270_SPI_OPERATION); \ + RTIO_DEFINE(bmi270_rtio_ctx_##inst, 8, 8); + +#define BMI270_RTIO_I2C_DEFINE(inst) \ + I2C_DT_IODEV_DEFINE(bmi270_iodev_##inst, DT_DRV_INST(inst)); \ + RTIO_DEFINE(bmi270_rtio_ctx_##inst, 8, 8); + +#define BMI270_RTIO_DEFINE(inst) \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (BMI270_RTIO_SPI_DEFINE(inst)), \ + (BMI270_RTIO_I2C_DEFINE(inst))) +#define BMI270_RTIO_DATA_INIT(inst) \ + .rtio_ctx = &bmi270_rtio_ctx_##inst, \ + .iodev = &bmi270_iodev_##inst, +#else +#define BMI270_RTIO_DEFINE(inst) +#define BMI270_RTIO_DATA_INIT(inst) +#endif + #define BMI270_CREATE_INST(inst) \ \ - static struct bmi270_data bmi270_drv_##inst; \ + BMI270_RTIO_DEFINE(inst) \ + \ + static struct bmi270_data bmi270_drv_##inst = { \ + BMI270_RTIO_DATA_INIT(inst) \ + }; \ \ static const struct bmi270_config bmi270_config_##inst = { \ COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ diff --git a/drivers/sensor/bosch/bmi270/bmi270.h b/drivers/sensor/bosch/bmi270/bmi270.h index 281659ca1dd4..d2c547975977 100644 --- a/drivers/sensor/bosch/bmi270/bmi270.h +++ b/drivers/sensor/bosch/bmi270/bmi270.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #define BMI270_WR_LEN 32 #define BMI270_CONFIG_FILE_RETRIES 15 @@ -34,10 +36,10 @@ int bmi270_adv_power_save_enable(const struct device *dev); int bmi270_adv_power_save_disable(const struct device *dev); #if defined(CONFIG_BMI270_STREAM) +void bmi270_stream_init(const struct device *dev); void bmi270_stream_handle_fifo(const struct device *dev); +void bmi270_stream_submit_fifo_job(const struct device *dev); void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); -void bmi270_submit_fifo_work(const struct device *dev); -struct k_work_q *bmi270_get_fifo_work_q(void); #endif #define BMI270_REG_CHIP_ID 0x00 @@ -56,6 +58,8 @@ struct k_work_q *bmi270_get_fifo_work_q(void); #define BMI270_REG_TEMPERATURE_0 0x22 #define BMI270_REG_FIFO_LENGTH_0 0x24 #define BMI270_REG_FIFO_DATA 0x26 + +#define BMI270_FIFO_DRAIN_CHUNK_SIZE 64 #define BMI270_REG_FEAT_PAGE 0x2F #define BMI270_REG_FEATURES_0 0x30 #define BMI270_REG_ACC_CONF 0x40 @@ -352,11 +356,26 @@ struct bmi270_data { #endif /* CONFIG_BMI270_TRIGGER */ #if defined(CONFIG_BMI270_STREAM) + struct rtio *rtio_ctx; + struct rtio_iodev *iodev; struct rtio_iodev_sqe *streaming_sqe; uint16_t fifo_watermark_bytes; + uint16_t fifo_len; + uint16_t fifo_drain_len; uint8_t int_status_1; + uint8_t fifo_status[2]; + uint8_t *fifo_data_buf; + uint8_t fifo_discard_buf[BMI270_FIFO_DRAIN_CHUNK_SIZE]; + uint8_t spi_dummy_byte; uint64_t timestamp; - struct k_work fifo_work; + struct mpsc fifo_jobs; + struct mpsc_node fifo_job; + struct k_work fifo_job_work; + struct k_spinlock fifo_job_lock; + uint16_t fifo_job_pending; + uint8_t fifo_job_phase; + bool fifo_job_queued; + bool fifo_job_processing; #endif /* CONFIG_BMI270_STREAM */ }; @@ -421,6 +440,25 @@ extern const struct bmi270_bus_io bmi270_bus_io_spi; extern const struct bmi270_bus_io bmi270_bus_io_i2c; #endif +#if defined(CONFIG_BMI270_STREAM) +int bmi270_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, size_t len, + uint8_t flags); +int bmi270_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t len, uint8_t flags); +#if CONFIG_BMI270_BUS_SPI +int bmi270_spi_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, + size_t len, uint8_t flags); +int bmi270_spi_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t len, uint8_t flags); +#endif +#if CONFIG_BMI270_BUS_I2C +int bmi270_i2c_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, + size_t len, uint8_t flags); +int bmi270_i2c_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t len, uint8_t flags); +#endif +#endif + int bmi270_reg_read(const struct device *dev, uint8_t reg, uint8_t *data, uint16_t length); int bmi270_reg_write(const struct device *dev, uint8_t reg, diff --git a/drivers/sensor/bosch/bmi270/bmi270_i2c.c b/drivers/sensor/bosch/bmi270/bmi270_i2c.c index 76c1955f6dab..96549256eb9a 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_i2c.c +++ b/drivers/sensor/bosch/bmi270/bmi270_i2c.c @@ -57,3 +57,55 @@ const struct bmi270_bus_io bmi270_bus_io_i2c = { .write = bmi270_reg_write_i2c, .init = bmi270_bus_init_i2c, }; + +#if defined(CONFIG_BMI270_STREAM) +int bmi270_i2c_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, + size_t len, uint8_t flags) +{ + struct bmi270_data *data = dev->data; + struct rtio_sqe *sqes[2]; + struct rtio_sqe *write_reg_sqe; + struct rtio_sqe *read_buf_sqe; + + if (rtio_sqe_acquire_array(data->rtio_ctx, ARRAY_SIZE(sqes), sqes) != 0) { + return -ENOMEM; + } + + write_reg_sqe = sqes[0]; + read_buf_sqe = sqes[1]; + + rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_HIGH, ®, 1, NULL); + write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_read(read_buf_sqe, data->iodev, RTIO_PRIO_HIGH, buf, len, NULL); + read_buf_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + read_buf_sqe->flags |= flags; + + return 2; +} + +int bmi270_i2c_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t len, uint8_t flags) +{ + struct bmi270_data *data = dev->data; + struct rtio_sqe *sqes[2]; + struct rtio_sqe *write_reg_sqe; + struct rtio_sqe *write_buf_sqe; + + if (rtio_sqe_acquire_array(data->rtio_ctx, ARRAY_SIZE(sqes), sqes) != 0) { + return -ENOMEM; + } + + write_reg_sqe = sqes[0]; + write_buf_sqe = sqes[1]; + + rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_HIGH, ®, 1, NULL); + write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_write(write_buf_sqe, data->iodev, RTIO_PRIO_HIGH, buf, len, NULL); + write_buf_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP; + write_buf_sqe->flags |= flags; + + return 2; +} +#endif diff --git a/drivers/sensor/bosch/bmi270/bmi270_spi.c b/drivers/sensor/bosch/bmi270/bmi270_spi.c index 4b7b5ee843c3..d2dc28f5a0c4 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_spi.c +++ b/drivers/sensor/bosch/bmi270/bmi270_spi.c @@ -102,3 +102,61 @@ const struct bmi270_bus_io bmi270_bus_io_spi = { .write = bmi270_reg_write_spi, .init = bmi270_bus_init_spi, }; + +#if defined(CONFIG_BMI270_STREAM) +int bmi270_spi_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, + size_t len, uint8_t flags) +{ + struct bmi270_data *data = dev->data; + struct rtio_sqe *sqes[3]; + struct rtio_sqe *write_reg_sqe; + struct rtio_sqe *dummy_sqe; + struct rtio_sqe *read_buf_sqe; + uint8_t addr = reg | 0x80; + + if (rtio_sqe_acquire_array(data->rtio_ctx, ARRAY_SIZE(sqes), sqes) != 0) { + return -ENOMEM; + } + + write_reg_sqe = sqes[0]; + dummy_sqe = sqes[1]; + read_buf_sqe = sqes[2]; + + rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_HIGH, &addr, 1, NULL); + write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_read(dummy_sqe, data->iodev, RTIO_PRIO_HIGH, &data->spi_dummy_byte, 1, + NULL); + /* Keep the dummy byte in the same SPI transaction; the payload read carries chaining. */ + dummy_sqe->flags |= RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_read(read_buf_sqe, data->iodev, RTIO_PRIO_HIGH, buf, len, NULL); + read_buf_sqe->flags |= flags; + + return 3; +} + +int bmi270_spi_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, + size_t len, uint8_t flags) +{ + struct bmi270_data *data = dev->data; + struct rtio_sqe *sqes[2]; + struct rtio_sqe *write_reg_sqe; + struct rtio_sqe *write_buf_sqe; + + if (rtio_sqe_acquire_array(data->rtio_ctx, ARRAY_SIZE(sqes), sqes) != 0) { + return -ENOMEM; + } + + write_reg_sqe = sqes[0]; + write_buf_sqe = sqes[1]; + + rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_HIGH, ®, 1, NULL); + write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_write(write_buf_sqe, data->iodev, RTIO_PRIO_HIGH, buf, len, NULL); + write_buf_sqe->flags |= flags; + + return 2; +} +#endif diff --git a/drivers/sensor/bosch/bmi270/bmi270_stream.c b/drivers/sensor/bosch/bmi270/bmi270_stream.c index 59c983887ec8..cc3f1344eb4c 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_stream.c +++ b/drivers/sensor/bosch/bmi270/bmi270_stream.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "bmi270.h" #include "bmi270_decoder.h" @@ -236,7 +237,7 @@ static void poll_work_fn(struct k_work *work) bmi270_reg_read(dev, BMI270_REG_FIFO_LENGTH_0, (uint8_t *)&fifo_len, 2); fifo_len = sys_get_le16((uint8_t *)&fifo_len) & 0x3FFF; if (fifo_len >= data->fifo_watermark_bytes) { - bmi270_submit_fifo_work(dev); + bmi270_stream_submit_fifo_job(dev); } k_work_schedule(&poll_work, K_MSEC(FIFO_POLL_MS)); } @@ -268,112 +269,483 @@ static void fifo_fill_encoded_header(struct bmi270_fifo_encoded_data *edata, edata->fifo_byte_count = fifo_len; } -static void fifo_drain_bytes(const struct device *dev, uint16_t remain) +static int bmi270_rtio_drain_cq(struct rtio *r, int result) { - uint8_t discard[64]; + struct rtio_cqe *cqe; + + do { + cqe = rtio_cqe_consume(r); + if (cqe != NULL) { + if (result >= 0) { + result = cqe->result; + } + rtio_cqe_release(r, cqe); + } + } while (cqe != NULL); + + return result; +} + +enum bmi270_fifo_job_phase { + BMI270_FIFO_JOB_STATUS, + BMI270_FIFO_JOB_DATA, + BMI270_FIFO_JOB_DRAIN, + BMI270_FIFO_JOB_CLEAR_STATUS, +}; + +/* SPI register reads use 3 SQEs, so keep each drain submission within the 8-SQE pool. */ +#define BMI270_FIFO_DATA_JOB_DRAIN_READS 1 +#define BMI270_FIFO_DRAIN_JOB_DRAIN_READS 2 + +static void bmi270_fifo_job_work_handler(struct k_work *work) +{ + struct bmi270_data *data = CONTAINER_OF(work, struct bmi270_data, fifo_job_work); + + bmi270_stream_handle_fifo(data->dev); +} + +void bmi270_stream_init(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + + k_work_init(&data->fifo_job_work, bmi270_fifo_job_work_handler); +} - while (remain > 0) { - size_t chunk = remain > sizeof(discard) ? sizeof(discard) : (size_t)remain; +void bmi270_stream_submit_fifo_job(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + k_spinlock_key_t key; - bmi270_reg_read(dev, BMI270_REG_FIFO_DATA, discard, chunk); - remain -= (uint16_t)chunk; + key = k_spin_lock(&data->fifo_job_lock); + data->fifo_job_pending++; + if (!data->fifo_job_queued && !data->fifo_job_processing) { + data->fifo_job_pending--; + data->fifo_job_queued = true; + data->fifo_job_phase = BMI270_FIFO_JOB_STATUS; + mpsc_push(&data->fifo_jobs, &data->fifo_job); } + k_spin_unlock(&data->fifo_job_lock, key); + + k_work_submit(&data->fifo_job_work); } -void bmi270_stream_handle_fifo(const struct device *dev) +static void bmi270_requeue_fifo_job_work(const struct device *dev, + enum bmi270_fifo_job_phase phase) +{ + struct bmi270_data *data = dev->data; + k_spinlock_key_t key; + + key = k_spin_lock(&data->fifo_job_lock); + data->fifo_job_phase = phase; + data->fifo_job_queued = true; + data->fifo_job_processing = false; + mpsc_push(&data->fifo_jobs, &data->fifo_job); + k_spin_unlock(&data->fifo_job_lock, key); + k_work_submit(&data->fifo_job_work); +} + +static void bmi270_fifo_job_complete(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + k_spinlock_key_t key; + bool submit = false; + + key = k_spin_lock(&data->fifo_job_lock); + data->fifo_job_processing = false; + if (data->fifo_job_pending > 0U) { + data->fifo_job_pending--; + data->fifo_job_queued = true; + data->fifo_job_phase = BMI270_FIFO_JOB_STATUS; + mpsc_push(&data->fifo_jobs, &data->fifo_job); + submit = true; + } else { + data->fifo_job_queued = false; + } + k_spin_unlock(&data->fifo_job_lock, key); + + if (submit) { + k_work_submit(&data->fifo_job_work); + } +} + +static void bmi270_fifo_job_abort(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + k_spinlock_key_t key; + + key = k_spin_lock(&data->fifo_job_lock); + data->fifo_job_pending = 0U; + data->fifo_job_queued = false; + data->fifo_job_processing = false; + k_spin_unlock(&data->fifo_job_lock, key); +} + +static int bmi270_prep_fifo_drain_async(const struct device *dev, size_t max_reads, + uint16_t *drained) { struct bmi270_data *data = dev->data; - struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; - uint8_t int_status_1; - uint16_t fifo_len; - uint64_t cycles; - uint8_t *buf; - uint32_t buf_len; - struct bmi270_fifo_encoded_data *edata; int ret; - if (iodev_sqe == NULL) { - return; + *drained = 0U; + + while (data->fifo_drain_len > *drained && max_reads > 0) { + uint16_t remaining = data->fifo_drain_len - *drained; + size_t chunk = MIN((size_t)remaining, sizeof(data->fifo_discard_buf)); + + ret = bmi270_prep_reg_read_async(dev, BMI270_REG_FIFO_DATA, data->fifo_discard_buf, + chunk, RTIO_SQE_CHAINED); + if (ret < 0) { + return ret; + } + + *drained += (uint16_t)chunk; + max_reads--; } - LOG_DBG("FIFO INT: handling watermark"); + return 0; +} - if (sensor_clock_get_cycles(&cycles) == 0) { - data->timestamp = sensor_clock_cycles_to_ns(cycles); +static void bmi270_fifo_drain_done_cb(struct rtio *r, const struct rtio_sqe *sqe, int result, + void *arg) +{ + ARG_UNUSED(sqe); + + const struct device *dev = arg; + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + + result = bmi270_rtio_drain_cq(r, result); + + if (result < 0 || iodev_sqe == NULL) { + if (iodev_sqe != NULL) { + stream_error(dev, data, iodev_sqe, result); + } + data->fifo_drain_len = 0U; + bmi270_fifo_job_abort(dev); + return; } - ret = bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &int_status_1, 1); - if (ret < 0) { - stream_error(dev, data, iodev_sqe, ret); + if (data->fifo_drain_len > 0U) { + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DRAIN); + } else { + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_CLEAR_STATUS); + } +} + +static void bmi270_fifo_read_done_cb(struct rtio *r, const struct rtio_sqe *sqe, int result, + void *arg) +{ + ARG_UNUSED(sqe); + + const struct device *dev = arg; + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + + result = bmi270_rtio_drain_cq(r, result); + + if (result < 0 || iodev_sqe == NULL) { + if (iodev_sqe != NULL) { + stream_error(dev, data, iodev_sqe, result); + } + bmi270_fifo_job_abort(dev); return; } - data->int_status_1 = int_status_1; + adv_power_save_enable_log_failure(dev); - if (!(int_status_1 & (BMI270_INT_STATUS_1_FWM_INT | BMI270_INT_STATUS_1_FFULL_INT))) { - adv_power_save_enable_log_failure(dev); + data->streaming_sqe = NULL; + rtio_iodev_sqe_ok(iodev_sqe, + sizeof(struct bmi270_fifo_encoded_data) + data->fifo_len); + bmi270_fifo_job_complete(dev); +} + +static void bmi270_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, int result, + void *arg) +{ + ARG_UNUSED(sqe); + + const struct device *dev = arg; + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + uint16_t fifo_len; + uint16_t fifo_len_orig; + uint8_t *buf; + uint32_t buf_len; + struct bmi270_fifo_encoded_data *edata; + size_t max_fifo; + size_t min_len; + int ret; + + if (result < 0 || iodev_sqe == NULL) { + result = bmi270_rtio_drain_cq(r, result); + if (iodev_sqe != NULL) { + stream_error(dev, data, iodev_sqe, result); + } + bmi270_fifo_job_abort(dev); return; } - /* Read FIFO length (14-bit, LSB then MSB) */ - ret = bmi270_reg_read(dev, BMI270_REG_FIFO_LENGTH_0, (uint8_t *)&fifo_len, 2); - if (ret < 0) { - stream_error(dev, data, iodev_sqe, ret); + result = bmi270_rtio_drain_cq(r, result); + if (result < 0) { + stream_error(dev, data, iodev_sqe, result); + bmi270_fifo_job_abort(dev); return; } - fifo_len = sys_get_le16((uint8_t *)&fifo_len) & 0x3FFF; + if (!(data->int_status_1 & (BMI270_INT_STATUS_1_FWM_INT | BMI270_INT_STATUS_1_FFULL_INT))) { + adv_power_save_enable_log_failure(dev); + data->streaming_sqe = NULL; + rtio_iodev_sqe_ok(iodev_sqe, 0); + bmi270_fifo_job_complete(dev); + return; + } + + fifo_len = sys_get_le16(data->fifo_status) & 0x3FFF; if (fifo_len == 0) { adv_power_save_enable_log_failure(dev); data->streaming_sqe = NULL; rtio_iodev_sqe_ok(iodev_sqe, 0); + bmi270_fifo_job_complete(dev); return; } - size_t max_fifo = - CONFIG_BMI270_FIFO_STREAM_BLOCK_SIZE - sizeof(struct bmi270_fifo_encoded_data); - uint16_t fifo_len_orig = fifo_len; + max_fifo = CONFIG_BMI270_FIFO_STREAM_BLOCK_SIZE - sizeof(struct bmi270_fifo_encoded_data); + fifo_len_orig = fifo_len; + data->fifo_drain_len = 0U; if (fifo_len > max_fifo) { LOG_WRN("FIFO len %u > max %zu, capping and draining remainder", fifo_len, max_fifo); fifo_len = (uint16_t)max_fifo; + data->fifo_drain_len = fifo_len_orig - fifo_len; } - size_t min_len = sizeof(struct bmi270_fifo_encoded_data) + fifo_len; + data->fifo_len = fifo_len; + min_len = sizeof(struct bmi270_fifo_encoded_data) + fifo_len; ret = rtio_sqe_rx_buf(iodev_sqe, min_len, min_len, &buf, &buf_len); if (ret != 0 || buf_len < min_len) { stream_error(dev, data, iodev_sqe, -ENOMEM); + bmi270_fifo_job_abort(dev); return; } edata = (struct bmi270_fifo_encoded_data *)buf; fifo_fill_encoded_header(edata, data, fifo_len); + data->fifo_data_buf = edata->fifo_data; - /* Burst read from FIFO_DATA (address does not auto-increment; burst read does) */ - ret = bmi270_reg_read(dev, BMI270_REG_FIFO_DATA, edata->fifo_data, fifo_len); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DATA); +} + +static void bmi270_submit_fifo_data_job(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + struct rtio_sqe *cb_sqe; + uint16_t drained; + int ret; + + if (iodev_sqe == NULL) { + bmi270_fifo_job_abort(dev); + return; + } + + ret = bmi270_prep_reg_read_async(dev, BMI270_REG_FIFO_DATA, data->fifo_data_buf, + data->fifo_len, RTIO_SQE_CHAINED); if (ret < 0) { - stream_error(dev, data, iodev_sqe, ret); + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DATA); return; } - /* If we capped, drain remainder so FIFO is empty for next watermark */ - if (fifo_len_orig > fifo_len) { - fifo_drain_bytes(dev, fifo_len_orig - fifo_len); + if (data->fifo_drain_len > 0U) { + ret = bmi270_prep_fifo_drain_async(dev, BMI270_FIFO_DATA_JOB_DRAIN_READS, + &drained); + if (ret < 0) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DATA); + return; + } + + cb_sqe = rtio_sqe_acquire(data->rtio_ctx); + if (cb_sqe == NULL) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DATA); + return; + } + rtio_sqe_prep_callback_no_cqe(cb_sqe, bmi270_fifo_drain_done_cb, (void *)dev, + NULL); + data->fifo_drain_len -= drained; + + rtio_submit(data->rtio_ctx, 0); + return; } - /* - * Clear the FWM/FFULL latch now that the FIFO is drained below - * watermark. Latched interrupts only clear when INT_STATUS is read - * AND fill level is below the threshold, so this must come AFTER drain. - */ - bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &int_status_1, 1); + ret = bmi270_prep_reg_read_async(dev, BMI270_REG_INT_STATUS_1, &data->int_status_1, 1, + RTIO_SQE_CHAINED); + if (ret < 0) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DATA); + return; + } - adv_power_save_enable_log_failure(dev); + cb_sqe = rtio_sqe_acquire(data->rtio_ctx); + if (cb_sqe == NULL) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DATA); + return; + } + rtio_sqe_prep_callback_no_cqe(cb_sqe, bmi270_fifo_read_done_cb, (void *)dev, NULL); - data->streaming_sqe = NULL; - rtio_iodev_sqe_ok(iodev_sqe, (int)min_len); + rtio_submit(data->rtio_ctx, 0); +} + +static void bmi270_submit_fifo_drain_job(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + struct rtio_sqe *cb_sqe; + uint16_t drained; + int ret; + + if (iodev_sqe == NULL) { + bmi270_fifo_job_abort(dev); + return; + } + + if (data->fifo_drain_len == 0U) { + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_CLEAR_STATUS); + return; + } + + ret = bmi270_prep_fifo_drain_async(dev, BMI270_FIFO_DRAIN_JOB_DRAIN_READS, &drained); + if (ret < 0) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DRAIN); + return; + } + + cb_sqe = rtio_sqe_acquire(data->rtio_ctx); + if (cb_sqe == NULL) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_DRAIN); + return; + } + rtio_sqe_prep_callback_no_cqe(cb_sqe, bmi270_fifo_drain_done_cb, (void *)dev, NULL); + data->fifo_drain_len -= drained; + + rtio_submit(data->rtio_ctx, 0); +} + +static void bmi270_submit_fifo_clear_status_job(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe = data->streaming_sqe; + struct rtio_sqe *cb_sqe; + int ret; + + if (iodev_sqe == NULL) { + bmi270_fifo_job_abort(dev); + return; + } + + ret = bmi270_prep_reg_read_async(dev, BMI270_REG_INT_STATUS_1, &data->int_status_1, 1, + RTIO_SQE_CHAINED); + if (ret < 0) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_CLEAR_STATUS); + return; + } + + cb_sqe = rtio_sqe_acquire(data->rtio_ctx); + if (cb_sqe == NULL) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_CLEAR_STATUS); + return; + } + rtio_sqe_prep_callback_no_cqe(cb_sqe, bmi270_fifo_read_done_cb, (void *)dev, NULL); + + rtio_submit(data->rtio_ctx, 0); +} + +void bmi270_stream_handle_fifo(const struct device *dev) +{ + struct bmi270_data *data = dev->data; + struct rtio_iodev_sqe *iodev_sqe; + struct rtio_sqe *cb_sqe; + struct mpsc_node *job; + k_spinlock_key_t key; + uint64_t cycles; + int ret; + + key = k_spin_lock(&data->fifo_job_lock); + if (data->fifo_job_processing) { + k_spin_unlock(&data->fifo_job_lock, key); + return; + } + + job = mpsc_pop(&data->fifo_jobs); + if (job == NULL) { + data->fifo_job_queued = false; + k_spin_unlock(&data->fifo_job_lock, key); + return; + } + data->fifo_job_queued = false; + data->fifo_job_processing = true; + k_spin_unlock(&data->fifo_job_lock, key); + + iodev_sqe = data->streaming_sqe; + if (iodev_sqe == NULL) { + bmi270_fifo_job_abort(dev); + return; + } + + if (data->fifo_job_phase == BMI270_FIFO_JOB_DATA) { + bmi270_submit_fifo_data_job(dev); + return; + } + + if (data->fifo_job_phase == BMI270_FIFO_JOB_DRAIN) { + bmi270_submit_fifo_drain_job(dev); + return; + } + + if (data->fifo_job_phase == BMI270_FIFO_JOB_CLEAR_STATUS) { + bmi270_submit_fifo_clear_status_job(dev); + return; + } + + LOG_DBG("FIFO INT: handling watermark"); + + if (sensor_clock_get_cycles(&cycles) == 0) { + data->timestamp = sensor_clock_cycles_to_ns(cycles); + } + + ret = bmi270_prep_reg_read_async(dev, BMI270_REG_INT_STATUS_1, &data->int_status_1, 1, + RTIO_SQE_CHAINED); + if (ret < 0) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_STATUS); + return; + } + + ret = bmi270_prep_reg_read_async(dev, BMI270_REG_FIFO_LENGTH_0, data->fifo_status, 2, + RTIO_SQE_CHAINED); + if (ret < 0) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_STATUS); + return; + } + + cb_sqe = rtio_sqe_acquire(data->rtio_ctx); + if (cb_sqe == NULL) { + rtio_sqe_drop_all(data->rtio_ctx); + bmi270_requeue_fifo_job_work(dev, BMI270_FIFO_JOB_STATUS); + return; + } + rtio_sqe_prep_callback_no_cqe(cb_sqe, bmi270_read_fifo_cb, (void *)dev, NULL); + + rtio_submit(data->rtio_ctx, 0); } void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) diff --git a/drivers/sensor/bosch/bmi270/bmi270_trigger.c b/drivers/sensor/bosch/bmi270/bmi270_trigger.c index b176948a4190..d7deb132dfa8 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_trigger.c +++ b/drivers/sensor/bosch/bmi270/bmi270_trigger.c @@ -12,11 +12,6 @@ LOG_MODULE_DECLARE(bmi270); #include "bmi270.h" #if defined(CONFIG_BMI270_STREAM) -/* Dedicated work queue so FIFO handler runs on a thread with large stack (SPI + RTIO). */ -static K_KERNEL_STACK_DEFINE(bmi270_fifo_work_stack, CONFIG_BMI270_FIFO_WORKQ_STACK_SIZE); -static struct k_work_q bmi270_fifo_work_q; -static bool bmi270_fifo_work_q_initialized; - static inline const struct gpio_dt_spec *bmi270_fifo_irq_pin(const struct bmi270_config *cfg) { #if defined(CONFIG_BMI270_FIFO_ON_INT2) @@ -26,25 +21,6 @@ static inline const struct gpio_dt_spec *bmi270_fifo_irq_pin(const struct bmi270 #endif } -static void bmi270_fifo_work_handler(struct k_work *work) -{ - struct bmi270_data *data = CONTAINER_OF(work, struct bmi270_data, fifo_work); - - bmi270_stream_handle_fifo(data->dev); -} - -void bmi270_submit_fifo_work(const struct device *dev) -{ - struct bmi270_data *data = dev->data; - - k_work_submit_to_queue(&bmi270_fifo_work_q, &data->fifo_work); -} - -struct k_work_q *bmi270_get_fifo_work_q(void) -{ - return &bmi270_fifo_work_q; -} - static bool bmi270_try_submit_fifo_irq(const struct device *dev, const struct gpio_dt_spec *irq_pin, const char *label) { @@ -58,8 +34,8 @@ static bool bmi270_try_submit_fifo_irq(const struct device *dev, const struct gp gpio_pin_interrupt_configure_dt(irq_pin, GPIO_INT_DISABLE); } - LOG_DBG("%s: submit FIFO work", label); - k_work_submit_to_queue(&bmi270_fifo_work_q, &data->fifo_work); + LOG_DBG("%s: handle FIFO stream", label); + bmi270_stream_submit_fifo_job(dev); return true; } #endif @@ -284,17 +260,6 @@ int bmi270_init_interrupts(const struct device *dev) return -EINVAL; } -#if defined(CONFIG_BMI270_STREAM) - if (!bmi270_fifo_work_q_initialized) { - k_work_queue_init(&bmi270_fifo_work_q); - k_work_queue_start(&bmi270_fifo_work_q, bmi270_fifo_work_stack, - K_THREAD_STACK_SIZEOF(bmi270_fifo_work_stack), - CONFIG_BMI270_THREAD_PRIORITY - 1, NULL); - bmi270_fifo_work_q_initialized = true; - } - k_work_init(&data->fifo_work, bmi270_fifo_work_handler); -#endif - ret = bmi270_configure_int_io_ctrl(dev, &cfg->int1, BMI270_REG_INT1_IO_CTRL, "INT1"); if (ret < 0) { return ret; From 6f4fded586a982d3f87400cc3f6aa9092ddf4e8a Mon Sep 17 00:00:00 2001 From: Bartosz Meus Date: Tue, 7 Jul 2026 13:59:59 +0200 Subject: [PATCH 080/455] drivers: sensor: bmi270: fix async streaming setup and re-arm Guard #if CONFIG_BMI270_BUS_SPI/I2C checks with defined(), since the bare macro form silently evaluates false when the bus is unselected. Mask the register address in bmi270_spi_prep_reg_write_async() before using it in the SQE. Gate the one-time FIFO config/INT-map/flush setup in bmi270_submit_stream() behind a fifo_configured flag so it does not re-run every RTIO_SQE_MULTISHOT cycle, but keep the GPIO interrupt re-arm unconditional since bmi270_try_submit_fifo_irq() disables it on every IRQ. Also make the FIFO poll fallback always reschedule itself, since returning early while streaming_sqe is momentarily NULL used to stop it permanently. Signed-off-by: Bartosz Meus (cherry picked from commit a817f78137a160f65f213e006b5b5d618eaad89a) --- drivers/sensor/bosch/bmi270/bmi270.c | 8 +- drivers/sensor/bosch/bmi270/bmi270.h | 10 ++- drivers/sensor/bosch/bmi270/bmi270_spi.c | 3 +- drivers/sensor/bosch/bmi270/bmi270_stream.c | 92 ++++++++++++--------- 4 files changed, 65 insertions(+), 48 deletions(-) diff --git a/drivers/sensor/bosch/bmi270/bmi270.c b/drivers/sensor/bosch/bmi270/bmi270.c index f49e5850023c..6954de162fcb 100644 --- a/drivers/sensor/bosch/bmi270/bmi270.c +++ b/drivers/sensor/bosch/bmi270/bmi270.c @@ -70,7 +70,7 @@ int bmi270_reg_write_with_delay(const struct device *dev, int bmi270_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, size_t len, uint8_t flags) { -#if CONFIG_BMI270_BUS_SPI +#if defined(CONFIG_BMI270_BUS_SPI) const struct bmi270_config *cfg = dev->config; if (cfg->bus_io == &bmi270_bus_io_spi) { @@ -78,7 +78,7 @@ int bmi270_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *b } #endif -#if CONFIG_BMI270_BUS_I2C +#if defined(CONFIG_BMI270_BUS_I2C) return bmi270_i2c_prep_reg_read_async(dev, reg, buf, len, flags); #else return -ENOTSUP; @@ -88,7 +88,7 @@ int bmi270_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *b int bmi270_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, size_t len, uint8_t flags) { -#if CONFIG_BMI270_BUS_SPI +#if defined(CONFIG_BMI270_BUS_SPI) const struct bmi270_config *cfg = dev->config; if (cfg->bus_io == &bmi270_bus_io_spi) { @@ -96,7 +96,7 @@ int bmi270_prep_reg_write_async(const struct device *dev, uint8_t reg, const uin } #endif -#if CONFIG_BMI270_BUS_I2C +#if defined(CONFIG_BMI270_BUS_I2C) return bmi270_i2c_prep_reg_write_async(dev, reg, buf, len, flags); #else return -ENOTSUP; diff --git a/drivers/sensor/bosch/bmi270/bmi270.h b/drivers/sensor/bosch/bmi270/bmi270.h index d2c547975977..d0a0686d12b3 100644 --- a/drivers/sensor/bosch/bmi270/bmi270.h +++ b/drivers/sensor/bosch/bmi270/bmi270.h @@ -58,8 +58,6 @@ void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev #define BMI270_REG_TEMPERATURE_0 0x22 #define BMI270_REG_FIFO_LENGTH_0 0x24 #define BMI270_REG_FIFO_DATA 0x26 - -#define BMI270_FIFO_DRAIN_CHUNK_SIZE 64 #define BMI270_REG_FEAT_PAGE 0x2F #define BMI270_REG_FEATURES_0 0x30 #define BMI270_REG_ACC_CONF 0x40 @@ -179,6 +177,9 @@ void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev #define BMI270_FIFO_FRAME_ACC_GYR_BYTES \ (BMI270_FIFO_HEADER_BYTES + BMI270_FIFO_FRAME_PAYLOAD_ACC_GYR_BYTES) +/* Chunk size used when draining leftover FIFO bytes that exceed a stream block */ +#define BMI270_FIFO_DRAIN_CHUNK_SIZE 64 + /* FIFO_CONFIG_0 (register 0x48) */ #define BMI270_FIFO_CONFIG_0_STOP_ON_FULL_MSK BIT(0) #define BMI270_FIFO_CONFIG_0_FIFO_TIME_EN_MSK BIT(1) @@ -376,6 +377,7 @@ struct bmi270_data { uint8_t fifo_job_phase; bool fifo_job_queued; bool fifo_job_processing; + bool fifo_configured; #endif /* CONFIG_BMI270_STREAM */ }; @@ -445,13 +447,13 @@ int bmi270_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *b uint8_t flags); int bmi270_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, size_t len, uint8_t flags); -#if CONFIG_BMI270_BUS_SPI +#if defined(CONFIG_BMI270_BUS_SPI) int bmi270_spi_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, size_t len, uint8_t flags); int bmi270_spi_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, size_t len, uint8_t flags); #endif -#if CONFIG_BMI270_BUS_I2C +#if defined(CONFIG_BMI270_BUS_I2C) int bmi270_i2c_prep_reg_read_async(const struct device *dev, uint8_t reg, uint8_t *buf, size_t len, uint8_t flags); int bmi270_i2c_prep_reg_write_async(const struct device *dev, uint8_t reg, const uint8_t *buf, diff --git a/drivers/sensor/bosch/bmi270/bmi270_spi.c b/drivers/sensor/bosch/bmi270/bmi270_spi.c index d2dc28f5a0c4..3d7f552b2ff1 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_spi.c +++ b/drivers/sensor/bosch/bmi270/bmi270_spi.c @@ -143,6 +143,7 @@ int bmi270_spi_prep_reg_write_async(const struct device *dev, uint8_t reg, const struct rtio_sqe *sqes[2]; struct rtio_sqe *write_reg_sqe; struct rtio_sqe *write_buf_sqe; + uint8_t addr = reg & BMI270_REG_MASK; if (rtio_sqe_acquire_array(data->rtio_ctx, ARRAY_SIZE(sqes), sqes) != 0) { return -ENOMEM; @@ -151,7 +152,7 @@ int bmi270_spi_prep_reg_write_async(const struct device *dev, uint8_t reg, const write_reg_sqe = sqes[0]; write_buf_sqe = sqes[1]; - rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_HIGH, ®, 1, NULL); + rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_HIGH, &addr, 1, NULL); write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; rtio_sqe_prep_write(write_buf_sqe, data->iodev, RTIO_PRIO_HIGH, buf, len, NULL); diff --git a/drivers/sensor/bosch/bmi270/bmi270_stream.c b/drivers/sensor/bosch/bmi270/bmi270_stream.c index cc3f1344eb4c..99bf89debdb6 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_stream.c +++ b/drivers/sensor/bosch/bmi270/bmi270_stream.c @@ -230,15 +230,16 @@ static void poll_work_fn(struct k_work *work) if (dev == NULL) { return; } + data = dev->data; - if (data->streaming_sqe == NULL) { - return; - } - bmi270_reg_read(dev, BMI270_REG_FIFO_LENGTH_0, (uint8_t *)&fifo_len, 2); - fifo_len = sys_get_le16((uint8_t *)&fifo_len) & 0x3FFF; - if (fifo_len >= data->fifo_watermark_bytes) { - bmi270_stream_submit_fifo_job(dev); + if (data->streaming_sqe != NULL) { + bmi270_reg_read(dev, BMI270_REG_FIFO_LENGTH_0, (uint8_t *)&fifo_len, 2); + fifo_len = sys_get_le16((uint8_t *)&fifo_len) & 0x3FFF; + if (fifo_len >= data->fifo_watermark_bytes) { + bmi270_stream_submit_fifo_job(dev); + } } + k_work_schedule(&poll_work, K_MSEC(FIFO_POLL_MS)); } #endif /* CONFIG_BMI270_FIFO_POLL_FALLBACK */ @@ -378,6 +379,9 @@ static void bmi270_fifo_job_abort(const struct device *dev) data->fifo_job_queued = false; data->fifo_job_processing = false; k_spin_unlock(&data->fifo_job_lock, key); + + /* Force a full re-arm on the next submit_stream(), state may be stale after an abort. */ + data->fifo_configured = false; } static int bmi270_prep_fifo_drain_async(const struct device *dev, size_t max_reads, @@ -795,42 +799,55 @@ void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev return; } - data->fifo_watermark_bytes = fifo_watermark_bytes(); + /* + * RTIO_SQE_MULTISHOT re-invokes this submit() on every FIFO cycle, not just at + * stream start, so only run the one-time FIFO/INT-map/flush setup once. + */ + if (!data->fifo_configured) { + data->fifo_watermark_bytes = fifo_watermark_bytes(); - ret = configure_fifo(dev, true, data->fifo_watermark_bytes); - if (ret < 0) { - LOG_ERR("FIFO config failed: %d", ret); - rtio_iodev_sqe_err(iodev_sqe, ret); - return; - } + ret = configure_fifo(dev, true, data->fifo_watermark_bytes); + if (ret < 0) { + LOG_ERR("FIFO config failed: %d", ret); + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } - ret = map_fifo_int(dev, use_wm, use_full); - if (ret < 0) { - rtio_iodev_sqe_err(iodev_sqe, ret); - return; - } + ret = map_fifo_int(dev, use_wm, use_full); + if (ret < 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } - /* Flush FIFO so first interrupt starts from a clean state */ - flush_cmd = BMI270_CMD_FIFO_FLUSH; - ret = bmi270_reg_write(dev, BMI270_REG_CMD, &flush_cmd, 1); - if (ret < 0) { - rtio_iodev_sqe_err(iodev_sqe, ret); - return; - } + /* Flush FIFO so first interrupt starts from a clean state */ + flush_cmd = BMI270_CMD_FIFO_FLUSH; + ret = bmi270_reg_write(dev, BMI270_REG_CMD, &flush_cmd, 1); + if (ret < 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } - /* - * FIFO was just flushed so fill level is 0 (below watermark). - * Reading INT_STATUS_1 clears the FWM/FFULL latch, which deasserts - * the INT pin so the GPIO sees a clean LOW before arming. - */ - bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &stat1, 1); + /* + * FIFO was just flushed so fill level is 0 (below watermark). + * Reading INT_STATUS_1 clears the FWM/FFULL latch, which + * deasserts the INT pin so the GPIO sees a clean LOW before + * arming. + */ + bmi270_reg_read(dev, BMI270_REG_INT_STATUS_1, &stat1, 1); + + ret = gpio_pin_interrupt_configure_dt(fifo_pin_submit, GPIO_INT_DISABLE); + if (ret != 0) { + rtio_iodev_sqe_err(iodev_sqe, ret); + return; + } - ret = gpio_pin_interrupt_configure_dt(fifo_pin_submit, GPIO_INT_DISABLE); - if (ret != 0) { - rtio_iodev_sqe_err(iodev_sqe, ret); - return; + data->fifo_configured = true; + + LOG_DBG("Stream submitted (wm %u bytes, pin=%d)", data->fifo_watermark_bytes, + gpio_pin_get_dt(fifo_pin_submit)); } + /* Re-arm on every call: bmi270_try_submit_fifo_irq() disables this on every IRQ. */ data->streaming_sqe = iodev_sqe; ret = gpio_pin_interrupt_configure_dt(fifo_pin_submit, GPIO_INT_EDGE_TO_ACTIVE); if (ret != 0) { @@ -839,9 +856,6 @@ void bmi270_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev return; } - LOG_DBG("Stream submitted (wm %u bytes, pin=%d)", data->fifo_watermark_bytes, - gpio_pin_get_dt(fifo_pin_submit)); - #if defined(CONFIG_BMI270_FIFO_POLL_FALLBACK) if (!poll_work_inited) { k_work_init_delayable(&poll_work, poll_work_fn); From 9125de0179286e43b94fa5803a43fc576f59a8de Mon Sep 17 00:00:00 2001 From: Bartosz Meus Date: Tue, 7 Jul 2026 14:30:50 +0200 Subject: [PATCH 081/455] drivers: sensor: bmi270: precompute FIFO decode scale as a single multiply Fold the SENSOR_G/SENSOR_PI micro-unit divisor into a per-buffer scale computed once in bmi270_decoder_decode(), instead of recomputing it on every frame and dividing again per axis. decode_accel_frame() and decode_gyro_frame() now convert each axis with a single multiply. Also group the plain field/macro assignments in bmi270_decoder_decode() separately from the ones requiring a function call, for readability. Signed-off-by: Bartosz Meus (cherry picked from commit bd1806ba4ed023fd961f76865be5a9853e29ddf4) --- drivers/sensor/bosch/bmi270/bmi270_decoder.c | 67 ++++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/drivers/sensor/bosch/bmi270/bmi270_decoder.c b/drivers/sensor/bosch/bmi270/bmi270_decoder.c index db4d682bce14..4be59adfbd1d 100644 --- a/drivers/sensor/bosch/bmi270/bmi270_decoder.c +++ b/drivers/sensor/bosch/bmi270/bmi270_decoder.c @@ -46,6 +46,9 @@ LOG_MODULE_REGISTER(bmi270_decoder, CONFIG_SENSOR_LOG_LEVEL); #define BMI270_ACC_SHIFT_BASE 5 #define BMI270_GYR_SHIFT_BASE 6 +/* SENSOR_G/SENSOR_PI are in micro units; divide this back out once per scale. */ +#define BMI270_MICRO_UNIT_SCALE 1000000LL + static inline uint8_t bmi270_fifo_control_frame_size(uint8_t parm) { switch (parm) { @@ -152,37 +155,46 @@ static int bmi270_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_ } } -/* Accel: raw -> m/s^2 in Q31 with shift. range in G (2,4,8,16) */ -static void decode_accel_frame(const uint8_t *payload, uint8_t range_g, int8_t shift, +/* Precompute the accel raw-to-Q31 scale once per buffer instead of per frame. */ +static int64_t bmi270_accel_scale(uint8_t range_g, int8_t shift) +{ + return (int64_t)SENSOR_G * range_g * (1LL << (31 - shift)) / INT16_MAX / + BMI270_MICRO_UNIT_SCALE; +} + +/* Precompute the gyro raw-to-Q31 scale for a given range/shift; see bmi270_accel_scale(). */ +static int64_t bmi270_gyro_scale(uint16_t range_dps, int8_t shift) +{ + return (int64_t)range_dps * SENSOR_PI * (1LL << (31 - shift)) / + (180LL * INT16_MAX) / BMI270_MICRO_UNIT_SCALE; +} + +/* Accel: raw -> m/s^2 in Q31, using a scale precomputed once per buffer by bmi270_accel_scale(). */ +static void decode_accel_frame(const uint8_t *payload, int64_t scale, struct sensor_three_axis_sample_data *out) { int16_t x = (int16_t)sys_get_le16(&payload[0]); int16_t y = (int16_t)sys_get_le16(&payload[2]); int16_t z = (int16_t)sys_get_le16(&payload[4]); - int64_t scale = (int64_t)SENSOR_G * range_g * (1LL << (31 - shift)) / INT16_MAX; - out->timestamp_delta = 0; - out->x = (q31_t)((x * scale) / 1000000LL); - out->y = (q31_t)((y * scale) / 1000000LL); - out->z = (q31_t)((z * scale) / 1000000LL); + out->x = (q31_t)(x * scale); + out->y = (q31_t)(y * scale); + out->z = (q31_t)(z * scale); } -/* Gyro: raw -> rad/s in Q31 with shift. range_dps in degrees/s */ -static void decode_gyro_frame(const uint8_t *payload, uint16_t range_dps, int8_t shift, +/* Gyro: raw -> rad/s in Q31, using a scale precomputed once per buffer by bmi270_gyro_scale(). */ +static void decode_gyro_frame(const uint8_t *payload, int64_t scale, struct sensor_three_axis_sample_data *out) { int16_t x = (int16_t)sys_get_le16(&payload[0]); int16_t y = (int16_t)sys_get_le16(&payload[2]); int16_t z = (int16_t)sys_get_le16(&payload[4]); - int64_t scale = - (int64_t)range_dps * SENSOR_PI * (1LL << (31 - shift)) / (180LL * INT16_MAX); - out->timestamp_delta = 0; - out->x = (q31_t)((x * scale) / 1000000LL); - out->y = (q31_t)((y * scale) / 1000000LL); - out->z = (q31_t)((z * scale) / 1000000LL); + out->x = (q31_t)(x * scale); + out->y = (q31_t)(y * scale); + out->z = (q31_t)(z * scale); } /* Accel range register value to G (2,4,8,16) */ @@ -207,10 +219,10 @@ struct bmi270_fifo_decode_ctx { uint32_t fit_base; uint32_t sample_period_ns; uint16_t chan_type; - uint8_t acc_g; - uint16_t gyr_dps; int8_t acc_shift; int8_t gyr_shift; + int64_t acc_scale; + int64_t gyr_scale; }; /* Headerless: fixed 12-byte frames, payload order GYR then ACC (same as header mode). */ @@ -227,11 +239,9 @@ static uint16_t decode_fifo_headerless(const uint8_t *p, const uint8_t *end, uin continue; } if (ctx->chan_type == SENSOR_CHAN_ACCEL_XYZ) { - decode_accel_frame(&p[6], ctx->acc_g, ctx->acc_shift, - &ctx->out->readings[decoded]); + decode_accel_frame(&p[6], ctx->acc_scale, &ctx->out->readings[decoded]); } else { - decode_gyro_frame(p, ctx->gyr_dps, ctx->gyr_shift, - &ctx->out->readings[decoded]); + decode_gyro_frame(p, ctx->gyr_scale, &ctx->out->readings[decoded]); } ctx->out->readings[decoded].timestamp_delta = (uint32_t)(ctx->fit_base + decoded) * ctx->sample_period_ns; @@ -284,11 +294,9 @@ static const uint8_t *fifo_decode_regular_frame(const uint8_t *p, const uint8_t if (ctx->chan_type == SENSOR_CHAN_ACCEL_XYZ) { int acc_off = has_gyr ? BMI270_FIFO_SENSOR_BYTES : 0; - decode_accel_frame(&frame[acc_off], ctx->acc_g, ctx->acc_shift, - &ctx->out->readings[*decoded]); + decode_accel_frame(&frame[acc_off], ctx->acc_scale, &ctx->out->readings[*decoded]); } else { - decode_gyro_frame(frame, ctx->gyr_dps, ctx->gyr_shift, - &ctx->out->readings[*decoded]); + decode_gyro_frame(frame, ctx->gyr_scale, &ctx->out->readings[*decoded]); } ctx->out->readings[*decoded].timestamp_delta = (uint32_t)(ctx->fit_base + *decoded) * ctx->sample_period_ns; @@ -342,14 +350,17 @@ static int bmi270_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec ctx.out = out; ctx.fit_base = *fit; - ctx.sample_period_ns = bmi270_sample_period_ns(&edata->header, chan_spec.chan_type); ctx.chan_type = chan_spec.chan_type; - ctx.acc_g = acc_range_reg_to_g(edata->header.acc_range); - ctx.gyr_dps = gyr_range_idx_to_dps(edata->header.gyr_range_idx); ctx.acc_shift = BMI270_ACC_SHIFT_BASE + (edata->header.acc_range > 0 ? edata->header.acc_range : 0); ctx.gyr_shift = BMI270_GYR_SHIFT_BASE; + ctx.sample_period_ns = bmi270_sample_period_ns(&edata->header, chan_spec.chan_type); + ctx.acc_scale = + bmi270_accel_scale(acc_range_reg_to_g(edata->header.acc_range), ctx.acc_shift); + ctx.gyr_scale = + bmi270_gyro_scale(gyr_range_idx_to_dps(edata->header.gyr_range_idx), ctx.gyr_shift); + if (edata->header.is_headerless) { decoded = decode_fifo_headerless(p, end, max_count, &ctx); } else { From 5b69ef3e9975ede5c86f6512ce6c8d9131b3755a Mon Sep 17 00:00:00 2001 From: Jinming Zhao Date: Fri, 17 Jul 2026 11:04:30 +0800 Subject: [PATCH 082/455] arch: riscv: stacktrace: fix exception frame unwinding The ra register is caller-saved. For a non-leaf function interrupted after a call, esf->ra points back into that function instead of to its caller. Emitting it unconditionally adds a bogus frame. A leaf frame may have no saved return address, so its direct caller must come from esf->ra. Detect the compact leaf layout using a validated, monotonically increasing caller frame pointer, then continue through the normal walk loop. This preserves callback and maximum-depth semantics. Request leaf frame pointers when CONFIG_FRAME_POINTER is enabled so supporting compilers cannot omit the frame entirely. Older compact leaf frames remain supported. Add exact RV32E, RV32, and RV64 traces for leaf and non-leaf exception frames. Trigger the fault with an illegal instruction so M-mode and S-mode exercise the same exception path without relying on ebreak behavior. Signed-off-by: Jinming Zhao --- arch/riscv/core/CMakeLists.txt | 2 + arch/riscv/core/stacktrace.c | 43 ++++--------------- tests/arch/common/stack_unwind/CMakeLists.txt | 4 ++ tests/arch/common/stack_unwind/src/main.c | 20 +++++++++ .../arch/common/stack_unwind/src/riscv_leaf.S | 25 +++++++++++ tests/arch/common/stack_unwind/tests.yaml | 31 +++++++++++++ 6 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 tests/arch/common/stack_unwind/src/riscv_leaf.S diff --git a/arch/riscv/core/CMakeLists.txt b/arch/riscv/core/CMakeLists.txt index 68f33d68f93f..f8319e198edc 100644 --- a/arch/riscv/core/CMakeLists.txt +++ b/arch/riscv/core/CMakeLists.txt @@ -2,6 +2,8 @@ zephyr_library() +zephyr_cc_option_ifdef(CONFIG_FRAME_POINTER -mno-omit-leaf-frame-pointer) + zephyr_library_sources( cpu_idle.c fatal.c diff --git a/arch/riscv/core/stacktrace.c b/arch/riscv/core/stacktrace.c index 3171312e8f8a..fe120bd0ac43 100644 --- a/arch/riscv/core/stacktrace.c +++ b/arch/riscv/core/stacktrace.c @@ -134,40 +134,15 @@ static void walk_stackframe(riscv_stacktrace_cb cb, void *cookie, const struct k /* Unwind to the previous frame */ frame = (struct stackframe *)fp - 1; - if ((i == 0) && (esf != NULL)) { - /* Print `esf->ra` if we are at the top of the stack */ - if (in_text_region(esf->ra) && !cb(cookie, esf->ra, fp)) { - break; - } - /** - * For the first stack frame, the `ra` is not stored in the frame if the - * preempted function doesn't call any other function, we can observe: - * - * .-------------. - * frame[0]->fp ---> | frame[0] fp | - * :-------------: - * frame[0]->ra ---> | frame[1] fp | - * | frame[1] ra | - * :~~~~~~~~~~~~~: - * | frame[N] fp | - * - * Instead of: - * - * .-------------. - * frame[0]->fp ---> | frame[0] fp | - * frame[0]->ra ---> | frame[1] ra | - * :-------------: - * | frame[1] fp | - * | frame[1] ra | - * :~~~~~~~~~~~~~: - * | frame[N] fp | - * - * Check if `frame->ra` actually points to a `fp`, and adjust accordingly - */ - if (vrfy(frame->ra, thread, esf)) { - fp = frame->ra; - frame = (struct stackframe *)fp; - } + /* + * frame->ra is a return address in a regular frame. A compact + * leaf frame stores the caller's frame pointer there instead. + */ + if ((i == 0) && (esf != NULL) && (frame->ra > fp) && + vrfy(frame->ra, thread, esf)) { + fp = frame->ra; + ra = esf->ra; + continue; } fp = frame->fp; diff --git a/tests/arch/common/stack_unwind/CMakeLists.txt b/tests/arch/common/stack_unwind/CMakeLists.txt index 15b043d13252..0840d9774536 100644 --- a/tests/arch/common/stack_unwind/CMakeLists.txt +++ b/tests/arch/common/stack_unwind/CMakeLists.txt @@ -7,3 +7,7 @@ project(stack_unwind_test) FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources}) + +if(CONFIG_RISCV) + target_sources(app PRIVATE src/riscv_leaf.S) +endif() diff --git a/tests/arch/common/stack_unwind/src/main.c b/tests/arch/common/stack_unwind/src/main.c index 1380bf91578f..306087bf1bf1 100644 --- a/tests/arch/common/stack_unwind/src/main.c +++ b/tests/arch/common/stack_unwind/src/main.c @@ -9,6 +9,17 @@ #include +#if defined(STACK_UNWIND_LEAF_TEST) +extern void leaf_fault(void); + +static void (*volatile leaf_fault_call)(void) = leaf_fault; + +static void __noinline leaf_caller(void) +{ + leaf_fault_call(); + printf("unexpected return\n"); +} +#else static void func1(int a); static void func2(int a); @@ -17,7 +28,11 @@ static void __noinline func2(int a) printf("%d: %s\n", a, __func__); if (a >= 5) { +#if defined(STACK_UNWIND_NONLEAF_TEST) + __asm__ volatile(".word 0"); +#else k_oops(); +#endif } func1(a + 1); @@ -30,12 +45,17 @@ static void __noinline func1(int a) func2(a + 1); printf("bottom %d: %s\n", a, __func__); } +#endif int main(void) { printf("Hello World! %s\n", CONFIG_BOARD); +#if defined(STACK_UNWIND_LEAF_TEST) + leaf_caller(); +#else func1(1); +#endif return 0; } diff --git a/tests/arch/common/stack_unwind/src/riscv_leaf.S b/tests/arch/common/stack_unwind/src/riscv_leaf.S new file mode 100644 index 000000000000..ef793bf2d132 --- /dev/null +++ b/tests/arch/common/stack_unwind/src/riscv_leaf.S @@ -0,0 +1,25 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +GTEXT(leaf_fault) +SECTION_FUNC(TEXT, leaf_fault) + + /* + * Model a leaf frame that saves its frame pointer but not ra. The + * exception frame is the only source for the direct caller address. + */ + addi sp, sp, -16 +#ifdef CONFIG_64BIT + sd s0, 8(sp) +#else + sw s0, 12(sp) +#endif + addi s0, sp, 16 + + .word 0 diff --git a/tests/arch/common/stack_unwind/tests.yaml b/tests/arch/common/stack_unwind/tests.yaml index 855fd9f9b0bc..c2afeb5a5789 100644 --- a/tests/arch/common/stack_unwind/tests.yaml +++ b/tests/arch/common/stack_unwind/tests.yaml @@ -19,6 +19,37 @@ tests: - "E: call trace:" - "E: 0: fp: \\w+ ra: \\w+" - "E: 1: fp: \\w+ ra: \\w+" + arch.common.stack_unwind.riscv_fp_nonleaf: + arch_allow: riscv + integration_platforms: + - qemu_riscv32e + - qemu_riscv32 + - qemu_riscv64 + extra_args: EXTRA_CFLAGS=-DSTACK_UNWIND_NONLEAF_TEST + extra_configs: + - CONFIG_FRAME_POINTER=y + - CONFIG_SYMTAB=y + harness_config: + type: multi_line + regex: + - "E: 0: fp: \\w+ ra: \\w+ \\[func2\\+0x\\w+\\]" + - "E: 1: fp: \\w+ ra: \\w+ \\[func1\\+0x\\w+\\]" + arch.common.stack_unwind.riscv_fp_leaf: + arch_allow: riscv + integration_platforms: + - qemu_riscv32e + - qemu_riscv32 + - qemu_riscv64 + extra_args: EXTRA_CFLAGS=-DSTACK_UNWIND_LEAF_TEST + extra_configs: + - CONFIG_FRAME_POINTER=y + - CONFIG_SYMTAB=y + harness_config: + type: multi_line + regex: + - "E: 0: fp: \\w+ ra: \\w+ \\[leaf_fault\\+0x\\w+\\]" + - "E: 1: fp: \\w+ ra: \\w+ \\[leaf_caller\\+0x\\w+\\]" + - "E: 2: fp: \\w+ ra: \\w+ \\[main\\+0x\\w+\\]" arch.common.stack_unwind.riscv_sp: arch_allow: riscv integration_platforms: From a754770b4cabba3f211770a5ab7962e9ff866294 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 21 Jul 2026 11:40:05 -0400 Subject: [PATCH 083/455] toolchain: fix TOOLCHAIN_PRAGMA feature guard TOOLCHAIN_PRAGMA() was guarded with #ifdef TOOLCHAIN_HAS_PRAGMA_DIAG, but toolchain.h unconditionally defines that symbol to 0 a few lines earlier when no toolchain header has set it. The #ifdef therefore always evaluated true and the no-op fallback was unreachable: every TOOLCHAIN_DISABLE_WARNING() / TOOLCHAIN_ENABLE_WARNING() emitted a _Pragma even for toolchains declaring no support for it. Use #if so the declared capability is actually honoured. Two toolchain headers needed adjusting for this to stay a no-op on currently supported compilers: - iar.h defined TOOLCHAIN_HAS_PRAGMA_DIAG with an empty body, which would be a preprocessor error under #if. It now uses 1, matching the 0/1 contract documented in toolchain.h. - mwdt.h never set the symbol and so inherited the __GNUC__ >= 4.6 check in gcc.h, which ccac fails despite being LLVM based and accepting "#pragma GCC diagnostic". It now declares support explicitly and keeps the pragmas it has been getting. XCC in its non-clang configuration is GCC 4.2 based and genuinely predates #pragma GCC diagnostic push/pop, so it now correctly stops emitting those pragmas. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Anas Nashif --- include/zephyr/toolchain.h | 2 +- include/zephyr/toolchain/iar.h | 2 +- include/zephyr/toolchain/mwdt.h | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/zephyr/toolchain.h b/include/zephyr/toolchain.h index a196fc2bee47..a8aaa060a1ad 100644 --- a/include/zephyr/toolchain.h +++ b/include/zephyr/toolchain.h @@ -145,7 +145,7 @@ * * @param x Pragma directive body. */ -#ifdef TOOLCHAIN_HAS_PRAGMA_DIAG +#if TOOLCHAIN_HAS_PRAGMA_DIAG #define TOOLCHAIN_PRAGMA(x) _Pragma(#x) #else #define TOOLCHAIN_PRAGMA(x) diff --git a/include/zephyr/toolchain/iar.h b/include/zephyr/toolchain/iar.h index 418eed7c2fa5..517b39ae84c5 100644 --- a/include/zephyr/toolchain/iar.h +++ b/include/zephyr/toolchain/iar.h @@ -7,7 +7,7 @@ #ifndef ZEPHYR_INCLUDE_TOOLCHAIN_IAR_H_ #define ZEPHYR_INCLUDE_TOOLCHAIN_IAR_H_ -#define TOOLCHAIN_HAS_PRAGMA_DIAG +#define TOOLCHAIN_HAS_PRAGMA_DIAG 1 #define _TOOLCHAIN_DISABLE_WARNING(warning) TOOLCHAIN_PRAGMA(diag_suppress = warning) #define _TOOLCHAIN_ENABLE_WARNING(warning) TOOLCHAIN_PRAGMA(diag_default = warning) diff --git a/include/zephyr/toolchain/mwdt.h b/include/zephyr/toolchain/mwdt.h index 45627776cf8e..915fde3aa1db 100644 --- a/include/zephyr/toolchain/mwdt.h +++ b/include/zephyr/toolchain/mwdt.h @@ -128,6 +128,12 @@ #define __fallthrough __attribute__((fallthrough)) #endif +/* ccac is LLVM based and accepts "#pragma GCC diagnostic". Declare this + * explicitly rather than inheriting it from the __GNUC__ version check in + * gcc.h, which reports a pre-4.6 version for this compiler. + */ +#define TOOLCHAIN_HAS_PRAGMA_DIAG 1 + #define TOOLCHAIN_HAS_C_GENERIC 1 #define TOOLCHAIN_HAS_C_AUTO_TYPE 1 From f028c70a456ca222d9626d4a0740a8309577b292 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 21 Jul 2026 11:40:05 -0400 Subject: [PATCH 084/455] toolchain: iar: add include guard and drop broken RISC-V include iar.h was the only toolchain header missing the guard that rejects direct inclusion, so including it outside failed in confusing ways instead of with the intended #error. Its closing comment also named ZEPHYR_INCLUDE_TOOLCHAIN_ICCARM_H_ rather than its own guard. The __ICCRISCV__ branch included "iar/iccriscv.h", which does not exist anywhere in the tree, so any IAR RISC-V build failed at preprocessing. There is no IAR RISC-V support under cmake/ either (only iccarm-cpu.cmake and iccarm-fpu.cmake), and __ICCRISCV__ is referenced nowhere else. Drop the dead branch rather than add an empty header; it can return together with the rest of that port. No functional change for IAR Arm builds, which are the only IAR configuration currently supported. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Anas Nashif --- include/zephyr/toolchain/iar.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/zephyr/toolchain/iar.h b/include/zephyr/toolchain/iar.h index 517b39ae84c5..940d39e79b3d 100644 --- a/include/zephyr/toolchain/iar.h +++ b/include/zephyr/toolchain/iar.h @@ -7,6 +7,10 @@ #ifndef ZEPHYR_INCLUDE_TOOLCHAIN_IAR_H_ #define ZEPHYR_INCLUDE_TOOLCHAIN_IAR_H_ +#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_H_ +#error Please do not include toolchain-specific headers directly, use instead +#endif + #define TOOLCHAIN_HAS_PRAGMA_DIAG 1 #define _TOOLCHAIN_DISABLE_WARNING(warning) TOOLCHAIN_PRAGMA(diag_suppress = warning) @@ -146,8 +150,5 @@ #ifdef __ICCARM__ #include "iar/iccarm.h" #endif -#ifdef __ICCRISCV__ -#include "iar/iccriscv.h" -#endif -#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_ICCARM_H_ */ +#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_IAR_H_ */ From a52423919933e5221fa7ca7ea32f8d8ff131817b Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 21 Jul 2026 11:40:06 -0400 Subject: [PATCH 085/455] toolchain: gcc: drop probe for non-existent __builtin_div_overflow GCC and Clang provide __builtin_add_overflow, __builtin_sub_overflow and __builtin_mul_overflow, but neither compiler has a __builtin_div_overflow. The HAS_BUILTIN___builtin_div_overflow entry advertised a builtin that cannot be called. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Anas Nashif --- include/zephyr/toolchain/gcc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/zephyr/toolchain/gcc.h b/include/zephyr/toolchain/gcc.h index 2c490775dcc8..f13fb848d7a1 100644 --- a/include/zephyr/toolchain/gcc.h +++ b/include/zephyr/toolchain/gcc.h @@ -330,7 +330,6 @@ do { \ #define HAS_BUILTIN___builtin_add_overflow 1 #define HAS_BUILTIN___builtin_sub_overflow 1 #define HAS_BUILTIN___builtin_mul_overflow 1 -#define HAS_BUILTIN___builtin_div_overflow 1 #endif #if TOOLCHAIN_GCC_VERSION >= 40800 #define HAS_BUILTIN___builtin_bswap16 1 From 8cf02b0423df8072e456a782fdb278771db45d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 17:37:38 +0000 Subject: [PATCH 086/455] drivers: sensor: ism330dhcx: fix endless loop in interrupt handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drdy work loop only exited once every STATUS_REG data-ready bit was clear, but those bits are only cleared by a registered handler fetching the corresponding output registers, so an enabled source without a handler spun the loop forever. Exit as soon as no pending source has a registered handler. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/ism330dhcx/ism330dhcx_trigger.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/sensor/st/ism330dhcx/ism330dhcx_trigger.c b/drivers/sensor/st/ism330dhcx/ism330dhcx_trigger.c index 80ef7caf14a7..fd32d115473f 100644 --- a/drivers/sensor/st/ism330dhcx/ism330dhcx_trigger.c +++ b/drivers/sensor/st/ism330dhcx/ism330dhcx_trigger.c @@ -182,11 +182,12 @@ static void ism330dhcx_handle_interrupt(const struct device *dev) return; } - if ((status.xlda == 0) && (status.gda == 0) + if (!((status.xlda && (ism330dhcx->handler_drdy_acc != NULL)) || + (status.gda && (ism330dhcx->handler_drdy_gyr != NULL)) #if defined(CONFIG_ISM330DHCX_ENABLE_TEMP) - && (status.tda == 0) + || (status.tda && (ism330dhcx->handler_drdy_temp != NULL)) #endif - ) { + )) { break; } From 424b9d8adc81ec01ba685ca642e712aa4f744b2e Mon Sep 17 00:00:00 2001 From: Bastien Jauny Date: Tue, 18 Aug 2026 09:19:19 +0200 Subject: [PATCH 087/455] hostap: Do not load certificates and private keys when using PEAP When using WIFI_SECURITY_TYPE_EAP_PEAP_* security types, the certificates are optional. This skips their loading unless explicitely requested via the verify_peer_cert parameter. Signed-off-by: Bastien Jauny --- modules/hostap/src/supp_api.c | 126 +++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 56 deletions(-) diff --git a/modules/hostap/src/supp_api.c b/modules/hostap/src/supp_api.c index 6e42fd6d1456..d70e21400aea 100644 --- a/modules/hostap/src/supp_api.c +++ b/modules/hostap/src/supp_api.c @@ -1158,9 +1158,9 @@ static int wpas_add_and_config_network(struct wpa_supplicant *wpa_s, } } - if (false == ((params->security == WIFI_SECURITY_TYPE_EAP_PEAP_MSCHAPV2 || - params->security == WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2) && - (!params->verify_peer_cert))) { + if (false == (params->security == WIFI_SECURITY_TYPE_EAP_PEAP_MSCHAPV2 || + params->security == WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2 || + params->security == WIFI_SECURITY_TYPE_EAP_PEAP_GTC)) { if (wpas_config_process_blob(wpa_s->conf, "ca_cert", enterprise_creds.ca_cert, enterprise_creds.ca_cert_len)) { @@ -1171,71 +1171,85 @@ static int wpas_add_and_config_network(struct wpa_supplicant *wpa_s, resp.network_id)) { goto out; } - } - if (wpas_config_process_blob(wpa_s->conf, "client_cert", - enterprise_creds.client_cert, - enterprise_creds.client_cert_len)) { - goto out; - } + if (wpas_config_process_blob(wpa_s->conf, "client_cert", + enterprise_creds.client_cert, + enterprise_creds.client_cert_len)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d client_cert \"blob://client_cert\"", - resp.network_id)) { - goto out; - } + if (!wpa_cli_cmd_v("set_network %d client_cert \"blob://client_cert\"", + resp.network_id)) { + goto out; + } - if (wpas_config_process_blob(wpa_s->conf, "private_key", - enterprise_creds.client_key, - enterprise_creds.client_key_len)) { - goto out; - } + if (wpas_config_process_blob(wpa_s->conf, "private_key", + enterprise_creds.client_key, + enterprise_creds.client_key_len)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d private_key \"blob://private_key\"", - resp.network_id)) { - goto out; - } + if (!wpa_cli_cmd_v("set_network %d private_key \"blob://private_key\"", + resp.network_id)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d private_key_passwd \"%s\"", - resp.network_id, params->key_passwd)) { - goto out; - } + if (!wpa_cli_cmd_v("set_network %d private_key_passwd \"%s\"", + resp.network_id, params->key_passwd)) { + goto out; + } - if (wpas_config_process_blob(wpa_s->conf, "ca_cert2", - enterprise_creds.ca_cert2, - enterprise_creds.ca_cert2_len)) { - goto out; - } + if (wpas_config_process_blob(wpa_s->conf, "ca_cert2", + enterprise_creds.ca_cert2, + enterprise_creds.ca_cert2_len)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d ca_cert2 \"blob://ca_cert2\"", - resp.network_id)) { - goto out; - } + if (!wpa_cli_cmd_v("set_network %d ca_cert2 \"blob://ca_cert2\"", + resp.network_id)) { + goto out; + } - if (wpas_config_process_blob(wpa_s->conf, "client_cert2", - enterprise_creds.client_cert2, - enterprise_creds.client_cert2_len)) { - goto out; - } + if (wpas_config_process_blob(wpa_s->conf, "client_cert2", + enterprise_creds.client_cert2, + enterprise_creds.client_cert2_len)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d client_cert2 \"blob://client_cert2\"", - resp.network_id)) { - goto out; - } + if (!wpa_cli_cmd_v("set_network %d client_cert2 \"blob://client_cert2\"", + resp.network_id)) { + goto out; + } - if (wpas_config_process_blob(wpa_s->conf, "private_key2", - enterprise_creds.client_key2, - enterprise_creds.client_key2_len)) { - goto out; - } + if (wpas_config_process_blob(wpa_s->conf, "private_key2", + enterprise_creds.client_key2, + enterprise_creds.client_key2_len)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d private_key2 \"blob://private_key2\"", - resp.network_id)) { - goto out; - } + if (!wpa_cli_cmd_v("set_network %d private_key2 \"blob://private_key2\"", + resp.network_id)) { + goto out; + } - if (!wpa_cli_cmd_v("set_network %d private_key2_passwd \"%s\"", - resp.network_id, params->key2_passwd)) { - goto out; + if (!wpa_cli_cmd_v("set_network %d private_key2_passwd \"%s\"", + resp.network_id, params->key2_passwd)) { + goto out; + } + } else if (params->verify_peer_cert) { + /* If we're using MSCHAPV2 and we're + * requested a CA cert valid, load it + */ + if (wpas_config_process_blob(wpa_s->conf, "ca_cert", + enterprise_creds.ca_cert, + enterprise_creds.ca_cert_len)) { + goto out; + } + + if (!wpa_cli_cmd_v("set_network %d ca_cert \"blob://ca_cert\"", + resp.network_id)) { + goto out; + } } #endif #ifdef CONFIG_WEP From 5389672ba0b873df170cfe330ce12dc0a7a2e879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 02:17:28 +0000 Subject: [PATCH 088/455] arch: riscv: remove deprecated EXTRA_EXCEPTION_INFO Kconfig option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RISC-V specific shadow definition of EXTRA_EXCEPTION_INFO was deprecated in Zephyr 4.3 and is now removed as part of the 4.5 deprecation removal cycle. The RISC-V exception handling code (isr.S, fatal.c, coredump.c and the offsets definitions) has been keyed off CONFIG_EXCEPTION_DEBUG since the deprecation, so removing the symbol requires no code conversion. RISC-V applications that still set CONFIG_EXTRA_EXCEPTION_INFO must use CONFIG_EXCEPTION_DEBUG instead. Note this only drops the deprecated RISC-V local definition. The generic EXTRA_EXCEPTION_INFO option in arch/Kconfig, guarded by ARCH_HAS_EXTRA_EXCEPTION_INFO and used by Arm and SPARC, is unaffected. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- arch/riscv/Kconfig | 6 ------ doc/releases/migration-guide-4.5.rst | 3 +++ doc/releases/release-notes-4.5.rst | 4 ++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 89ee8a61b434..a8ae913c4af2 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -360,12 +360,6 @@ config RISCV_HART_MASK i.e. 128, 129, ..(0x80, 8x81, ..), this can be configured to 63 (0x7f) such that we can extract the bits that start from 0. -config EXTRA_EXCEPTION_INFO - bool "Collect extra exception info [DEPRECATED]" - select DEPRECATED - help - This option is deprecated and should be replaced with CONFIG_EXCEPTION_DEBUG. - config RISCV_PMP bool "RISC-V PMP Support" depends on !RISCV_S_MODE diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 9a2a60f44c3a..dc6b2eeb6c26 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -1875,6 +1875,9 @@ Architectures after the stack pointers have been set up, and is skipped on resume from suspend-to-RAM. +* The RISC-V specific ``CONFIG_EXTRA_EXCEPTION_INFO`` has been removed. Use + :kconfig:option:`CONFIG_EXCEPTION_DEBUG` instead. The option is unchanged on Arm and SPARC. + Video ===== diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index ec0f1e62b867..fd57d4a18ad2 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -75,6 +75,10 @@ Removed APIs and options * ``CONFIG_PLATFORM_SPECIFIC_INIT`` * ``z_arm_platform_init()`` + * RISC-V + + * ``CONFIG_EXTRA_EXCEPTION_INFO`` + * x86 * ``CONFIG_SSE`` From cb8b633d62487f62456e13c40b70cfa12115611b Mon Sep 17 00:00:00 2001 From: Perry Naseck Date: Thu, 20 Aug 2026 10:49:50 -0400 Subject: [PATCH 089/455] dts: Add gen defines and bnf for DT_BINDING_COMPAT_* Add gen defines and bnf for DT_BINDING_COMPAT_*, which allows for getting a node's compatible string at compile time. Adds DT_BINDING_COMPAT_TOKEN, DT_BINDING_COMPAT_UPPER_TOKEN, and DT_BINDING_COMPAT_UNQUOTED. Assisted-by: Claude:claude-opus-5 Signed-off-by: Perry Naseck --- doc/build/dts/macros.bnf | 6 ++++++ scripts/dts/gen_defines.py | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/doc/build/dts/macros.bnf b/doc/build/dts/macros.bnf index acb25e99e0cc..358735b0a670 100644 --- a/doc/build/dts/macros.bnf +++ b/doc/build/dts/macros.bnf @@ -71,6 +71,12 @@ node-macro =/ %s"DT_N" path-id %s"_COMPAT_VENDOR_IDX_" DIGIT "_EXISTS" node-macro =/ %s"DT_N" path-id %s"_COMPAT_VENDOR_IDX_" DIGIT node-macro =/ %s"DT_N" path-id %s"_COMPAT_MODEL_IDX_" DIGIT "_EXISTS" node-macro =/ %s"DT_N" path-id %s"_COMPAT_MODEL_IDX_" DIGIT +; The binding compatible (the compatible that matched a binding) as a token, +; uppercased token, or unquoted string. Only defined for nodes with a +; matching binding. +node-macro =/ %s"DT_N" path-id %s"_BINDING_COMPAT_TOKEN" +node-macro =/ %s"DT_N" path-id %s"_BINDING_COMPAT_UPPER_TOKEN" +node-macro =/ %s"DT_N" path-id %s"_BINDING_COMPAT_UNQUOTED" ; Every non-root node gets one of these macros, which expands to the node ; identifier for that node's parent in the devicetree. node-macro =/ %s"DT_N" path-id %s"_PARENT" diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 8d56057d757f..dd291dee2cda 100644 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -542,6 +542,14 @@ def write_compatibles(node: edtlib.Node) -> None: # about whether edtlib / Zephyr's binding language recognizes # them. The compatibles the node provides are what is important. + if node.matching_compat: + as_token = edtlib.str_as_token(node.matching_compat) + out_dt_define(f"{node.z_path_id}_BINDING_COMPAT_TOKEN", as_token) + out_dt_define(f"{node.z_path_id}_BINDING_COMPAT_UPPER_TOKEN", as_token.upper()) + out_dt_define( + f"{node.z_path_id}_BINDING_COMPAT_UNQUOTED", escape_unquoted(node.matching_compat) + ) + for i, compat in enumerate(node.compats): out_dt_define(f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1) From 1fd56d219f81f7171a896b806b661180627a9e15 Mon Sep 17 00:00:00 2001 From: Perry Naseck Date: Thu, 20 Aug 2026 10:51:22 -0400 Subject: [PATCH 090/455] dts: Add macros for DT_BINDING_COMPAT_* Add macros for DT_BINDING_COMPAT_*, which allows for getting a node's compatible string at compile time. Adds DT_BINDING_COMPAT_TOKEN, DT_BINDING_COMPAT_UPPER_TOKEN, and DT_BINDING_COMPAT_UNQUOTED. Assisted-by: Claude:claude-opus-5 Signed-off-by: Perry Naseck --- include/zephyr/devicetree.h | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index 696b7771c330..a83ee2ee2e6a 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -4344,6 +4344,59 @@ #define DT_NODE_HAS_COMPAT(node_id, compat) \ IS_ENABLED(DT_CAT3(node_id, _COMPAT_MATCHES_, compat)) +/** + * @brief Get a node's binding compatible as a token. + * + * Expands to the compatible string (in token form, with special + * characters replaced by underscores) that matched this node's + * binding. Useful for token-pasting to dispatch to + * compat-namespaced symbols at compile time. + * + * Example, assuming the GPIO controller node matched "atmel,sam0-gpio": + * + * @code{.c} + * DT_BINDING_COMPAT_TOKEN(DT_NODELABEL(gpio0)) // expands to: atmel_sam0_gpio + * @endcode + * + * @param node_id node identifier + * @return The binding compatible as a C token. + */ +#define DT_BINDING_COMPAT_TOKEN(node_id) DT_CAT(node_id, _BINDING_COMPAT_TOKEN) + +/** + * @brief Get a node's binding compatible as an uppercased token. + * + * Like DT_BINDING_COMPAT_TOKEN(), but uppercased. + * + * Example, assuming the GPIO controller node matched "atmel,sam0-gpio": + * + * @code{.c} + * DT_BINDING_COMPAT_UPPER_TOKEN(DT_NODELABEL(gpio0)) // expands to: ATMEL_SAM0_GPIO + * @endcode + * + * @param node_id node identifier + * @return The binding compatible as an uppercased C token. + */ +#define DT_BINDING_COMPAT_UPPER_TOKEN(node_id) DT_CAT(node_id, _BINDING_COMPAT_UPPER_TOKEN) + +/** + * @brief Get a node's binding compatible as an unquoted sequence of tokens. + * + * Expands to the compatible string that matched this node's binding, + * as a sequence of tokens with no quotes. This can be used in macros + * that stringify their argument to produce a string literal. + * + * Example, assuming the GPIO controller node matched "atmel,sam0-gpio": + * + * @code{.c} + * DT_BINDING_COMPAT_UNQUOTED(DT_NODELABEL(gpio0)) // expands to: atmel,sam0-gpio + * @endcode + * + * @param node_id node identifier + * @return The binding compatible as an unquoted token sequence. + */ +#define DT_BINDING_COMPAT_UNQUOTED(node_id) DT_CAT(node_id, _BINDING_COMPAT_UNQUOTED) + /** * @brief Does a devicetree node have a compatible and status? * From 1f49157b843af02bb0d71c3bc6ffc9ea3e5cd6d3 Mon Sep 17 00:00:00 2001 From: Perry Naseck Date: Thu, 20 Aug 2026 10:52:24 -0400 Subject: [PATCH 091/455] dts: Add tests for DT_BINDING_COMPAT_* Add tests for DT_BINDING_COMPAT_*, which allows for getting a node's compatible string at compile time. Adds DT_BINDING_COMPAT_TOKEN, DT_BINDING_COMPAT_UPPER_TOKEN, and DT_BINDING_COMPAT_UNQUOTED. Assisted-by: Claude:claude-opus-5 Signed-off-by: Perry Naseck --- tests/lib/devicetree/api/src/main.c | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/lib/devicetree/api/src/main.c b/tests/lib/devicetree/api/src/main.c index 9cd801c01edc..797a4dc729ab 100644 --- a/tests/lib/devicetree/api/src/main.c +++ b/tests/lib/devicetree/api/src/main.c @@ -573,6 +573,39 @@ ZTEST(devicetree_api, test_has_compat) zassert_true(DT_INST_NODE_HAS_COMPAT(0, zephyr_model2)); } +ZTEST(devicetree_api, test_binding_compat) +{ + /* test_gpio_1 has compatible "vnd,gpio-device" with a matching binding */ + const char *token = STRINGIFY(DT_BINDING_COMPAT_TOKEN(TEST_DEADBEEF)); + + zexpect_str_equal(token, "vnd_gpio_device"); + + const char *upper = STRINGIFY(DT_BINDING_COMPAT_UPPER_TOKEN(TEST_DEADBEEF)); + + zexpect_str_equal(upper, "VND_GPIO_DEVICE"); + + /* UNQUOTED contains a comma, so wrap in parentheses for STRINGIFY */ + const char *unquoted = STRINGIFY((DT_BINDING_COMPAT_UNQUOTED(TEST_DEADBEEF))); + + zexpect_str_equal(unquoted, "(vnd,gpio-device)"); + + /* TEST_ARRAYS has two compatibles: "vnd,array-holder" (has a binding) + * and "vnd,undefined-compat" (no binding). Only the one with a + * matching binding should be returned. + */ + const char *arrays_token = STRINGIFY(DT_BINDING_COMPAT_TOKEN(TEST_ARRAYS)); + + zexpect_str_equal(arrays_token, "vnd_array_holder"); + + const char *arrays_upper = STRINGIFY(DT_BINDING_COMPAT_UPPER_TOKEN(TEST_ARRAYS)); + + zexpect_str_equal(arrays_upper, "VND_ARRAY_HOLDER"); + + const char *arrays_unquoted = STRINGIFY((DT_BINDING_COMPAT_UNQUOTED(TEST_ARRAYS))); + + zexpect_str_equal(arrays_unquoted, "(vnd,array-holder)"); +} + ZTEST(devicetree_api, test_has_status) { zassert_equal(DT_NODE_HAS_STATUS(DT_NODELABEL(test_gpio_1), okay), From b6a5e6e8aa9072a10a52420095af2d99f04d71fc Mon Sep 17 00:00:00 2001 From: Kyra Lengfeld Date: Fri, 21 Aug 2026 14:13:48 +0200 Subject: [PATCH 092/455] Bluetooth: Host: Fix bt_conn reservation leak for extended advertising This fixes a leak for the following scenario: A bonded peer connects via undirected adv set while directed connectable extended advertisement runs on the same identity. The enhanced connection complete is cached, not yet processed and the undirected set is terminated, which clears the `BT_ADV_ENABLED` flag. During processing of the cached enhanced connection complete event, the `find_pending_connect()` function then finds the directed set instead of the undirected set (as until now there was no distinction of the advertising set type in the lookup function). This leads to a reservation leak, as the directed set is not terminated (but the `BT_ADV_ENABLED` flag is cleared). This commits adds the advertising set context to the `bt_hci_le_enh_conn_complete()` and `find_pending_connect()` functions, so that the correct advertising set is used for the lookup. Note: This change has been locally tested with a test case created by AI that reproduces the above scenario. Signed-off-by: Kyra Lengfeld --- subsys/bluetooth/host/adv.c | 7 ++--- subsys/bluetooth/host/hci_core.c | 46 +++++++++++++++++++++++++++----- subsys/bluetooth/host/hci_core.h | 20 +++++++------- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/subsys/bluetooth/host/adv.c b/subsys/bluetooth/host/adv.c index d2bc7dcccf1c..2a5a10d5ca09 100644 --- a/subsys/bluetooth/host/adv.c +++ b/subsys/bluetooth/host/adv.c @@ -2159,14 +2159,15 @@ void bt_hci_le_adv_set_terminated(struct net_buf *buf) if (bt_dev.cached_conn_complete[i].valid && bt_dev.cached_conn_complete[i].evt.handle == evt->conn_handle) { if (was_adv_enabled) { - /* Process the cached connection complete event - * now that the corresponding advertising set is known. + /* Process the cached connection complete event with the + * advertising set context. * * If the advertiser has been stopped before the connection * complete event has been raised to the application, we * discard the event. */ - bt_hci_le_enh_conn_complete(&bt_dev.cached_conn_complete[i].evt); + bt_hci_le_enh_conn_complete(&bt_dev.cached_conn_complete[i].evt, + adv); } bt_dev.cached_conn_complete[i].valid = false; } diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 99e2e73ecd93..e9a03a06c09e 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1258,7 +1258,8 @@ int bt_le_set_phy(struct bt_conn *conn, uint8_t all_phys, return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PHY, buf, NULL); } -static struct bt_conn *find_pending_connect(uint8_t role, bt_addr_le_t *peer_addr) +static struct bt_conn *find_pending_connect(uint8_t role, const bt_addr_le_t *peer_addr, + const struct bt_le_ext_adv *ext_adv) { struct bt_conn *conn; @@ -1279,6 +1280,33 @@ static struct bt_conn *find_pending_connect(uint8_t role, bt_addr_le_t *peer_add } if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && role == BT_HCI_ROLE_PERIPHERAL) { + /* Do not fall back between directed and undirected pending connections + * when the terminating advertising set is known. Such a fallback can + * consume a reservation belonging to another active set. + */ + if (ext_adv != NULL) { + if (bt_addr_le_eq(&ext_adv->target_addr, BT_ADDR_LE_ANY)) { + /* Having multiple same-identity undirected reservations and + * finding the first one that might not have been the one that + * was used to initiate the connection is not a problem. + * Undirected reservations have no advertising-set association + * or per-set state, so any matching reservation can + * be consumed; one remains for each other enabled set. + */ + return bt_conn_lookup_state_le(ext_adv->id, BT_ADDR_LE_NONE, + BT_CONN_ADV_CONNECTABLE); + } + + return bt_conn_lookup_state_le(ext_adv->id, &ext_adv->target_addr, + BT_CONN_ADV_DIR_CONNECTABLE); + } + + /* In case there is no advertising handle, there can be at most one + * relevant peripheral advertiser. This is the case for legacy + * advertising, or when the controller does not support extended + * advertising. In this case, we can fall back to the legacy lookup + * behaviour. + */ conn = bt_conn_lookup_state_le(bt_dev.adv_conn_id, peer_addr, BT_CONN_ADV_DIR_CONNECTABLE); if (!conn) { @@ -1303,7 +1331,7 @@ static void le_conn_complete_cancel(uint8_t err) * There is no need to check ID address as only one * connection in central role can be in pending state. */ - conn = find_pending_connect(BT_HCI_ROLE_CENTRAL, NULL); + conn = find_pending_connect(BT_HCI_ROLE_CENTRAL, NULL, NULL); if (!conn) { LOG_ERR("No pending central connection"); return; @@ -1363,7 +1391,7 @@ static void le_conn_complete_adv_timeout(void) /* There is no need to check ID address as only one * connection in peripheral role can be in pending state. */ - conn = find_pending_connect(BT_HCI_ROLE_PERIPHERAL, NULL); + conn = find_pending_connect(BT_HCI_ROLE_PERIPHERAL, NULL, NULL); if (!conn) { LOG_ERR("No pending peripheral connection"); return; @@ -1404,7 +1432,7 @@ static void enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) return; } #endif - bt_hci_le_enh_conn_complete(evt); + bt_hci_le_enh_conn_complete(evt, NULL); } static void translate_addrs(bt_addr_le_t *peer_addr, bt_addr_le_t *id_addr, @@ -1442,7 +1470,8 @@ static void update_conn(struct bt_conn *conn, const bt_addr_le_t *id_addr, #endif } -void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) +void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt, + const struct bt_le_ext_adv *ext_adv) { __ASSERT_NO_MSG(evt->status == BT_HCI_ERR_SUCCESS); @@ -1461,10 +1490,13 @@ void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) bt_id_pending_keys_update(); #endif - id = evt->role == BT_HCI_ROLE_PERIPHERAL ? bt_dev.adv_conn_id : BT_ID_DEFAULT; + id = BT_ID_DEFAULT; + if (evt->role == BT_HCI_ROLE_PERIPHERAL) { + id = ext_adv != NULL ? ext_adv->id : bt_dev.adv_conn_id; + } translate_addrs(&peer_addr, &id_addr, evt, id); - conn = find_pending_connect(evt->role, &id_addr); + conn = find_pending_connect(evt->role, &id_addr, ext_adv); if (IS_ENABLED(CONFIG_BT_CENTRAL) && evt->role == BT_HCI_ROLE_CENTRAL) { diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 7bb978f27f92..7d0ba8f74e9d 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -342,14 +342,15 @@ struct bt_dev { /* Pointer to reserved advertising set */ struct bt_le_ext_adv *adv; #if defined(CONFIG_BT_CONN) && (CONFIG_BT_EXT_ADV_MAX_ADV_SET > 1) - /* When supporting multiple concurrent connectable advertising sets - * with multiple identities, we need to know the identity of - * the terminating advertising set to identify the connection object. - * The identity of the advertising set is determined by its - * advertising handle, which is part of the - * LE Set Advertising Set Terminated event which is always sent - * _after_ the LE Enhanced Connection complete event. - * Therefore we need cache this event until its identity is known. + /* When supporting multiple concurrent connectable advertising sets, + * we need to know the identity of the terminating advertising set to + * identify the connection object. If multiple sets share an identity, + * we also need to know whether the terminating set is directed or + * undirected to select the corresponding connection reservation. The + * advertising set is identified by its advertising handle, which is part + * of the LE Advertising Set Terminated event which is always sent _after_ + * the LE Enhanced Connection Complete event. Therefore we need to cache + * this event until its advertising set is known. */ struct { bool valid; @@ -516,7 +517,8 @@ void bt_hci_user_passkey_req(struct net_buf *buf); void bt_hci_auth_complete(struct net_buf *buf); /* Common HCI event handlers */ -void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt); +void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt, + const struct bt_le_ext_adv *ext_adv); /* Scan HCI event handlers */ void bt_hci_le_adv_report(struct net_buf *buf); From ced15b10b896b94da07e0cb1f4041ac7f669f50f Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 13 Dec 2025 14:18:16 +0900 Subject: [PATCH 093/455] drivers: pinctrl: Add RaspberryPi RP1 pinctrl driver Add a pinctrl driver for RaspberryPi RP1. Signed-off-by: TOKITA Hiroshi --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.rp1 | 9 + drivers/pinctrl/pinctrl_rp1.c | 101 ++++ .../pinctrl/raspberrypi,rp1-pinctrl.yaml | 190 ++++++++ include/zephyr/drivers/gpio/gpio_rp1.h | 79 ++++ .../drivers/pinctrl/pinctrl_rp1_common.h | 99 ++++ .../zephyr/dt-bindings/pinctrl/rp1-pinctrl.h | 430 ++++++++++++++++++ 8 files changed, 910 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.rp1 create mode 100644 drivers/pinctrl/pinctrl_rp1.c create mode 100644 dts/bindings/pinctrl/raspberrypi,rp1-pinctrl.yaml create mode 100644 include/zephyr/drivers/gpio/gpio_rp1.h create mode 100644 include/zephyr/drivers/pinctrl/pinctrl_rp1_common.h create mode 100644 include/zephyr/dt-bindings/pinctrl/rp1-pinctrl.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index 523e0fccbb67..5fd3370ed1c5 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -48,6 +48,7 @@ zephyr_library_sources_ifdef(CONFIG_PINCTRL_NXP_PORT pinctrl_nxp_port.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_NXP_SIUL2 pinctrl_nxp_siul2.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_QUICKLOGIC_EOS_S3 pinctrl_eos_s3.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_REALTEK_RTS5912 pinctrl_realtek_rts5912.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_RP1 pinctrl_rp1.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_RPI_PICO pinctrl_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_RTS5817 pinctrl_rts5817.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_RV32M1 pinctrl_rv32m1.c) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 256161c36edb..70aee442afd8 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -77,6 +77,7 @@ source "drivers/pinctrl/Kconfig.numicro" source "drivers/pinctrl/Kconfig.nxp_port" source "drivers/pinctrl/Kconfig.nxp_siul2" source "drivers/pinctrl/Kconfig.realtek_rts5912" +source "drivers/pinctrl/Kconfig.rp1" source "drivers/pinctrl/Kconfig.rpi_pico" source "drivers/pinctrl/Kconfig.rts5817" source "drivers/pinctrl/Kconfig.rv32m1" diff --git a/drivers/pinctrl/Kconfig.rp1 b/drivers/pinctrl/Kconfig.rp1 new file mode 100644 index 000000000000..2118022e6aef --- /dev/null +++ b/drivers/pinctrl/Kconfig.rp1 @@ -0,0 +1,9 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config PINCTRL_RP1 + bool "Raspberry Pi RP1 pin controller driver" + default y + depends on DT_HAS_RASPBERRYPI_RP1_PINCTRL_ENABLED + help + Enable pin controller driver for the Raspberry Pi RP1 peripheral. diff --git a/drivers/pinctrl/pinctrl_rp1.c b/drivers/pinctrl/pinctrl_rp1.c new file mode 100644 index 000000000000..aa02cce3e8b5 --- /dev/null +++ b/drivers/pinctrl/pinctrl_rp1.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT raspberrypi_rp1_pinctrl + +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(pinctrl_rp1, CONFIG_PINCTRL_LOG_LEVEL); + +#define GPIO_CTRL(base, n) ((base) + (n) * 8 + 4) +#define PADS_CTRL(base, n) ((base) + (n) * 4) + +#define GPIO_CTRL_BITS(reg, val) \ + (((val) << UTIL_CAT(UTIL_CAT(GPIO_CTRL_, reg), _SHIFT)) & \ + UTIL_CAT(UTIL_CAT(GPIO_CTRL_, reg), _MASK)) + +#define GPIO_PADS_BITS(reg, val) \ + (((val) << UTIL_CAT(UTIL_CAT(GPIO_PADS_, reg), _SHIFT)) & \ + UTIL_CAT(UTIL_CAT(GPIO_PADS_, reg), _MASK)) + +#define DEV_CFG(dev) ((const struct pinctrl_rp1_config *)(dev)->config) +#define DEV_DATA(dev) ((struct pinctrl_rp1_data *)(dev)->data) + +struct pinctrl_rp1_config { + DEVICE_MMIO_NAMED_ROM(gpio); + DEVICE_MMIO_NAMED_ROM(pads); +}; + +struct pinctrl_rp1_data { + DEVICE_MMIO_NAMED_RAM(gpio); + DEVICE_MMIO_NAMED_RAM(pads); +}; + +int raspberrypi_rp1_pinctrl_configure_pin(const struct raspberrypi_rp1_pinctrl_pinconfig *pin) +{ + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + const mm_reg_t ctrl_addr = GPIO_CTRL(DEVICE_MMIO_NAMED_GET(dev, gpio), pin->pin_num); + const mm_reg_t pads_addr = PADS_CTRL(DEVICE_MMIO_NAMED_GET(dev, pads), pin->pin_num); + uint32_t pad_val; + uint32_t ctrl_val; + + pad_val = sys_read32(pads_addr) & GPIO_PADS_RESERVED_MASK; + pad_val |= GPIO_PADS_BITS(SLEWFAST, pin->slew_rate); + pad_val |= GPIO_PADS_BITS(SCHMITT_ENABLE, pin->schmitt_enable); + pad_val |= GPIO_PADS_BITS(PULL_DOWN_ENABLE, pin->pulldown); + pad_val |= GPIO_PADS_BITS(PULL_UP_ENABLE, pin->pullup); + pad_val |= GPIO_PADS_BITS(DRIVE, pin->drive_strength); + pad_val |= GPIO_PADS_BITS(INPUT_ENABLE, pin->input_enable); + pad_val |= GPIO_PADS_BITS(OUTPUT_DISABLE, pin->output_disable); + + sys_write32(pad_val, pads_addr); + + ctrl_val = sys_read32(ctrl_addr) & GPIO_CTRL_RESERVED_MASK; + ctrl_val |= GPIO_CTRL_BITS(FUNCSEL, pin->alt_func); + ctrl_val |= GPIO_CTRL_BITS(F_M, pin->f_m); + ctrl_val |= GPIO_CTRL_BITS(OUTOVER, pin->out_override); + ctrl_val |= GPIO_CTRL_BITS(OEOVER, pin->oe_override); + ctrl_val |= GPIO_CTRL_BITS(INOVER, pin->in_override); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_EDGE_LOW, pin->irqmask_edge_low); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_EDGE_HIGH, pin->irqmask_edge_high); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_LEVEL_LOW, pin->irqmask_level_low); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_LEVEL_HIGH, pin->irqmask_level_high); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_F_EDGE_LOW, pin->irqmask_f_edge_low); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_F_EDGE_HIGH, pin->irqmask_f_edge_high); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_DB_LEVEL_LOW, pin->irqmask_db_level_low); + ctrl_val |= GPIO_CTRL_BITS(IRQMASK_DB_LEVEL_HIGH, pin->irqmask_db_level_high); + ctrl_val |= GPIO_CTRL_BITS(IRQOVER, pin->irq_override); + + sys_write32(ctrl_val, ctrl_addr); + + return 0; +} + +static int pinctrl_rp1_init(const struct device *dev) +{ + DEVICE_MMIO_NAMED_MAP(dev, gpio, K_MEM_CACHE_NONE); + DEVICE_MMIO_NAMED_MAP(dev, pads, K_MEM_CACHE_NONE); + + return 0; +} + +#define PINCTRL_RP1_INIT(n) \ + static struct pinctrl_rp1_data pinctrl_rp1_data_##n; \ + \ + static const struct pinctrl_rp1_config pinctrl_rp1_cfg_##n = { \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(gpio, DT_DRV_INST(n)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(pads, DT_DRV_INST(n)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &pinctrl_rp1_init, NULL, &pinctrl_rp1_data_##n, \ + &pinctrl_rp1_cfg_##n, PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); + +DT_INST_FOREACH_STATUS_OKAY(PINCTRL_RP1_INIT) diff --git a/dts/bindings/pinctrl/raspberrypi,rp1-pinctrl.yaml b/dts/bindings/pinctrl/raspberrypi,rp1-pinctrl.yaml new file mode 100644 index 000000000000..7ea4543aa319 --- /dev/null +++ b/dts/bindings/pinctrl/raspberrypi,rp1-pinctrl.yaml @@ -0,0 +1,190 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +title: Raspberry Pi RP1 Pin Controller + +description: | + RP1 I/O controller pinmux and pad configuration. The RP1 is the + peripheral controller used on the Raspberry Pi 5. Each controller bank + manages a group of GPIO pins and exposes the function selection and pad + control registers for those pins. + +compatible: "raspberrypi,rp1-pinctrl" + +include: base.yaml + +properties: + reg: + required: true + + reg-names: + required: true + +child-binding: + description: | + Pin controller state node. + child-binding: + + include: + - name: pincfg-node.yaml + property-allowlist: + - bias-disable + - bias-pull-down + - bias-pull-up + - input-enable + - input-schmitt-enable + - output-disable + - drive-strength + - slew-rate + + properties: + pinmux: + required: true + type: array + description: | + Pin mux selections, encoded using RP1_PINMUX() or signal-specific + macros such as RP1_UART0_TX_P14 defined in + :zephyr_file:`include/zephyr/dt-bindings/pinctrl/rp1-pinctrl.h`. + drive-strength: + enum: + - 2 + - 4 + - 8 + - 12 + default: 4 + description: | + Drive strength in mA. The value is encoded as an index in the + driver. + slew-rate: + enum: + - 0 + - 1 + default: 0 + description: | + Slew rate of a pin. 0 selects slow, 1 selects fast. + + raspberrypi,f-m: + type: int + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + default: 4 + description: | + Value written to the RP1 GPIO control register F_M field. + Refer to the RP1 documentation for the meaning of each value. + + raspberrypi,oe-override: + type: int + enum: + - 0 + - 1 + - 2 + - 3 + default: 0 + description: | + Override output-enable for a pin. + + - 0 - drive output enable from selected peripheral signal. + - 1 - drive output enable from inverse of selected peripheral + signal. + - 2 - disable output. + - 3 - enable output. + + The default value is 0, as this is the power on reset value. + raspberrypi,out-override: + type: int + enum: + - 0 + - 1 + - 2 + - 3 + default: 0 + description: | + Override output for a pin. + + - 0 - drive output from selected peripheral signal. + - 1 - drive output from inverse of selected peripheral signal. + - 2 - drive output low. + - 3 - drive output high. + + The default value is 0, as this is the power on reset value. + raspberrypi,in-override: + type: int + enum: + - 0 + - 1 + - 2 + - 3 + default: 0 + description: | + Override input for a pin. + + - 0 - input comes from the selected pin. + - 1 - input comes from the inverse of the selected pin. + - 2 - force input low. + - 3 - force input high. + + The default value is 0, as this is the power on reset value. + raspberrypi,irq-override: + type: int + enum: + - 0 + - 1 + - 2 + - 3 + default: 0 + description: | + Override interrupt signal for a pin. + + - 0 - interrupt signal from the selected pin. + - 1 - interrupt signal from the inverse of the selected pin. + - 2 - force interrupt low. + - 3 - force interrupt high. + + The default value is 0, as this is the power on reset value. + + raspberrypi,irqmask-edge-low: + type: boolean + description: | + Enable interrupt on falling edge. + + raspberrypi,irqmask-edge-high: + type: boolean + description: | + Enable interrupt on rising edge. + + raspberrypi,irqmask-level-low: + type: boolean + description: | + Enable interrupt on low level. + + raspberrypi,irqmask-level-high: + type: boolean + description: | + Enable interrupt on high level. + + raspberrypi,irqmask-f-edge-low: + type: boolean + description: | + Enable filtered interrupt on falling edge. + + raspberrypi,irqmask-f-edge-high: + type: boolean + description: | + Enable filtered interrupt on rising edge. + + raspberrypi,irqmask-db-level-low: + type: boolean + description: | + Enable debounced interrupt on low level. + + raspberrypi,irqmask-db-level-high: + type: boolean + description: | + Enable debounced interrupt on high level. diff --git a/include/zephyr/drivers/gpio/gpio_rp1.h b/include/zephyr/drivers/gpio/gpio_rp1.h new file mode 100644 index 000000000000..5dc8fb7a1a74 --- /dev/null +++ b/include/zephyr/drivers/gpio/gpio_rp1.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Junho Lee + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_GPIO_GPIO_RP1_H +#define ZEPHYR_INCLUDE_DRIVERS_GPIO_GPIO_RP1_H + +/** + * @file + * @brief RP1 GPIO register field definitions shared by GPIO and pinctrl drivers. + */ + +/** + * @cond INTERNAL_HIDDEN + * Internal register definitions shared by the RP1 GPIO and pinctrl drivers. + */ + +/* Bit positions and masks for the RP1 GPIO control register. */ +#define GPIO_CTRL_FUNCSEL_SHIFT 0 +#define GPIO_CTRL_FUNCSEL_MASK (BIT_MASK(5) << GPIO_CTRL_FUNCSEL_SHIFT) +#define GPIO_CTRL_F_M_SHIFT 5 +#define GPIO_CTRL_F_M_MASK (BIT_MASK(7) << GPIO_CTRL_F_M_SHIFT) +#define GPIO_CTRL_OUTOVER_SHIFT 12 +#define GPIO_CTRL_OUTOVER_MASK (BIT_MASK(2) << GPIO_CTRL_OUTOVER_SHIFT) +#define GPIO_CTRL_OEOVER_SHIFT 14 +#define GPIO_CTRL_OEOVER_MASK (BIT_MASK(2) << GPIO_CTRL_OEOVER_SHIFT) +#define GPIO_CTRL_INOVER_SHIFT 16 +#define GPIO_CTRL_INOVER_MASK (BIT_MASK(2) << GPIO_CTRL_INOVER_SHIFT) +#define GPIO_CTRL_IRQMASK_EDGE_LOW_SHIFT 20 +#define GPIO_CTRL_IRQMASK_EDGE_LOW_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_EDGE_LOW_SHIFT) +#define GPIO_CTRL_IRQMASK_EDGE_HIGH_SHIFT 21 +#define GPIO_CTRL_IRQMASK_EDGE_HIGH_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_EDGE_HIGH_SHIFT) +#define GPIO_CTRL_IRQMASK_LEVEL_LOW_SHIFT 22 +#define GPIO_CTRL_IRQMASK_LEVEL_LOW_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_LEVEL_LOW_SHIFT) +#define GPIO_CTRL_IRQMASK_LEVEL_HIGH_SHIFT 23 +#define GPIO_CTRL_IRQMASK_LEVEL_HIGH_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_LEVEL_HIGH_SHIFT) +#define GPIO_CTRL_IRQMASK_F_EDGE_LOW_SHIFT 24 +#define GPIO_CTRL_IRQMASK_F_EDGE_LOW_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_F_EDGE_LOW_SHIFT) +#define GPIO_CTRL_IRQMASK_F_EDGE_HIGH_SHIFT 25 +#define GPIO_CTRL_IRQMASK_F_EDGE_HIGH_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_F_EDGE_HIGH_SHIFT) +#define GPIO_CTRL_IRQMASK_DB_LEVEL_LOW_SHIFT 26 +#define GPIO_CTRL_IRQMASK_DB_LEVEL_LOW_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_DB_LEVEL_LOW_SHIFT) +#define GPIO_CTRL_IRQMASK_DB_LEVEL_HIGH_SHIFT 27 +#define GPIO_CTRL_IRQMASK_DB_LEVEL_HIGH_MASK (BIT_MASK(1) << GPIO_CTRL_IRQMASK_DB_LEVEL_HIGH_SHIFT) +#define GPIO_CTRL_IRQRESET_SHIFT 28 +#define GPIO_CTRL_IRQRESET_MASK (BIT_MASK(1) << GPIO_CTRL_IRQRESET_SHIFT) +#define GPIO_CTRL_IRQOVER_SHIFT 30 +#define GPIO_CTRL_IRQOVER_MASK (BIT_MASK(2) << GPIO_CTRL_IRQOVER_SHIFT) + +/* Reserved bits in the RP1 GPIO control register. */ +#define GPIO_CTRL_RESERVED_MASK (BIT(29) | BIT(19) | BIT(18)) + +/* Bit positions and masks for the RP1 pad control register. */ +#define GPIO_PADS_SLEWFAST_SHIFT 0 +#define GPIO_PADS_SLEWFAST_MASK (BIT_MASK(1) << GPIO_PADS_SLEWFAST_SHIFT) +#define GPIO_PADS_SCHMITT_ENABLE_SHIFT 1 +#define GPIO_PADS_SCHMITT_ENABLE_MASK (BIT_MASK(1) << GPIO_PADS_SCHMITT_ENABLE_SHIFT) +#define GPIO_PADS_PULL_DOWN_ENABLE_SHIFT 2 +#define GPIO_PADS_PULL_DOWN_ENABLE_MASK (BIT_MASK(1) << GPIO_PADS_PULL_DOWN_ENABLE_SHIFT) +#define GPIO_PADS_PULL_UP_ENABLE_SHIFT 3 +#define GPIO_PADS_PULL_UP_ENABLE_MASK (BIT_MASK(1) << GPIO_PADS_PULL_UP_ENABLE_SHIFT) +#define GPIO_PADS_DRIVE_SHIFT 4 +#define GPIO_PADS_DRIVE_MASK (BIT_MASK(2) << GPIO_PADS_DRIVE_SHIFT) +#define GPIO_PADS_INPUT_ENABLE_SHIFT 6 +#define GPIO_PADS_INPUT_ENABLE_MASK (BIT_MASK(1) << GPIO_PADS_INPUT_ENABLE_SHIFT) +#define GPIO_PADS_OUTPUT_DISABLE_SHIFT 7 +#define GPIO_PADS_OUTPUT_DISABLE_MASK (BIT_MASK(1) << GPIO_PADS_OUTPUT_DISABLE_SHIFT) + +/* Reserved bits in the RP1 pad control register. */ +#define GPIO_PADS_RESERVED_MASK GENMASK(31, 8) + +/** + * @endcond + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_GPIO_GPIO_RP1_H */ diff --git a/include/zephyr/drivers/pinctrl/pinctrl_rp1_common.h b/include/zephyr/drivers/pinctrl/pinctrl_rp1_common.h new file mode 100644 index 000000000000..c20dec4e0dcb --- /dev/null +++ b/include/zephyr/drivers/pinctrl/pinctrl_rp1_common.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_RP1_COMMON_H_ +#define ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_RP1_COMMON_H_ + +/** + * @file + * @brief Common definitions for the RP1 pinctrl driver. + */ + +#include +#include + +/** + * Extract the GPIO number from an encoded RP1 pinmux value. + * + * @param pinctrl A pin configuration value defined in devicetree + * @return GPIO number + * + */ +#define RP1_GET_PIN_NUM(pinctrl) (((pinctrl) >> RP1_PIN_NUM_POS) & RP1_PIN_NUM_MASK) + +/** + * Extract the alternate function selector from an encoded RP1 pinmux value. + * + * @param pinctrl A pin configuration value defined in devicetree + * @return GPIO alternate function number + */ +#define RP1_GET_ALT_FUNC(pinctrl) (((pinctrl) >> RP1_ALT_FUNC_POS) & RP1_ALT_FUNC_MASK) + +/** + * @cond INTERNAL_HIDDEN + */ + +/* Initialize one RP1 pinctrl entry from a devicetree property item. */ +#define RASPBERRYPI_RP1_PINCTRL_STATE_PIN_INIT(node_id, prop, idx) \ + { \ + .pin_num = RP1_GET_PIN_NUM(DT_PROP_BY_IDX(node_id, prop, idx)), \ + .alt_func = RP1_GET_ALT_FUNC(DT_PROP_BY_IDX(node_id, prop, idx)), \ + .f_m = DT_PROP(node_id, raspberrypi_f_m), \ + .oe_override = DT_PROP(node_id, raspberrypi_oe_override), \ + .out_override = DT_PROP(node_id, raspberrypi_out_override), \ + .in_override = DT_PROP(node_id, raspberrypi_in_override), \ + .irqmask_edge_low = DT_PROP(node_id, raspberrypi_irqmask_edge_low), \ + .irqmask_edge_high = DT_PROP(node_id, raspberrypi_irqmask_edge_high), \ + .irqmask_level_low = DT_PROP(node_id, raspberrypi_irqmask_level_low), \ + .irqmask_level_high = DT_PROP(node_id, raspberrypi_irqmask_level_high), \ + .irqmask_f_edge_low = DT_PROP(node_id, raspberrypi_irqmask_f_edge_low), \ + .irqmask_f_edge_high = DT_PROP(node_id, raspberrypi_irqmask_f_edge_high), \ + .irqmask_db_level_low = DT_PROP(node_id, raspberrypi_irqmask_db_level_low), \ + .irqmask_db_level_high = DT_PROP(node_id, raspberrypi_irqmask_db_level_high), \ + .irq_override = DT_PROP(node_id, raspberrypi_irq_override), \ + .slew_rate = DT_ENUM_IDX(node_id, slew_rate), \ + .schmitt_enable = DT_PROP(node_id, input_schmitt_enable), \ + .pulldown = DT_PROP(node_id, bias_pull_down), \ + .pullup = DT_PROP(node_id, bias_pull_up), \ + .drive_strength = DT_ENUM_IDX(node_id, drive_strength), \ + .input_enable = DT_PROP(node_id, input_enable), \ + .output_disable = DT_PROP(node_id, output_disable), \ + } + +/* Per-pin RP1 pinctrl settings expanded from devicetree properties. */ +struct raspberrypi_rp1_pinctrl_pinconfig { + uint32_t pin_num: 5; + uint32_t alt_func: 5; + uint32_t f_m: 7; + uint32_t oe_override: 2; + uint32_t out_override: 2; + uint32_t in_override: 2; + uint32_t irqmask_edge_low: 1; + uint32_t irqmask_edge_high: 1; + uint32_t irqmask_level_low: 1; + uint32_t irqmask_level_high: 1; + uint32_t irqmask_f_edge_low: 1; + uint32_t irqmask_f_edge_high: 1; + uint32_t irqmask_db_level_low: 1; + uint32_t irqmask_db_level_high: 1; + uint32_t irq_override: 2; + uint32_t slew_rate: 1; + uint32_t schmitt_enable: 1; + uint32_t pulldown: 1; + uint32_t pullup: 1; + uint32_t drive_strength: 2; + uint32_t input_enable: 1; + uint32_t output_disable: 1; +}; + +/* Apply one RP1 pin configuration entry. */ +int raspberrypi_rp1_pinctrl_configure_pin(const struct raspberrypi_rp1_pinctrl_pinconfig *pin); + +/** + * @endcond + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_RP1_COMMON_H_ */ diff --git a/include/zephyr/dt-bindings/pinctrl/rp1-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/rp1-pinctrl.h new file mode 100644 index 000000000000..50c470a8cb90 --- /dev/null +++ b/include/zephyr/dt-bindings/pinctrl/rp1-pinctrl.h @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Devicetree pin control helpers for Raspberry Pi RP1. + */ + +#ifndef ZEPHYR_DT_BINDINGS_PINCTRL_RP1_PINCTRL_H_ +#define ZEPHYR_DT_BINDINGS_PINCTRL_RP1_PINCTRL_H_ + +/** @cond INTERNAL_HIDDEN */ + +#define RP1_ALT_FUNC_POS 0 +#define RP1_ALT_FUNC_MASK 0x1f + +#define RP1_PIN_NUM_POS 5 +#define RP1_PIN_NUM_MASK 0x1f + +/* RP1 pinmux encoding does not include a bank selector. */ +#define RP1_BANK_POS 0 +#define RP1_BANK_MASK 0x0 + +/** + * @name RP1 Alternate functions + * @brief Common RP1 alternate function selectors. + * + * SoC-specific signal names are defined in the corresponding pin list below. + * Refer to the RP1 datasheet tables describing GPIO alternate functions for + * the source of these selector values. + */ +#define RP1_FUNC_ALT0 0 +#define RP1_FUNC_ALT1 1 +#define RP1_FUNC_ALT2 2 +#define RP1_FUNC_ALT3 3 +#define RP1_FUNC_ALT4 4 +#define RP1_FUNC_ALT5 5 +#define RP1_FUNC_ALT6 6 +#define RP1_FUNC_ALT7 7 +#define RP1_FUNC_ALT8 8 +#define RP1_FUNC_NULL 31 + +/** + * @brief Encode an RP1 pin number and alternate function into one pinmux value. + */ +#define RP1_PINMUX(pin, func) (((pin) << RP1_PIN_NUM_POS) | ((func) << RP1_ALT_FUNC_POS)) + +/** @brief Extract the bank field from an encoded RP1 pinmux value. */ +#define RP1_GET_BANK(pinctrl) (((pinctrl) >> RP1_BANK_POS) & RP1_BANK_MASK) +/** @brief Extract the pin number from an encoded RP1 pinmux value. */ +#define RP1_GET_PIN(pinctrl) (((pinctrl) >> RP1_PIN_NUM_POS) & RP1_PIN_NUM_MASK) +/** @brief Extract the alternate function from an encoded RP1 pinmux value. */ +#define RP1_GET_FUNC(pinctrl) (((pinctrl) >> RP1_ALT_FUNC_POS) & RP1_ALT_FUNC_MASK) + +/** + * @name RP1 GPIO Override + * + * Output, input, and IRQ override values for RP1 GPIO control. + * + * @{ + */ +#define RP1_GPIO_OVERRIDE_NORMAL 0 +#define RP1_GPIO_OVERRIDE_INVERT 1 +#define RP1_GPIO_OVERRIDE_LOW 2 +#define RP1_GPIO_OVERRIDE_HIGH 3 + +/** + * @} + */ + +/** + * @name RP1_ALT0 + * + * ALT0 signal mappings. + * + * @{ + */ + +#define RP1_SPI0_SIO3_P0 RP1_PINMUX(0, RP1_FUNC_ALT0) +#define RP1_SPI0_SIO2_P1 RP1_PINMUX(1, RP1_FUNC_ALT0) +#define RP1_SPI0_CSN3_P2 RP1_PINMUX(2, RP1_FUNC_ALT0) +#define RP1_SPI0_CSN2_P3 RP1_PINMUX(3, RP1_FUNC_ALT0) +#define RP1_GPCLK0_P4 RP1_PINMUX(4, RP1_FUNC_ALT0) +#define RP1_GPCLK1_P5 RP1_PINMUX(5, RP1_FUNC_ALT0) +#define RP1_GPCLK2_P6 RP1_PINMUX(6, RP1_FUNC_ALT0) +#define RP1_SPI0_CSN1_P7 RP1_PINMUX(7, RP1_FUNC_ALT0) +#define RP1_SPI0_CSN0_P8 RP1_PINMUX(8, RP1_FUNC_ALT0) +#define RP1_SPI0_SIO1_P9 RP1_PINMUX(9, RP1_FUNC_ALT0) +#define RP1_SPI0_SIO0_P10 RP1_PINMUX(10, RP1_FUNC_ALT0) +#define RP1_SPI0_SCLK_P11 RP1_PINMUX(11, RP1_FUNC_ALT0) +#define RP1_PWM00_P12 RP1_PINMUX(12, RP1_FUNC_ALT0) +#define RP1_PWM01_P13 RP1_PINMUX(13, RP1_FUNC_ALT0) +#define RP1_PWM02_P14 RP1_PINMUX(14, RP1_FUNC_ALT0) +#define RP1_PWM03_P15 RP1_PINMUX(15, RP1_FUNC_ALT0) +#define RP1_SPI1_CSN2_P16 RP1_PINMUX(16, RP1_FUNC_ALT0) +#define RP1_SPI1_CSN1_P17 RP1_PINMUX(17, RP1_FUNC_ALT0) +#define RP1_SPI1_CSN0_P18 RP1_PINMUX(18, RP1_FUNC_ALT0) +#define RP1_SPI1_SIO1_P19 RP1_PINMUX(19, RP1_FUNC_ALT0) +#define RP1_SPI1_SIO0_P20 RP1_PINMUX(20, RP1_FUNC_ALT0) +#define RP1_SPI1_SCLK_P21 RP1_PINMUX(21, RP1_FUNC_ALT0) +#define RP1_SDIO0_CLK_P22 RP1_PINMUX(22, RP1_FUNC_ALT0) +#define RP1_SDIO0_CMD_P23 RP1_PINMUX(23, RP1_FUNC_ALT0) +#define RP1_SDIO0_DAT0_P24 RP1_PINMUX(24, RP1_FUNC_ALT0) +#define RP1_SDIO0_DAT1_P25 RP1_PINMUX(25, RP1_FUNC_ALT0) +#define RP1_SDIO0_DAT2_P26 RP1_PINMUX(26, RP1_FUNC_ALT0) +#define RP1_SDIO0_DAT3_P27 RP1_PINMUX(27, RP1_FUNC_ALT0) + +/** + * @} + */ + +/** + * @name RP1_ALT2 + * + * ALT2 signal mappings. + * + * @{ + */ +#define RP1_UART1_TX_P0 RP1_PINMUX(0, RP1_FUNC_ALT2) +#define RP1_UART1_RX_P1 RP1_PINMUX(1, RP1_FUNC_ALT2) +#define RP1_UART1_CTS_P2 RP1_PINMUX(2, RP1_FUNC_ALT2) +#define RP1_UART1_RTS_P3 RP1_PINMUX(3, RP1_FUNC_ALT2) +#define RP1_UART2_TX_P4 RP1_PINMUX(4, RP1_FUNC_ALT2) +#define RP1_UART2_RX_P5 RP1_PINMUX(5, RP1_FUNC_ALT2) +#define RP1_UART2_CTS_P6 RP1_PINMUX(6, RP1_FUNC_ALT2) +#define RP1_UART2_RTS_P7 RP1_PINMUX(7, RP1_FUNC_ALT2) +#define RP1_UART3_TX_P8 RP1_PINMUX(8, RP1_FUNC_ALT2) +#define RP1_UART3_RX_P9 RP1_PINMUX(9, RP1_FUNC_ALT2) +#define RP1_UART3_CTS_P10 RP1_PINMUX(10, RP1_FUNC_ALT2) +#define RP1_UART3_RTS_P11 RP1_PINMUX(11, RP1_FUNC_ALT2) +#define RP1_UART4_TX_P12 RP1_PINMUX(12, RP1_FUNC_ALT2) +#define RP1_UART4_RX_P13 RP1_PINMUX(13, RP1_FUNC_ALT2) +#define RP1_UART4_CTS_P14 RP1_PINMUX(14, RP1_FUNC_ALT2) +#define RP1_UART4_RTS_P15 RP1_PINMUX(15, RP1_FUNC_ALT2) +#define RP1_MIPI0_DSI_TE_P16 RP1_PINMUX(16, RP1_FUNC_ALT2) +#define RP1_MIPI1_DSI_TE_P17 RP1_PINMUX(17, RP1_FUNC_ALT2) +#define RP1_I2S0_SCLK_P18 RP1_PINMUX(18, RP1_FUNC_ALT2) +#define RP1_I2S0_WS_P19 RP1_PINMUX(19, RP1_FUNC_ALT2) +#define RP1_I2S0_SDI0_P20 RP1_PINMUX(20, RP1_FUNC_ALT2) +#define RP1_I2S0_SDO0_P21 RP1_PINMUX(21, RP1_FUNC_ALT2) +#define RP1_I2S0_SDI1_P22 RP1_PINMUX(22, RP1_FUNC_ALT2) +#define RP1_I2S0_SDO1_P23 RP1_PINMUX(23, RP1_FUNC_ALT2) +#define RP1_I2S0_SDI2_P24 RP1_PINMUX(24, RP1_FUNC_ALT2) +#define RP1_I2S0_SDO2_P25 RP1_PINMUX(25, RP1_FUNC_ALT2) +#define RP1_I2S0_SDI3_P26 RP1_PINMUX(26, RP1_FUNC_ALT2) +#define RP1_I2S0_SDO3_P27 RP1_PINMUX(27, RP1_FUNC_ALT2) + +/** + * @} + */ + +/** + * @name RP1_ALT1 + * + * ALT1 signal mappings. + * + * @{ + */ +#define RP1_DPI_PCLK_P0 RP1_PINMUX(0, RP1_FUNC_ALT1) +#define RP1_DPI_DE_P1 RP1_PINMUX(1, RP1_FUNC_ALT1) +#define RP1_DPI_VSYNC_P2 RP1_PINMUX(2, RP1_FUNC_ALT1) +#define RP1_DPI_HSYNC_P3 RP1_PINMUX(3, RP1_FUNC_ALT1) +#define RP1_DPI_D0_P4 RP1_PINMUX(4, RP1_FUNC_ALT1) +#define RP1_DPI_D1_P5 RP1_PINMUX(5, RP1_FUNC_ALT1) +#define RP1_DPI_D2_P6 RP1_PINMUX(6, RP1_FUNC_ALT1) +#define RP1_DPI_D3_P7 RP1_PINMUX(7, RP1_FUNC_ALT1) +#define RP1_DPI_D4_P8 RP1_PINMUX(8, RP1_FUNC_ALT1) +#define RP1_DPI_D5_P9 RP1_PINMUX(9, RP1_FUNC_ALT1) +#define RP1_DPI_D6_P10 RP1_PINMUX(10, RP1_FUNC_ALT1) +#define RP1_DPI_D7_P11 RP1_PINMUX(11, RP1_FUNC_ALT1) +#define RP1_DPI_D8_P12 RP1_PINMUX(12, RP1_FUNC_ALT1) +#define RP1_DPI_D9_P13 RP1_PINMUX(13, RP1_FUNC_ALT1) +#define RP1_DPI_D10_P14 RP1_PINMUX(14, RP1_FUNC_ALT1) +#define RP1_DPI_D11_P15 RP1_PINMUX(15, RP1_FUNC_ALT1) +#define RP1_DPI_D12_P16 RP1_PINMUX(16, RP1_FUNC_ALT1) +#define RP1_DPI_D13_P17 RP1_PINMUX(17, RP1_FUNC_ALT1) +#define RP1_DPI_D14_P18 RP1_PINMUX(18, RP1_FUNC_ALT1) +#define RP1_DPI_D15_P19 RP1_PINMUX(19, RP1_FUNC_ALT1) +#define RP1_DPI_D16_P20 RP1_PINMUX(20, RP1_FUNC_ALT1) +#define RP1_DPI_D17_P21 RP1_PINMUX(21, RP1_FUNC_ALT1) +#define RP1_DPI_D18_P22 RP1_PINMUX(22, RP1_FUNC_ALT1) +#define RP1_DPI_D19_P23 RP1_PINMUX(23, RP1_FUNC_ALT1) +#define RP1_DPI_D20_P24 RP1_PINMUX(24, RP1_FUNC_ALT1) +#define RP1_DPI_D21_P25 RP1_PINMUX(25, RP1_FUNC_ALT1) +#define RP1_DPI_D22_P26 RP1_PINMUX(26, RP1_FUNC_ALT1) +#define RP1_DPI_D23_P27 RP1_PINMUX(27, RP1_FUNC_ALT1) + +/** + * @} + */ + +/** + * @name RP1_ALT3 + * + * ALT3 signal mappings. + * + * @{ + */ +#define RP1_I2C0_SDA_P0 RP1_PINMUX(0, RP1_FUNC_ALT3) +#define RP1_I2C0_SCL_P1 RP1_PINMUX(1, RP1_FUNC_ALT3) +#define RP1_I2C1_SDA_P2 RP1_PINMUX(2, RP1_FUNC_ALT3) +#define RP1_I2C1_SCL_P3 RP1_PINMUX(3, RP1_FUNC_ALT3) +#define RP1_I2C2_SDA_P4 RP1_PINMUX(4, RP1_FUNC_ALT3) +#define RP1_I2C2_SCL_P5 RP1_PINMUX(5, RP1_FUNC_ALT3) +#define RP1_I2C3_SDA_P6 RP1_PINMUX(6, RP1_FUNC_ALT3) +#define RP1_I2C3_SCL_P7 RP1_PINMUX(7, RP1_FUNC_ALT3) +#define RP1_I2C0_SDA_P8 RP1_PINMUX(8, RP1_FUNC_ALT3) +#define RP1_I2C0_SCL_P9 RP1_PINMUX(9, RP1_FUNC_ALT3) +#define RP1_I2C1_SDA_P10 RP1_PINMUX(10, RP1_FUNC_ALT3) +#define RP1_I2C1_SCL_P11 RP1_PINMUX(11, RP1_FUNC_ALT3) +#define RP1_I2C2_SDA_P12 RP1_PINMUX(12, RP1_FUNC_ALT3) +#define RP1_I2C2_SCL_P13 RP1_PINMUX(13, RP1_FUNC_ALT3) +#define RP1_I2C3_SDA_P14 RP1_PINMUX(14, RP1_FUNC_ALT3) +#define RP1_I2C3_SCL_P15 RP1_PINMUX(15, RP1_FUNC_ALT3) +#define RP1_PWM02_P18 RP1_PINMUX(18, RP1_FUNC_ALT3) +#define RP1_PWM03_P19 RP1_PINMUX(19, RP1_FUNC_ALT3) +#define RP1_GPCLK0_P20 RP1_PINMUX(20, RP1_FUNC_ALT3) +#define RP1_GPCLK1_P21 RP1_PINMUX(21, RP1_FUNC_ALT3) +#define RP1_I2C3_SDA_P22 RP1_PINMUX(22, RP1_FUNC_ALT3) +#define RP1_I2C3_SCL_P23 RP1_PINMUX(23, RP1_FUNC_ALT3) +#define RP1_I2S1_SDI2_P24 RP1_PINMUX(24, RP1_FUNC_ALT3) +#define RP1_AUDIO_IN_CLK_P25 RP1_PINMUX(25, RP1_FUNC_ALT3) +#define RP1_AUDIO_IN_DAT0_P26 RP1_PINMUX(26, RP1_FUNC_ALT3) +#define RP1_AUDIO_IN_DAT1_P27 RP1_PINMUX(27, RP1_FUNC_ALT3) + +/** + * @} + */ + +/** + * @name RP1_ALT4 + * + * ALT4 signal mappings. + * + * @{ + */ +#define RP1_UART0_IR_RX_P2 RP1_PINMUX(2, RP1_FUNC_ALT4) +#define RP1_UART0_IR_TX_P3 RP1_PINMUX(3, RP1_FUNC_ALT4) +#define RP1_UART0_RI_P4 RP1_PINMUX(4, RP1_FUNC_ALT4) +#define RP1_UART0_DTR_P5 RP1_PINMUX(5, RP1_FUNC_ALT4) +#define RP1_UART0_DCD_P6 RP1_PINMUX(6, RP1_FUNC_ALT4) +#define RP1_UART0_DSR_P7 RP1_PINMUX(7, RP1_FUNC_ALT4) +#define RP1_AUDIO_OUT_L_P12 RP1_PINMUX(12, RP1_FUNC_ALT4) +#define RP1_AUDIO_OUT_R_P13 RP1_PINMUX(13, RP1_FUNC_ALT4) +#define RP1_UART0_TX_P14 RP1_PINMUX(14, RP1_FUNC_ALT4) +#define RP1_UART0_RX_P15 RP1_PINMUX(15, RP1_FUNC_ALT4) +#define RP1_UART0_CTS_P16 RP1_PINMUX(16, RP1_FUNC_ALT4) +#define RP1_UART0_RTS_P17 RP1_PINMUX(17, RP1_FUNC_ALT4) +#define RP1_I2S1_SCLK_P18 RP1_PINMUX(18, RP1_FUNC_ALT4) +#define RP1_I2S1_WS_P19 RP1_PINMUX(19, RP1_FUNC_ALT4) +#define RP1_I2S1_SDI0_P20 RP1_PINMUX(20, RP1_FUNC_ALT4) +#define RP1_I2S1_SDO0_P21 RP1_PINMUX(21, RP1_FUNC_ALT4) +#define RP1_I2S1_SDI1_P22 RP1_PINMUX(22, RP1_FUNC_ALT4) +#define RP1_I2S1_SDO1_P23 RP1_PINMUX(23, RP1_FUNC_ALT4) +#define RP1_I2S1_SDO2_P25 RP1_PINMUX(25, RP1_FUNC_ALT4) +#define RP1_I2S1_SDI3_P26 RP1_PINMUX(26, RP1_FUNC_ALT4) +#define RP1_I2S1_SDO3_P27 RP1_PINMUX(27, RP1_FUNC_ALT4) + +/** + * @} + */ + +/** + * @name RP1_ALT5 + * + * ALT5 signal mappings. + * + * @{ + */ +#define RP1_SYS_RIO0_P0 RP1_PINMUX(0, RP1_FUNC_ALT5) +#define RP1_SYS_RIO1_P1 RP1_PINMUX(1, RP1_FUNC_ALT5) +#define RP1_SYS_RIO2_P2 RP1_PINMUX(2, RP1_FUNC_ALT5) +#define RP1_SYS_RIO3_P3 RP1_PINMUX(3, RP1_FUNC_ALT5) +#define RP1_SYS_RIO4_P4 RP1_PINMUX(4, RP1_FUNC_ALT5) +#define RP1_SYS_RIO5_P5 RP1_PINMUX(5, RP1_FUNC_ALT5) +#define RP1_SYS_RIO6_P6 RP1_PINMUX(6, RP1_FUNC_ALT5) +#define RP1_SYS_RIO7_P7 RP1_PINMUX(7, RP1_FUNC_ALT5) +#define RP1_SYS_RIO8_P8 RP1_PINMUX(8, RP1_FUNC_ALT5) +#define RP1_SYS_RIO9_P9 RP1_PINMUX(9, RP1_FUNC_ALT5) +#define RP1_SYS_RIO10_P10 RP1_PINMUX(10, RP1_FUNC_ALT5) +#define RP1_SYS_RIO11_P11 RP1_PINMUX(11, RP1_FUNC_ALT5) +#define RP1_SYS_RIO12_P12 RP1_PINMUX(12, RP1_FUNC_ALT5) +#define RP1_SYS_RIO13_P13 RP1_PINMUX(13, RP1_FUNC_ALT5) +#define RP1_SYS_RIO14_P14 RP1_PINMUX(14, RP1_FUNC_ALT5) +#define RP1_SYS_RIO15_P15 RP1_PINMUX(15, RP1_FUNC_ALT5) +#define RP1_SYS_RIO16_P16 RP1_PINMUX(16, RP1_FUNC_ALT5) +#define RP1_SYS_RIO17_P17 RP1_PINMUX(17, RP1_FUNC_ALT5) +#define RP1_SYS_RIO18_P18 RP1_PINMUX(18, RP1_FUNC_ALT5) +#define RP1_SYS_RIO19_P19 RP1_PINMUX(19, RP1_FUNC_ALT5) +#define RP1_SYS_RIO20_P20 RP1_PINMUX(20, RP1_FUNC_ALT5) +#define RP1_SYS_RIO21_P21 RP1_PINMUX(21, RP1_FUNC_ALT5) +#define RP1_SYS_RIO22_P22 RP1_PINMUX(22, RP1_FUNC_ALT5) +#define RP1_SYS_RIO23_P23 RP1_PINMUX(23, RP1_FUNC_ALT5) +#define RP1_SYS_RIO24_P24 RP1_PINMUX(24, RP1_FUNC_ALT5) +#define RP1_SYS_RIO25_P25 RP1_PINMUX(25, RP1_FUNC_ALT5) +#define RP1_SYS_RIO26_P26 RP1_PINMUX(26, RP1_FUNC_ALT5) +#define RP1_SYS_RIO27_P27 RP1_PINMUX(27, RP1_FUNC_ALT5) + +/** + * @} + */ + +/** + * @name RP1_ALT6 + * + * ALT6 signal mappings. + * + * @{ + */ +#define RP1_PROC_RIO0_P0 RP1_PINMUX(0, RP1_FUNC_ALT6) +#define RP1_PROC_RIO1_P1 RP1_PINMUX(1, RP1_FUNC_ALT6) +#define RP1_PROC_RIO2_P2 RP1_PINMUX(2, RP1_FUNC_ALT6) +#define RP1_PROC_RIO3_P3 RP1_PINMUX(3, RP1_FUNC_ALT6) +#define RP1_PROC_RIO4_P4 RP1_PINMUX(4, RP1_FUNC_ALT6) +#define RP1_PROC_RIO5_P5 RP1_PINMUX(5, RP1_FUNC_ALT6) +#define RP1_PROC_RIO6_P6 RP1_PINMUX(6, RP1_FUNC_ALT6) +#define RP1_PROC_RIO7_P7 RP1_PINMUX(7, RP1_FUNC_ALT6) +#define RP1_PROC_RIO8_P8 RP1_PINMUX(8, RP1_FUNC_ALT6) +#define RP1_PROC_RIO9_P9 RP1_PINMUX(9, RP1_FUNC_ALT6) +#define RP1_PROC_RIO10_P10 RP1_PINMUX(10, RP1_FUNC_ALT6) +#define RP1_PROC_RIO11_P11 RP1_PINMUX(11, RP1_FUNC_ALT6) +#define RP1_PROC_RIO12_P12 RP1_PINMUX(12, RP1_FUNC_ALT6) +#define RP1_PROC_RIO13_P13 RP1_PINMUX(13, RP1_FUNC_ALT6) +#define RP1_PROC_RIO14_P14 RP1_PINMUX(14, RP1_FUNC_ALT6) +#define RP1_PROC_RIO15_P15 RP1_PINMUX(15, RP1_FUNC_ALT6) +#define RP1_PROC_RIO16_P16 RP1_PINMUX(16, RP1_FUNC_ALT6) +#define RP1_PROC_RIO17_P17 RP1_PINMUX(17, RP1_FUNC_ALT6) +#define RP1_PROC_RIO18_P18 RP1_PINMUX(18, RP1_FUNC_ALT6) +#define RP1_PROC_RIO19_P19 RP1_PINMUX(19, RP1_FUNC_ALT6) +#define RP1_PROC_RIO20_P20 RP1_PINMUX(20, RP1_FUNC_ALT6) +#define RP1_PROC_RIO21_P21 RP1_PINMUX(21, RP1_FUNC_ALT6) +#define RP1_PROC_RIO22_P22 RP1_PINMUX(22, RP1_FUNC_ALT6) +#define RP1_PROC_RIO23_P23 RP1_PINMUX(23, RP1_FUNC_ALT6) +#define RP1_PROC_RIO24_P24 RP1_PINMUX(24, RP1_FUNC_ALT6) +#define RP1_PROC_RIO25_P25 RP1_PINMUX(25, RP1_FUNC_ALT6) +#define RP1_PROC_RIO26_P26 RP1_PINMUX(26, RP1_FUNC_ALT6) +#define RP1_PROC_RIO27_P27 RP1_PINMUX(27, RP1_FUNC_ALT6) + +/** + * @} + */ + +/** + * @name RP1_ALT7 + * + * ALT7 signal mappings. + * + * @{ + */ +#define RP1_PIO0_P0 RP1_PINMUX(0, RP1_FUNC_ALT7) +#define RP1_PIO1_P1 RP1_PINMUX(1, RP1_FUNC_ALT7) +#define RP1_PIO2_P2 RP1_PINMUX(2, RP1_FUNC_ALT7) +#define RP1_PIO3_P3 RP1_PINMUX(3, RP1_FUNC_ALT7) +#define RP1_PIO4_P4 RP1_PINMUX(4, RP1_FUNC_ALT7) +#define RP1_PIO5_P5 RP1_PINMUX(5, RP1_FUNC_ALT7) +#define RP1_PIO6_P6 RP1_PINMUX(6, RP1_FUNC_ALT7) +#define RP1_PIO7_P7 RP1_PINMUX(7, RP1_FUNC_ALT7) +#define RP1_PIO8_P8 RP1_PINMUX(8, RP1_FUNC_ALT7) +#define RP1_PIO9_P9 RP1_PINMUX(9, RP1_FUNC_ALT7) +#define RP1_PIO10_P10 RP1_PINMUX(10, RP1_FUNC_ALT7) +#define RP1_PIO11_P11 RP1_PINMUX(11, RP1_FUNC_ALT7) +#define RP1_PIO12_P12 RP1_PINMUX(12, RP1_FUNC_ALT7) +#define RP1_PIO13_P13 RP1_PINMUX(13, RP1_FUNC_ALT7) +#define RP1_PIO14_P14 RP1_PINMUX(14, RP1_FUNC_ALT7) +#define RP1_PIO15_P15 RP1_PINMUX(15, RP1_FUNC_ALT7) +#define RP1_PIO16_P16 RP1_PINMUX(16, RP1_FUNC_ALT7) +#define RP1_PIO17_P17 RP1_PINMUX(17, RP1_FUNC_ALT7) +#define RP1_PIO18_P18 RP1_PINMUX(18, RP1_FUNC_ALT7) +#define RP1_PIO19_P19 RP1_PINMUX(19, RP1_FUNC_ALT7) +#define RP1_PIO20_P20 RP1_PINMUX(20, RP1_FUNC_ALT7) +#define RP1_PIO21_P21 RP1_PINMUX(21, RP1_FUNC_ALT7) +#define RP1_PIO22_P22 RP1_PINMUX(22, RP1_FUNC_ALT7) +#define RP1_PIO23_P23 RP1_PINMUX(23, RP1_FUNC_ALT7) +#define RP1_PIO24_P24 RP1_PINMUX(24, RP1_FUNC_ALT7) +#define RP1_PIO25_P25 RP1_PINMUX(25, RP1_FUNC_ALT7) +#define RP1_PIO26_P26 RP1_PINMUX(26, RP1_FUNC_ALT7) +#define RP1_PIO27_P27 RP1_PINMUX(27, RP1_FUNC_ALT7) + +/** + * @} + */ + +/** + * @name RP1_ALT8 + * + * ALT8 signal mappings. + * + * @{ + */ +#define RP1_SPI2_CSN0_P0 RP1_PINMUX(0, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN1_P1 RP1_PINMUX(1, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN2_P2 RP1_PINMUX(2, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN3_P3 RP1_PINMUX(3, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN4_P4 RP1_PINMUX(4, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN5_P5 RP1_PINMUX(5, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN6_P6 RP1_PINMUX(6, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN7_P7 RP1_PINMUX(7, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN8_P8 RP1_PINMUX(8, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN9_P9 RP1_PINMUX(9, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN10_P10 RP1_PINMUX(10, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN11_P11 RP1_PINMUX(11, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN12_P12 RP1_PINMUX(12, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN13_P13 RP1_PINMUX(13, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN14_P14 RP1_PINMUX(14, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN15_P15 RP1_PINMUX(15, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN16_P16 RP1_PINMUX(16, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN17_P17 RP1_PINMUX(17, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN18_P18 RP1_PINMUX(18, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN19_P19 RP1_PINMUX(19, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN20_P20 RP1_PINMUX(20, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN21_P21 RP1_PINMUX(21, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN22_P22 RP1_PINMUX(22, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN23_P23 RP1_PINMUX(23, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN24_P24 RP1_PINMUX(24, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN25_P25 RP1_PINMUX(25, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN26_P26 RP1_PINMUX(26, RP1_FUNC_ALT8) +#define RP1_SPI2_CSN27_P27 RP1_PINMUX(27, RP1_FUNC_ALT8) + +/** + * @} + */ + +/** + * @endcond + */ + +#endif /* ZEPHYR_DT_BINDINGS_PINCTRL_RP1_PINCTRL_H_ */ From 989e6e35b7d26b7bd9bc8b649e36f37a37ef55bd Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Tue, 16 Dec 2025 10:50:34 +0900 Subject: [PATCH 094/455] drivers: pinctrl: Add a bcm2712 pinctrl stub driver Enabling the rp1 pinctrl also requires the bcm2712 pinctrl, so add a minimal stub. This does not perform any processing, but preserves the register settings at reset. Signed-off-by: TOKITA Hiroshi --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.bcm2712 | 9 +++++ drivers/pinctrl/pinctrl_bcm2712.c | 14 +++++++ .../pinctrl/brcm,bcm2712-pinctrl.yaml | 20 ++++++++++ .../drivers/pinctrl/pinctrl_bcm2712_common.h | 40 +++++++++++++++++++ .../dt-bindings/pinctrl/bcm2712-pinctrl.h | 15 +++++++ 7 files changed, 100 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.bcm2712 create mode 100644 drivers/pinctrl/pinctrl_bcm2712.c create mode 100644 dts/bindings/pinctrl/brcm,bcm2712-pinctrl.yaml create mode 100644 include/zephyr/drivers/pinctrl/pinctrl_bcm2712_common.h create mode 100644 include/zephyr/dt-bindings/pinctrl/bcm2712-pinctrl.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index 5fd3370ed1c5..a1977f50877c 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -17,6 +17,7 @@ zephyr_library_sources_ifdef(CONFIG_PINCTRL_ARM_MPS4 pinctrl_arm_mps4.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_ARM_V2M_BEETLE pinctrl_arm_v2m_beetle.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_ARM_V2M_MUSCA_B1 pinctrl_arm_v2m_musca_b1.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_BCM2711 pinctrl_bcm2711.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_BCM2712 pinctrl_bcm2712.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_BEE pinctrl_bee.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_CC13XX_CC26XX pinctrl_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_CC23X0 pinctrl_cc23x0.c) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 70aee442afd8..d060ccffaa65 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -50,6 +50,7 @@ source "drivers/pinctrl/Kconfig.arm_v2m_beetle" source "drivers/pinctrl/Kconfig.arm_v2m_musca_b1" source "drivers/pinctrl/Kconfig.b91" source "drivers/pinctrl/Kconfig.bcm2711" +source "drivers/pinctrl/Kconfig.bcm2712" source "drivers/pinctrl/Kconfig.bee" source "drivers/pinctrl/Kconfig.bflb" source "drivers/pinctrl/Kconfig.cc13xx_cc26xx" diff --git a/drivers/pinctrl/Kconfig.bcm2712 b/drivers/pinctrl/Kconfig.bcm2712 new file mode 100644 index 000000000000..137770786cd3 --- /dev/null +++ b/drivers/pinctrl/Kconfig.bcm2712 @@ -0,0 +1,9 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config PINCTRL_BCM2712 + bool "Broadcom BCM2712 pin controller driver" + default y + depends on DT_HAS_BRCM_BCM2712_PINCTRL_ENABLED + help + Enable pin controller driver for the BCM2712 SoC pinmux block. diff --git a/drivers/pinctrl/pinctrl_bcm2712.c b/drivers/pinctrl/pinctrl_bcm2712.c new file mode 100644 index 000000000000..d0309b0eb8ea --- /dev/null +++ b/drivers/pinctrl/pinctrl_bcm2712.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int brcm_bcm2712_pinctrl_configure_pin(const struct brcm_bcm2712_pinctrl_pinconfig *pin) +{ + ARG_UNUSED(pin); + return 0; +} diff --git a/dts/bindings/pinctrl/brcm,bcm2712-pinctrl.yaml b/dts/bindings/pinctrl/brcm,bcm2712-pinctrl.yaml new file mode 100644 index 000000000000..91cfcfbf062d --- /dev/null +++ b/dts/bindings/pinctrl/brcm,bcm2712-pinctrl.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +title: Broadcom BCM2712 Pin Controller + +description: | + BCM2712 SoC pinmux and pad control. Each controller bank manages a group + of GPIO pins and exposes the function selection and pad control registers + for those pins. + +compatible: "brcm,bcm2712-pinctrl" + +include: base.yaml + +properties: + reg: + required: true + +child-binding: + description: BCM2712 pin controller pin group. diff --git a/include/zephyr/drivers/pinctrl/pinctrl_bcm2712_common.h b/include/zephyr/drivers/pinctrl/pinctrl_bcm2712_common.h new file mode 100644 index 000000000000..35a89ccc71bc --- /dev/null +++ b/include/zephyr/drivers/pinctrl/pinctrl_bcm2712_common.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Common definitions for the Broadcom BCM2712 pinctrl driver. + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_BCM2712_COMMON_H_ +#define ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_BCM2712_COMMON_H_ + +#include +#include + +/** + * @cond INTERNAL_HIDDEN + */ + +/* Initialize one BCM2712 pinctrl entry from a devicetree property item. */ +#define BRCM_BCM2712_PINCTRL_STATE_PIN_INIT(node_id, prop, idx) \ + { \ + .unused = 0, \ + } + +/* Per-pin configuration container for BCM2712 pinctrl state data. */ +struct brcm_bcm2712_pinctrl_pinconfig { + uint8_t unused; +}; + +/* Apply one BCM2712 pin configuration entry. */ +int brcm_bcm2712_pinctrl_configure_pin(const struct brcm_bcm2712_pinctrl_pinconfig *pin); + +/** + * @endcond + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_PINCTRL_PINCTRL_BCM2712_COMMON_H_ */ diff --git a/include/zephyr/dt-bindings/pinctrl/bcm2712-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/bcm2712-pinctrl.h new file mode 100644 index 000000000000..034aa50b0bb5 --- /dev/null +++ b/include/zephyr/dt-bindings/pinctrl/bcm2712-pinctrl.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Devicetree pin control helpers for Broadcom BCM2712. + */ + +#ifndef ZEPHYR_DT_BINDINGS_PINCTRL_BCM2712_PINCTRL_H_ +#define ZEPHYR_DT_BINDINGS_PINCTRL_BCM2712_PINCTRL_H_ + +#endif /* ZEPHYR_DT_BINDINGS_PINCTRL_BCM2712_PINCTRL_H_ */ From 74fee4cc4cc347731c201e121277d0dff12edd5f Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Wed, 31 Dec 2025 11:42:46 +0900 Subject: [PATCH 095/455] soc: brcm: bcm2712: Enable pinctrl driver For the rpi5, you will need to use both the BCM2712 the RP1 pinctrl driver. The process for selecting which to use is done in `soc/brcm/bcm2712/pinctrl_soc.c`. Signed-off-by: TOKITA Hiroshi --- boards/raspberrypi/rpi_5/rpi_5-pinctrl.dtsi | 12 ++++++ boards/raspberrypi/rpi_5/rpi_5_bcm2712.dts | 3 ++ dts/arm64/broadcom/bcm2712.dtsi | 21 +++++++++-- soc/brcm/bcm2712/CMakeLists.txt | 2 + soc/brcm/bcm2712/pinctrl_soc.c | 41 +++++++++++++++++++++ soc/brcm/bcm2712/pinctrl_soc.h | 41 +++++++++++++++++++++ 6 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 boards/raspberrypi/rpi_5/rpi_5-pinctrl.dtsi create mode 100644 soc/brcm/bcm2712/pinctrl_soc.c create mode 100644 soc/brcm/bcm2712/pinctrl_soc.h diff --git a/boards/raspberrypi/rpi_5/rpi_5-pinctrl.dtsi b/boards/raspberrypi/rpi_5/rpi_5-pinctrl.dtsi new file mode 100644 index 000000000000..f188cac68f86 --- /dev/null +++ b/boards/raspberrypi/rpi_5/rpi_5-pinctrl.dtsi @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +&pinctrl { + /* Not yet implemented BCM2712 side pinctrl. Put a stub definition at this time. */ + uart10_default: uart10_default {}; +}; diff --git a/boards/raspberrypi/rpi_5/rpi_5_bcm2712.dts b/boards/raspberrypi/rpi_5/rpi_5_bcm2712.dts index 339adba011c8..e0d22d9d3958 100644 --- a/boards/raspberrypi/rpi_5/rpi_5_bcm2712.dts +++ b/boards/raspberrypi/rpi_5/rpi_5_bcm2712.dts @@ -8,6 +8,7 @@ #include #include +#include "rpi_5-pinctrl.dtsi" / { compatible = "raspberrypi,5-model-b", "brcm,bcm2712"; @@ -44,6 +45,8 @@ &uart10 { status = "okay"; current-speed = <115200>; + pinctrl-0 = <&uart10_default>; + pinctrl-names = "default"; }; &gpio0_0 { diff --git a/dts/arm64/broadcom/bcm2712.dtsi b/dts/arm64/broadcom/bcm2712.dtsi index 1b151a1da1ae..368c1ff221fa 100644 --- a/dts/arm64/broadcom/bcm2712.dtsi +++ b/dts/arm64/broadcom/bcm2712.dtsi @@ -94,6 +94,12 @@ clocks = <&clk_uart>; status = "disabled"; }; + + pinctrl: pinctrl@107d504100 { + compatible = "brcm,bcm2712-pinctrl"; + reg = <0x10 0x7d504100 0x200>; + status = "okay"; + }; }; clocks { @@ -139,11 +145,20 @@ #address-cells = <2>; #size-cells = <1>; - gpio0: gpio@1f000d0000 { - compatible = "simple-bus"; - reg = <0x1f 0xd0000 0x30000>; + rp1_uartclk: uartclk { + compatible = "fixed-clock"; + clock-frequency = <50000000>; + #clock-cells = <0>; + }; + + rp1_pinctrl: pinctrl@1f000d0000 { + compatible = "raspberrypi,rp1-pinctrl"; + reg = <0x1f 0xd0000 0x128>, + <0x1f 0xf0004 0x70>; + reg-names = "gpio", "pads"; #address-cells = <1>; #size-cells = <0>; + status = "okay"; gpio0_0: gpio@0 { compatible = "raspberrypi,rp1-gpio"; diff --git a/soc/brcm/bcm2712/CMakeLists.txt b/soc/brcm/bcm2712/CMakeLists.txt index fb8e677f1527..f2fee9b9c013 100644 --- a/soc/brcm/bcm2712/CMakeLists.txt +++ b/soc/brcm/bcm2712/CMakeLists.txt @@ -1,3 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 +zephyr_sources_ifdef(CONFIG_PINCTRL pinctrl_soc.c) + set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm64/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/brcm/bcm2712/pinctrl_soc.c b/soc/brcm/bcm2712/pinctrl_soc.c new file mode 100644 index 000000000000..ffe0f0b63b39 --- /dev/null +++ b/soc/brcm/bcm2712/pinctrl_soc.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "pinctrl_soc.h" + +LOG_MODULE_REGISTER(brcm_bcm2712, CONFIG_SOC_LOG_LEVEL); + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg) +{ + int ret = 0; + + ARG_UNUSED(reg); + + for (uint8_t i = 0U; i < pin_cnt; i++) { + if (pins[i].type == DT_DEP_ORD(DT_NODELABEL(pinctrl))) { + ret = brcm_bcm2712_pinctrl_configure_pin(&pins[i].brcm_bcm2712_pinctrl); + + if (ret != 0) { + break; + } + } else if (pins[i].type == DT_DEP_ORD(DT_NODELABEL(rp1_pinctrl))) { + ret = raspberrypi_rp1_pinctrl_configure_pin( + &pins[i].raspberrypi_rp1_pinctrl); + + if (ret != 0) { + break; + } + } else { + LOG_ERR("Unsupported pin controller type %d", pins[i].type); + return -ENOTSUP; + } + } + + return ret; +} diff --git a/soc/brcm/bcm2712/pinctrl_soc.h b/soc/brcm/bcm2712/pinctrl_soc.h new file mode 100644 index 000000000000..5eed9af8407e --- /dev/null +++ b/soc/brcm/bcm2712/pinctrl_soc.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_BRCM_BCM2712_PINCTRL_SOC_H_ +#define ZEPHYR_SOC_BRCM_BCM2712_PINCTRL_SOC_H_ + +#include +#include +#include +#include +#include + +struct bcm2712_composite_pinctrl_soc_pin { + int type; + union { + struct brcm_bcm2712_pinctrl_pinconfig brcm_bcm2712_pinctrl; + struct raspberrypi_rp1_pinctrl_pinconfig raspberrypi_rp1_pinctrl; + }; +}; + +typedef struct bcm2712_composite_pinctrl_soc_pin pinctrl_soc_pin_t; + +#define Z_BCM2712_PINCTRL_STATE_PIN_INIT(node_id, prop, idx) \ + UTIL_CAT(DT_BINDING_COMPAT_UPPER_TOKEN(DT_GPARENT(node_id)), _STATE_PIN_INIT)(node_id, \ + prop, idx) + +#define Z_PINCTRL_STATE_PIN_INIT(node_id, prop, idx) \ + { \ + .type = DT_DEP_ORD(DT_GPARENT(node_id)), \ + .DT_BINDING_COMPAT_TOKEN(DT_GPARENT(node_id)) = \ + Z_BCM2712_PINCTRL_STATE_PIN_INIT(node_id, prop, idx), \ + }, + +#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \ + {DT_FOREACH_CHILD_VARGS(DT_PHANDLE(node_id, prop), DT_FOREACH_PROP_ELEM, pinmux, \ + Z_PINCTRL_STATE_PIN_INIT)} + +#endif /* ZEPHYR_SOC_BRCM_BCM2712_PINCTRL_SOC_H_ */ From bfe429144bcac5d617e38c01e946d21f8d8b072d Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Wed, 31 Dec 2025 11:44:22 +0900 Subject: [PATCH 096/455] dts: arm64: broadcom: bcm2712: Add RP1 UART configuration Add configuration for RP1 UART peripherals. Signed-off-by: TOKITA Hiroshi --- .../raspberrypi/rpi_5/rpi_5_bcm2712_defconfig | 1 + dts/arm64/broadcom/bcm2712.dtsi | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/boards/raspberrypi/rpi_5/rpi_5_bcm2712_defconfig b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_defconfig index fc82b7f50d27..b96293fa2090 100644 --- a/boards/raspberrypi/rpi_5/rpi_5_bcm2712_defconfig +++ b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_defconfig @@ -8,5 +8,6 @@ CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME=y CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y +CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_PCIE=y diff --git a/dts/arm64/broadcom/bcm2712.dtsi b/dts/arm64/broadcom/bcm2712.dtsi index 368c1ff221fa..0139d78573bf 100644 --- a/dts/arm64/broadcom/bcm2712.dtsi +++ b/dts/arm64/broadcom/bcm2712.dtsi @@ -187,6 +187,46 @@ status = "disabled"; }; }; + + rp1_uart0: serial@1f00030000 { + compatible = "arm,pl011"; + reg = <0x1f 0x30000 0x100>; + clocks = <&rp1_uartclk>; + clock-names = "uartclk"; + status = "disabled"; + }; + + rp1_uart1: serial@1f00034000 { + compatible = "arm,pl011"; + reg = <0x1f 0x34000 0x100>; + clocks = <&rp1_uartclk>; + clock-names = "uartclk"; + status = "disabled"; + }; + + rp1_uart2: serial@1f00038000 { + compatible = "arm,pl011"; + reg = <0x1f 0x38000 0x100>; + clocks = <&rp1_uartclk>; + clock-names = "uartclk"; + status = "disabled"; + }; + + rp1_uart3: serial@1f0003c000 { + compatible = "arm,pl011"; + reg = <0x1f 0x3c000 0x100>; + clocks = <&rp1_uartclk>; + clock-names = "uartclk"; + status = "disabled"; + }; + + rp1_uart4: serial@1f00040000 { + compatible = "arm,pl011"; + reg = <0x1f 0x40000 0x100>; + clocks = <&rp1_uartclk>; + clock-names = "uartclk"; + status = "disabled"; + }; }; }; }; From 48826b98a94bd2c0ec6f5241ab31e67fc9af5258 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Wed, 31 Dec 2025 11:21:06 +0900 Subject: [PATCH 097/455] boards: raspberrypi: rpi5: Add configuration for using SWD connector Add a configuration to use UART0 for the console and make able to use the connector shared by SWD and UART10 for SWD purposes. Signed-off-by: TOKITA Hiroshi --- boards/raspberrypi/rpi_5/board.yml | 2 ++ .../rpi_5/rpi_5_bcm2712_swd-pinctrl.dtsi | 22 ++++++++++++++++ .../raspberrypi/rpi_5/rpi_5_bcm2712_swd.dts | 26 +++++++++++++++++++ .../raspberrypi/rpi_5/rpi_5_bcm2712_swd.yaml | 7 +++++ .../rpi_5/rpi_5_bcm2712_swd_defconfig | 13 ++++++++++ 5 files changed, 70 insertions(+) create mode 100644 boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd-pinctrl.dtsi create mode 100644 boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.dts create mode 100644 boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.yaml create mode 100644 boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd_defconfig diff --git a/boards/raspberrypi/rpi_5/board.yml b/boards/raspberrypi/rpi_5/board.yml index 2c90e5c2db06..04a65e2daf55 100644 --- a/boards/raspberrypi/rpi_5/board.yml +++ b/boards/raspberrypi/rpi_5/board.yml @@ -4,3 +4,5 @@ board: vendor: raspberrypi socs: - name: bcm2712 + variants: + - name: swd diff --git a/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd-pinctrl.dtsi b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd-pinctrl.dtsi new file mode 100644 index 000000000000..ee260b1eca56 --- /dev/null +++ b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd-pinctrl.dtsi @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "rpi_5-pinctrl.dtsi" + +&rp1_pinctrl { + rp1_uart0_default: rp1_uart0_default { + group1 { + pinmux = ; + }; + + group2 { + pinmux = ; + input-enable; + input-schmitt-enable; + bias-pull-up; + }; + }; +}; diff --git a/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.dts b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.dts new file mode 100644 index 000000000000..bcb344f2d915 --- /dev/null +++ b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.dts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "rpi_5_bcm2712.dts" +#include "rpi_5_bcm2712_swd-pinctrl.dtsi" + +/ { + chosen { + zephyr,console = &rp1_uart0; + zephyr,shell-uart = &rp1_uart0; + }; +}; + +&rp1_uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&rp1_uart0_default>; + pinctrl-names = "default"; +}; + +&uart10 { + status = "disabled"; +}; diff --git a/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.yaml b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.yaml new file mode 100644 index 000000000000..6a70feb5e66f --- /dev/null +++ b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd.yaml @@ -0,0 +1,7 @@ +identifier: rpi_5/bcm2712/swd +name: Raspberry Pi 5 SWD-enabled +type: mcu +arch: arm64 +toolchain: + - zephyr + - cross-compile diff --git a/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd_defconfig b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd_defconfig new file mode 100644 index 000000000000..a0ae7337a7e4 --- /dev/null +++ b/boards/raspberrypi/rpi_5/rpi_5_bcm2712_swd_defconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ARM64_VA_BITS_40=y +CONFIG_ARM64_PA_BITS_40=y +CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME=y + +# Enable serial console. +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n + +CONFIG_PCIE=y From 0da7023a2453708fa8e12ecd76b3bbb7efa98623 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Mon, 9 Feb 2026 09:50:20 -0300 Subject: [PATCH 098/455] drivers: wifi: esp32: Add PM support Add power management support for Wi-Fi driver. Signed-off-by: Raffael Rostagno --- drivers/wifi/esp32/src/esp_wifi_drv.c | 41 ++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/wifi/esp32/src/esp_wifi_drv.c b/drivers/wifi/esp32/src/esp_wifi_drv.c index 00d32a588492..2d82ca04ced1 100644 --- a/drivers/wifi/esp32/src/esp_wifi_drv.c +++ b/drivers/wifi/esp32/src/esp_wifi_drv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2020-2026 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,7 @@ LOG_MODULE_REGISTER(esp32_wifi, CONFIG_WIFI_LOG_LEVEL); #include #endif #include +#include #include #include #include @@ -1837,6 +1838,32 @@ static int esp32_wifi_reset_stats(const struct device *dev __unused, } #endif +static int esp32_wifi_pm_action(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_RESUME: + break; + + case PM_DEVICE_ACTION_SUSPEND: +#if defined(SOC_WIFI_HW_TSF) + if (esp_wifi_internal_is_tsf_active()) { + /* Reject sleep while TSF is active (timing critical) */ + return -EBUSY; + } +#endif + break; + + case PM_DEVICE_ACTION_TURN_ON: + case PM_DEVICE_ACTION_TURN_OFF: + break; + + default: + return -ENOTSUP; + } + + return 0; +} + static int esp32_wifi_dev_init(const struct device *dev) { #if CONFIG_SOC_SERIES_ESP32S2 || CONFIG_SOC_SERIES_ESP32C3 @@ -1877,7 +1904,7 @@ static int esp32_wifi_dev_init(const struct device *dev) return -EIO; } - return 0; + return pm_device_driver_init(dev, esp32_wifi_pm_action); } static int esp32_wifi_set_config(const struct device *dev __unused, @@ -1940,11 +1967,11 @@ static const struct net_wifi_mgmt_offload esp32_api = { .wifi_mgmt_api = &esp32_wifi_mgmt, }; -NET_DEVICE_DT_INST_DEFINE(0, - esp32_wifi_dev_init, NULL, - &esp32_data, NULL, CONFIG_WIFI_INIT_PRIORITY, - &esp32_api, ETHERNET_L2, - NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU); +PM_DEVICE_DT_INST_DEFINE(0, esp32_wifi_pm_action); + +NET_DEVICE_DT_INST_DEFINE(0, esp32_wifi_dev_init, PM_DEVICE_DT_INST_GET(0), &esp32_data, NULL, + CONFIG_WIFI_INIT_PRIORITY, &esp32_api, ETHERNET_L2, + NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU); #if defined(CONFIG_ESP32_WIFI_AP_STA_MODE) NET_DEVICE_DT_INST_ADD_IFACE(0, ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU, 1); From 5a9929e30db14cb42522d396764eea80e5ce626a Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Wed, 12 Aug 2026 10:32:12 -0300 Subject: [PATCH 099/455] drivers: wifi: esp32: Report station IP to stack Report the station IPv4 address to the Wi-Fi stack from the interface address events, and from the station connect handler for an address configured before the association, so that a static address and a DHCP lease behave the same. Signed-off-by: Raffael Rostagno --- drivers/wifi/esp32/src/esp_wifi_drv.c | 36 +++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/wifi/esp32/src/esp_wifi_drv.c b/drivers/wifi/esp32/src/esp_wifi_drv.c index 2d82ca04ced1..900242a59011 100644 --- a/drivers/wifi/esp32/src/esp_wifi_drv.c +++ b/drivers/wifi/esp32/src/esp_wifi_drv.c @@ -137,7 +137,32 @@ struct esp32_wifi_event { K_MSGQ_DEFINE(esp32_wifi_event_msgq, sizeof(struct esp32_wifi_event), CONFIG_ESP32_WIFI_EVENT_QUEUE_SIZE, 4); +/* + * Set the station IPv4 address in the Wi-Fi stack. The address is only reported + * when it is usable and the station is associated. A static address is usually + * set before the connection, so the connect handler calls this as well. + */ +static void esp32_wifi_set_sta_ip(void) +{ + if (esp32_data.state != ESP32_STA_CONNECTED) { + return; + } + + if (net_if_ipv4_get_global_addr(esp32_wifi_iface, NET_ADDR_PREFERRED) == NULL) { + return; + } + + esp_wifi_internal_set_sta_ip(); +} + +#if defined(CONFIG_NET_IPV4) #if defined(CONFIG_WIFI_STA_AUTO_DHCPV4) +#define ESP32_WIFI_IPV4_EVENT_MASK \ + (NET_EVENT_IPV4_ADDR_ADD | NET_EVENT_IPV4_ACD_SUCCEED | NET_EVENT_IPV4_DHCP_BOUND) +#else +#define ESP32_WIFI_IPV4_EVENT_MASK (NET_EVENT_IPV4_ADDR_ADD | NET_EVENT_IPV4_ACD_SUCCEED) +#endif + static void wifi_event_handler(uint64_t mgmt_event, struct net_if *iface, void *info __unused, size_t info_length __unused, void *user_data __unused) { @@ -146,17 +171,23 @@ static void wifi_event_handler(uint64_t mgmt_event, struct net_if *iface, void * } switch (mgmt_event) { + case NET_EVENT_IPV4_ADDR_ADD: + case NET_EVENT_IPV4_ACD_SUCCEED: + esp32_wifi_set_sta_ip(); + break; +#if defined(CONFIG_WIFI_STA_AUTO_DHCPV4) case NET_EVENT_IPV4_DHCP_BOUND: wifi_mgmt_raise_connect_result_event(iface, WIFI_STATUS_CONN_SUCCESS); break; +#endif default: break; } } -NET_MGMT_REGISTER_EVENT_HANDLER(esp32_wifi_events, NET_EVENT_IPV4_DHCP_BOUND, wifi_event_handler, +NET_MGMT_REGISTER_EVENT_HANDLER(esp32_wifi_events, ESP32_WIFI_IPV4_EVENT_MASK, wifi_event_handler, NULL); -#endif /* CONFIG_WIFI_STA_AUTO_DHCPV4 */ +#endif /* CONFIG_NET_IPV4 */ static void esp32_wifi_tx_done(uint8_t ifidx, uint8_t *data __unused, uint16_t *data_len __unused, bool status __unused) @@ -473,6 +504,7 @@ static void esp_wifi_handle_sta_connect_event(void *event_data) ARG_UNUSED(event_data); esp32_data.state = ESP32_STA_CONNECTED; net_if_dormant_off(esp32_wifi_iface); + esp32_wifi_set_sta_ip(); #if defined(CONFIG_WIFI_STA_AUTO_DHCPV4) net_dhcpv4_start(esp32_wifi_iface); #else From 48d9f309a57a798fb0f85d467b1531c06652fbde Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Fri, 22 May 2026 16:13:50 -0300 Subject: [PATCH 100/455] soc: esp32: wifi: Sleep optimization symbols Add sleep optimization symbols for WiFi PM support. Signed-off-by: Raffael Rostagno --- soc/espressif/common/Kconfig.pm | 3 +++ soc/espressif/esp32/Kconfig.caps | 4 ++++ soc/espressif/esp32c2/Kconfig.caps | 4 ++++ soc/espressif/esp32c3/Kconfig.caps | 4 ++++ soc/espressif/esp32c5/Kconfig.caps | 8 ++++++++ soc/espressif/esp32c6/Kconfig.caps | 8 ++++++++ soc/espressif/esp32s2/Kconfig.caps | 4 ++++ soc/espressif/esp32s3/Kconfig.caps | 4 ++++ 8 files changed, 39 insertions(+) diff --git a/soc/espressif/common/Kconfig.pm b/soc/espressif/common/Kconfig.pm index b20effe9416e..0aa9cde441cf 100644 --- a/soc/espressif/common/Kconfig.pm +++ b/soc/espressif/common/Kconfig.pm @@ -76,6 +76,9 @@ config ESP32_PM_ESP_SLEEP_POWER_DOWN_CPU bool default y if ESP32_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP +config SOC_ESP32_PM_SLP_DEFAULT_PARAMS_OPT + bool + config ESP32_TIMER_IN_IRAM bool diff --git a/soc/espressif/esp32/Kconfig.caps b/soc/espressif/esp32/Kconfig.caps index c31bd082efc1..4fcc15241f0d 100644 --- a/soc/espressif/esp32/Kconfig.caps +++ b/soc/espressif/esp32/Kconfig.caps @@ -8,3 +8,7 @@ config ESP32_SOC_FLASH_SUPPORTED config ESP32_SOC_PM_SUPPORT_VDDSDIO_PD bool default y + +config SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + bool + default y diff --git a/soc/espressif/esp32c2/Kconfig.caps b/soc/espressif/esp32c2/Kconfig.caps index c31bd082efc1..4fcc15241f0d 100644 --- a/soc/espressif/esp32c2/Kconfig.caps +++ b/soc/espressif/esp32c2/Kconfig.caps @@ -8,3 +8,7 @@ config ESP32_SOC_FLASH_SUPPORTED config ESP32_SOC_PM_SUPPORT_VDDSDIO_PD bool default y + +config SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + bool + default y diff --git a/soc/espressif/esp32c3/Kconfig.caps b/soc/espressif/esp32c3/Kconfig.caps index b322ea395f07..63d8f6f37919 100644 --- a/soc/espressif/esp32c3/Kconfig.caps +++ b/soc/espressif/esp32c3/Kconfig.caps @@ -12,3 +12,7 @@ config ESP32_SOC_FLASH_SUPPORTED config ESP32_SOC_PM_SUPPORT_VDDSDIO_PD bool default y + +config SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + bool + default y diff --git a/soc/espressif/esp32c5/Kconfig.caps b/soc/espressif/esp32c5/Kconfig.caps index cd1fee73092e..f308606e9222 100644 --- a/soc/espressif/esp32c5/Kconfig.caps +++ b/soc/espressif/esp32c5/Kconfig.caps @@ -9,6 +9,14 @@ config ESP32_SOC_SPI_MEM_SUPPORT_TIMING_TUNING bool default y +config SOC_ESP32_PM_SUPPORT_BEACON_WAKEUP + bool + default y + +config SOC_ESP32_WIFI_HE_SUPPORT + bool + default y + config ESP32_SOC_PM_SUPPORT_TOP_PD bool default y diff --git a/soc/espressif/esp32c6/Kconfig.caps b/soc/espressif/esp32c6/Kconfig.caps index d5d6c24ed8d2..c2a248426b75 100644 --- a/soc/espressif/esp32c6/Kconfig.caps +++ b/soc/espressif/esp32c6/Kconfig.caps @@ -5,6 +5,14 @@ config ESP32_SOC_PM_SUPPORT_CPU_PD bool default y +config SOC_ESP32_PM_SUPPORT_BEACON_WAKEUP + bool + default y + +config SOC_ESP32_WIFI_HE_SUPPORT + bool + default y + config ESP32_SOC_PM_SUPPORT_TOP_PD bool default y diff --git a/soc/espressif/esp32s2/Kconfig.caps b/soc/espressif/esp32s2/Kconfig.caps index c31bd082efc1..4fcc15241f0d 100644 --- a/soc/espressif/esp32s2/Kconfig.caps +++ b/soc/espressif/esp32s2/Kconfig.caps @@ -8,3 +8,7 @@ config ESP32_SOC_FLASH_SUPPORTED config ESP32_SOC_PM_SUPPORT_VDDSDIO_PD bool default y + +config SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + bool + default y diff --git a/soc/espressif/esp32s3/Kconfig.caps b/soc/espressif/esp32s3/Kconfig.caps index 92b755d68042..c7d002e42b41 100644 --- a/soc/espressif/esp32s3/Kconfig.caps +++ b/soc/espressif/esp32s3/Kconfig.caps @@ -16,3 +16,7 @@ config ESP32_SOC_PM_SUPPORT_VDDSDIO_PD config ESP32_SOC_SPI_MEM_SUPPORT_TIMING_TUNING bool default y + +config SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + bool + default y From c78ad83a220f7eeecbf5936e4d4794ce9e7ffc27 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Fri, 22 May 2026 16:20:28 -0300 Subject: [PATCH 101/455] drivers: wifi: esp32: Sleep optimization Add sleep (standby) optimization options for WiFi. Signed-off-by: Raffael Rostagno --- drivers/wifi/esp32/Kconfig.esp32 | 77 +++++++++++++++++++++++++++ drivers/wifi/esp32/src/esp_wifi_drv.c | 18 +++++++ 2 files changed, 95 insertions(+) diff --git a/drivers/wifi/esp32/Kconfig.esp32 b/drivers/wifi/esp32/Kconfig.esp32 index 05ef99f136e9..cdd9da89592a 100644 --- a/drivers/wifi/esp32/Kconfig.esp32 +++ b/drivers/wifi/esp32/Kconfig.esp32 @@ -307,6 +307,14 @@ config ESP32_WIFI_IRAM_OPT When this option is disabled, more than 10Kbytes of IRAM memory will be saved but Wi-Fi throughput will be reduced. +config ESP32_WIFI_EXTRA_IRAM_OPT + bool "Wi-Fi EXTRA IRAM speed optimization" + default y if SOC_ESP32_WIFI_HE_SUPPORT + help + Select this option to place additional frequently called Wi-Fi library functions + in IRAM. When this option is disabled, more than 5Kbytes of IRAM memory will be saved + but Wi-Fi throughput will be reduced. + config ESP32_WIFI_RX_IRAM_OPT bool "WiFi RX IRAM speed optimization" help @@ -314,6 +322,19 @@ config ESP32_WIFI_RX_IRAM_OPT When this option is disabled, more than 17Kbytes of IRAM memory will be saved but Wi-Fi performance will be reduced. +config ESP32_WIFI_SLP_IRAM_OPT + bool "Wi-Fi SLP IRAM speed optimization" + select SOC_ESP32_PM_SLP_DEFAULT_PARAMS_OPT if PM && TICKLESS_KERNEL + select ESP32_PM_SLP_IRAM_OPT if PM && TICKLESS_KERNEL + default y if SOC_ESP32_WIFI_HE_SUPPORT + help + Select this option to place called Wi-Fi library TBTT process and receive + beacon functions in IRAM. Some functions can be put in IRAM either by + ESP32_WIFI_IRAM_OPT and ESP32_WIFI_RX_IRAM_OPT, or this one. + With ESP32_WIFI_IRAM_OPT already enabled, an additional 7.3KB IRAM is used. + With ESP32_WIFI_RX_IRAM_OPT only, an additional 1.3KB. With neither enabled, + an additional 7.4KB. Wi-Fi power-save mode average current is reduced. + config ESP32_WIFI_MAX_THREAD_PRIORITY int "Maximum work queue thread priority" default 7 @@ -633,6 +654,62 @@ config WIFI_ESP32_MESH_IP endif # WIFI_ESP32_MESH +config ESP32_WIFI_ENHANCED_LIGHT_SLEEP + bool "Wi-Fi modem automatically receives the beacon [EXPERIMENTAL]" + depends on MAC_BB_PD && SOC_ESP32_PM_SUPPORT_BEACON_WAKEUP + select EXPERIMENTAL + help + The Wi-Fi modem automatically receives the beacon frame during light sleep + (PMU modem-state wakeup). This hands CPU wake-up over to the PMU modem + state machine instead of the RTC timer. + + EXPERIMENTAL: known to cause system hangs on disconnect in some + configurations. Use plain MAC_BB_PD light sleep (without this option) + for reliable connected light sleep. + +config ESP32_WIFI_SLP_BEACON_LOST_OPT + bool "Wi-Fi sleep optimize when beacon lost" + depends on MAC_BB_PD + help + Enable Wi-Fi sleep optimization when beacon loss occurs and immediately enter + sleep mode when the Wi-Fi module detects beacon loss. + +config ESP32_WIFI_SLP_BEACON_LOST_TIMEOUT + int "Beacon loss timeout" + range 5 100 + default 10 + depends on ESP32_WIFI_SLP_BEACON_LOST_OPT + help + Timeout before turning the RF PHY off when beacon loss occurs. + Unit: 1024 microseconds. + +config ESP32_WIFI_SLP_BEACON_LOST_THRESHOLD + int "Maximum number of consecutive lost beacons allowed" + range 0 8 + default 3 + depends on ESP32_WIFI_SLP_BEACON_LOST_OPT + help + Maximum number of consecutive lost beacons allowed, Wi-Fi keeps Rx state when + the number of consecutive beacons lost is greater than the given threshold. + +config ESP32_WIFI_SLP_PHY_ON_DELTA_EARLY_TIME + int "Delta early time for RF PHY on" + range 0 100 + default 2 + depends on ESP32_WIFI_SLP_BEACON_LOST_OPT && SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + help + Delta early time for rf phy on, When the beacon is lost, the next rf phy on will + be earlier the time specified by the configuration item, Unit: 32 microsecond. + +config ESP32_WIFI_SLP_PHY_OFF_DELTA_TIMEOUT_TIME + int "Delta timeout time for RF PHY off" + range 0 8 + default 2 + depends on ESP32_WIFI_SLP_BEACON_LOST_OPT && SOC_ESP32_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW + help + Delta timeout time for rf phy off, When the beacon is lost, the next rf phy off will + be delayed for the time specified by the configuration item. Unit: 1024 microsecond. + config ESP32_WIFI_MBEDTLS_CRYPTO bool "Use MbedTLS crypto APIs" select MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS diff --git a/drivers/wifi/esp32/src/esp_wifi_drv.c b/drivers/wifi/esp32/src/esp_wifi_drv.c index 900242a59011..44f575f659a9 100644 --- a/drivers/wifi/esp32/src/esp_wifi_drv.c +++ b/drivers/wifi/esp32/src/esp_wifi_drv.c @@ -22,6 +22,7 @@ LOG_MODULE_REGISTER(esp32_wifi, CONFIG_WIFI_LOG_LEVEL); #include #include #include +#include #include #include #include @@ -1882,10 +1883,27 @@ static int esp32_wifi_pm_action(const struct device *dev, enum pm_device_action /* Reject sleep while TSF is active (timing critical) */ return -EBUSY; } +#endif +#if defined(CONFIG_ESP32_WIFI_ENHANCED_LIGHT_SLEEP) + if (sleep_modem_wifi_modem_state_skip_light_sleep()) { + /* Block the system from entering sleep before modem link done */ + return -EBUSY; + } #endif break; case PM_DEVICE_ACTION_TURN_ON: +#if defined(CONFIG_PM) + /* Register the Wi-Fi modem sleep configuration. Advanced DTIM + * sleep (ESP32_WIFI_ENHANCED_LIGHT_SLEEP) and default sleep + * timing parameters (SOC_ESP32_PM_SLP_DEFAULT_PARAMS_OPT) are + * applied inside when those options are enabled. + */ + sleep_modem_configure(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, + CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, true); +#endif + break; + case PM_DEVICE_ACTION_TURN_OFF: break; From 60d13842156093c477dd9b841a8d4bf0aadfa592 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Tue, 24 Feb 2026 10:20:01 -0300 Subject: [PATCH 102/455] soc: esp32: wifi: Update linker script for sleep optimization Update linker script for sleep optimization. Signed-off-by: Raffael Rostagno --- soc/espressif/esp32/default.ld | 113 ++++++++++++++++++++++++++----- soc/espressif/esp32c2/default.ld | 105 +++++++++++++++++++++++----- soc/espressif/esp32c3/default.ld | 105 +++++++++++++++++++++++----- soc/espressif/esp32c5/default.ld | 105 +++++++++++++++++++++++----- soc/espressif/esp32c6/default.ld | 105 +++++++++++++++++++++++----- soc/espressif/esp32s2/default.ld | 101 +++++++++++++++++++++++---- soc/espressif/esp32s3/default.ld | 102 ++++++++++++++++++++++++---- 7 files changed, 628 insertions(+), 108 deletions(-) diff --git a/soc/espressif/esp32/default.ld b/soc/espressif/esp32/default.ld index 05737de07ccd..d3fd8187a896 100644 --- a/soc/espressif/esp32/default.ld +++ b/soc/espressif/esp32/default.ld @@ -490,11 +490,47 @@ SECTIONS *libzephyr.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiorslpiram .wifiorslpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libcoexist.a:(.coexsleepiram .coexsleepiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -502,12 +538,12 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif . = ALIGN(4); @@ -1018,15 +1054,60 @@ SECTIONS __text_region_start = ABSOLUTE(.); __rom_region_start = ABSOLUTE(.); -#ifndef CONFIG_ESP32_WIFI_IRAM_OPT - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiorslpiram .wifiorslpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libcoexist.a:(.coexsleepiram .coexsleepiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) #endif -#ifndef CONFIG_ESP32_WIFI_RX_IRAM_OPT - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif #endif *(.fini.literal) diff --git a/soc/espressif/esp32c2/default.ld b/soc/espressif/esp32c2/default.ld index 8261c414bffa..ea5b547c4ff8 100644 --- a/soc/espressif/esp32c2/default.ld +++ b/soc/espressif/esp32c2/default.ld @@ -332,11 +332,43 @@ SECTIONS *libzephyr.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -344,12 +376,12 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *libbtbb.a:(.iram1 .iram1.*) *libble_app.a:(.isr_iram1.* .conn_iram1.* .sleep_iram1.*) @@ -685,15 +717,56 @@ SECTIONS __rom_region_start = ABSOLUTE(.); #if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif #if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *(.literal .text .literal.* .text.*) *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) diff --git a/soc/espressif/esp32c3/default.ld b/soc/espressif/esp32c3/default.ld index d1d3b0958cb7..2a81c8c7f928 100644 --- a/soc/espressif/esp32c3/default.ld +++ b/soc/espressif/esp32c3/default.ld @@ -436,11 +436,43 @@ SECTIONS *libzephyr.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -448,12 +480,12 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif . = ALIGN(4) + 16; @@ -786,15 +818,56 @@ SECTIONS __rom_region_start = ABSOLUTE(.); #if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif #if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *(.literal .text .literal.* .text.*) *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) diff --git a/soc/espressif/esp32c5/default.ld b/soc/espressif/esp32c5/default.ld index 356a7120135f..6500bfc68839 100644 --- a/soc/espressif/esp32c5/default.ld +++ b/soc/espressif/esp32c5/default.ld @@ -503,11 +503,43 @@ SECTIONS *libzephyr.a:cache_utils.*(.literal .text .literal.* .text.*) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -515,12 +547,12 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *libble_app.a:(.high_perf_code_iram1 .high_perf_code_iram1.*) *libble_app.a:(.bt_iram_text .bt_iram_text.*) @@ -916,15 +948,56 @@ SECTIONS __rom_region_start = ABSOLUTE(.); #if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif #if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *(.literal .text .literal.* .text.*) *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) diff --git a/soc/espressif/esp32c6/default.ld b/soc/espressif/esp32c6/default.ld index 1de1f1cd76b0..36176a1bb066 100644 --- a/soc/espressif/esp32c6/default.ld +++ b/soc/espressif/esp32c6/default.ld @@ -483,11 +483,43 @@ SECTIONS *libzephyr.a:cache_utils.*(.literal .text .literal.* .text.*) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -495,12 +527,12 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *libbtbb.a:(.iram1 .iram1.*) *libbtbb.a:(.high_perf_code_iram1 .high_perf_code_iram1.*) @@ -873,15 +905,56 @@ SECTIONS __rom_region_start = ABSOLUTE(.); #if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.* .wifi_extra_iram.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) -#endif /* CONFIG_ESP32_WIFI_IRAM_OPT */ + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif #if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) -#endif /* CONFIG_ESP32_WIFI_RX_IRAM_OPT */ + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif +#endif *(.adv_fast_execute_code_iram1 .adv_fast_execute_code_iram1.*) *(.literal .text .literal.* .text.*) diff --git a/soc/espressif/esp32s2/default.ld b/soc/espressif/esp32s2/default.ld index 82e18ca7ddec..c4af7f9371e6 100644 --- a/soc/espressif/esp32s2/default.ld +++ b/soc/espressif/esp32s2/default.ld @@ -492,11 +492,43 @@ SECTIONS *libzephyr.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:(.wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:(.wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiorslpiram .wifiorslpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -504,11 +536,11 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif #endif /* align + add 16B for CPU dummy speculative instr. fetch */ @@ -730,7 +762,7 @@ SECTIONS *libphy.a:(.rodata .rodata.*) -#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) /* [mapping:esp_wifi] */ *(.rodata.wifi_clock_enable_wrapper) *(.rodata.wifi_clock_disable_wrapper) @@ -912,14 +944,55 @@ SECTIONS __rom_region_start = ABSOLUTE(.); #if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiorslpiram .wifiorslpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) #endif #if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif #endif *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) diff --git a/soc/espressif/esp32s3/default.ld b/soc/espressif/esp32s3/default.ld index cb5714669b34..4cc6bce918d9 100644 --- a/soc/espressif/esp32s3/default.ld +++ b/soc/espressif/esp32s3/default.ld @@ -517,11 +517,43 @@ SECTIONS *libzephyr.a:esp_cache_msync.*(.literal .literal.* .text .text.*) #if defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:(.wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:(.wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiorslpiram .wifiorslpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) +#endif - /* [mapping:esp_wifi] */ +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) || defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) @@ -529,12 +561,13 @@ SECTIONS *(.literal.esp_phy_enable .text.esp_phy_enable) *(.literal.esp_phy_disable .text.esp_phy_disable) *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) -#endif -#if defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) +#if defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif #endif + . = ALIGN(4); } GROUP_DATA_LINK_IN(IRAM_REGION, ROMABLE_REGION) @@ -765,7 +798,7 @@ SECTIONS *libphy.a:(.rodata .rodata.*) -#if defined(CONFIG_ESP32_WIFI_IRAM_OPT) +#if defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) /* [mapping:esp_wifi] */ *(.rodata.wifi_clock_enable_wrapper) *(.rodata.wifi_clock_disable_wrapper) @@ -921,14 +954,55 @@ SECTIONS __rom_region_start = ABSOLUTE(.); #if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) - *libnet80211.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiextrairam .wifiextrairam.*) - *libpp.a:( .wifi0iram .wifi0iram.* .wifislpiram .wifislpiram.* .wifiorslpiram .wifiorslpiram.* .wifiextrairam .wifiextrairam.*) - *libcoexist.a:(.wifi_slp_iram .wifi_slp_iram.* .coexiram .coexiram.* .coexsleepiram .coexsleepiram.*) + *libnet80211.a:(.wifi0iram .wifi0iram.*) + *libpp.a:(.wifi0iram .wifi0iram.*) #endif #if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) - *libnet80211.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) - *libpp.a:( .wifirxiram .wifirxiram.* .wifislprxiram .wifislprxiram.*) + *libnet80211.a:(.wifirxiram .wifirxiram.*) + *libpp.a:(.wifirxiram .wifirxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislpiram .wifislpiram.*) + *libpp.a:(.wifislpiram .wifislpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libpp.a:(.wifiorslpiram .wifiorslpiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_RX_IRAM_OPT) && !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *libnet80211.a:(.wifislprxiram .wifislprxiram.*) + *libpp.a:(.wifislprxiram .wifislprxiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_EXTRA_IRAM_OPT) + *libnet80211.a:(.wifiextrairam .wifiextrairam.*) + *libpp.a:(.wifiextrairam .wifiextrairam.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *libcoexist.a:(.coexiram .coexiram.*) +#endif + +#if !defined(CONFIG_ESP32_WIFI_IRAM_OPT) + *(.literal.coex_pti_get_wrapper .text.coex_pti_get_wrapper) +#endif + +#if !defined(CONFIG_ESP32_WIFI_SLP_IRAM_OPT) + *(.literal.wifi_clock_enable_wrapper .text.wifi_clock_enable_wrapper) + *(.literal.wifi_clock_disable_wrapper .text.wifi_clock_disable_wrapper) + + /* [mapping:esp_phy] */ + *(.literal.esp_phy_enable .text.esp_phy_enable) + *(.literal.esp_phy_disable .text.esp_phy_disable) + *(.literal.esp_wifi_bt_power_domain_off .text.esp_wifi_bt_power_domain_off) + +#if !defined(CONFIG_PM) + *(.literal.wifi_apb80m_request .text.wifi_apb80m_request) + *(.literal.wifi_apb80m_release .text.wifi_apb80m_release) +#endif #endif *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) From e2829f55ae9361ce875a4dcbf5a0f2d9996d1d24 Mon Sep 17 00:00:00 2001 From: Raffael Rostagno Date: Mon, 9 Feb 2026 09:50:58 -0300 Subject: [PATCH 103/455] tests: espressif: wifi: Add PM testcase Add power management testcase for Wi-Fi (standby sleep). Signed-off-by: Raffael Rostagno --- snippets/espressif/pm/espressif-pm.conf | 2 + snippets/espressif/pm/snippet.yml | 2 +- tests/boards/espressif/wifi/Kconfig | 7 + tests/boards/espressif/wifi/socs/esp32c2.conf | 2 - tests/boards/espressif/wifi/src/main.c | 165 +++++++++++++----- tests/boards/espressif/wifi/tests.yaml | 16 ++ 6 files changed, 149 insertions(+), 45 deletions(-) diff --git a/snippets/espressif/pm/espressif-pm.conf b/snippets/espressif/pm/espressif-pm.conf index 313b7a45bbb9..dc5750f4b527 100644 --- a/snippets/espressif/pm/espressif-pm.conf +++ b/snippets/espressif/pm/espressif-pm.conf @@ -1 +1,3 @@ +CONFIG_PM=y +CONFIG_PM_DEVICE=y CONFIG_COUNTER=y diff --git a/snippets/espressif/pm/snippet.yml b/snippets/espressif/pm/snippet.yml index f40b168b6b2a..6242d21b1466 100644 --- a/snippets/espressif/pm/snippet.yml +++ b/snippets/espressif/pm/snippet.yml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 name: espressif-pm -description: Enables RTC timer wakeup and counter support for light sleep. +description: Enables PM, PM device, RTC timer wakeup and counter support for light sleep. append: EXTRA_CONF_FILE: espressif-pm.conf EXTRA_DTC_OVERLAY_FILE: espressif-pm.overlay diff --git a/tests/boards/espressif/wifi/Kconfig b/tests/boards/espressif/wifi/Kconfig index b252f682a6fa..7405b079380c 100644 --- a/tests/boards/espressif/wifi/Kconfig +++ b/tests/boards/espressif/wifi/Kconfig @@ -79,3 +79,10 @@ config WIFI_PING_TIMEOUT help Timeout duration for pinging the network gateway. If no reply is received within this time, test will fail. + +config HEAP_MEM_POOL_ADD_SIZE_WIFI_TEST + int "Wi-Fi test extra system heap (bytes)" + default 0 + help + Extra system heap for Wi-Fi PM test scenarios. + Added on top of subsystem minimums (Wi-Fi driver, board, etc.). diff --git a/tests/boards/espressif/wifi/socs/esp32c2.conf b/tests/boards/espressif/wifi/socs/esp32c2.conf index 0b1e576c2196..5061801e8774 100644 --- a/tests/boards/espressif/wifi/socs/esp32c2.conf +++ b/tests/boards/espressif/wifi/socs/esp32c2.conf @@ -1,5 +1,3 @@ -CONFIG_HEAP_MEM_POOL_SIZE=5120 - # Following settings are test only values, and # were adjusted due to stricter SRAM limits on C2 diff --git a/tests/boards/espressif/wifi/src/main.c b/tests/boards/espressif/wifi/src/main.c index b0428a34d5e9..81f280078e90 100644 --- a/tests/boards/espressif/wifi/src/main.c +++ b/tests/boards/espressif/wifi/src/main.c @@ -20,6 +20,7 @@ LOG_MODULE_REGISTER(wifi_test, LOG_LEVEL_INF); #include K_SEM_DEFINE(wifi_event, 0, 1); +K_SEM_DEFINE(icmp_event_sem, 0, 1); #define WIFI_MGMT_EVENTS \ (NET_EVENT_WIFI_SCAN_DONE | NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_CONNECT_RESULT | \ @@ -27,10 +28,19 @@ K_SEM_DEFINE(wifi_event, 0, 1); #define TEST_DATA "ICMP dummy data" +#define WIFI_PING_ERR_LEN 64 +#define SET_ERR(fmt, ...) \ + do { \ + if (err) { \ + snprintk(err, WIFI_PING_ERR_LEN, fmt, ##__VA_ARGS__); \ + } \ + } while (0) + static struct wifi_context { struct net_if *iface; uint32_t scan_result; bool connecting; + bool connected; int result; struct net_mgmt_event_callback wifi_mgmt_cb; } wifi_ctx; @@ -71,6 +81,7 @@ static void wifi_connect_result(struct net_mgmt_event_callback *cb) if (wifi_ctx.result) { LOG_INF("Connection request failed (%d)", wifi_ctx.result); } else { + wifi_ctx.connected = true; LOG_INF("Connected"); } } @@ -90,6 +101,7 @@ static void wifi_disconnect_result(struct net_mgmt_event_callback *cb) if (wifi_ctx.result) { LOG_INF("Disconnect failed (%d)", wifi_ctx.result); } else { + wifi_ctx.connected = false; LOG_INF("Disconnected"); } } else { @@ -151,7 +163,7 @@ static enum net_verdict icmp_event(struct net_icmp_ctx *ctx, struct net_pkt *pkt wifi_ctx.result = strcmp(buf, TEST_DATA); sem_give: - k_sem_give(&wifi_event); + k_sem_give(&icmp_event_sem); return wifi_ctx.result == 0 ? NET_OK : NET_DROP; } @@ -165,7 +177,7 @@ static int wifi_scan(void) return ret; } - LOG_INF("Wifi scan requested..."); + LOG_INF("Wi-Fi scan requested..."); return 0; } @@ -232,6 +244,92 @@ static int wifi_state(void) return status.state; } +static int wifi_ping_gw(char *err) +{ + struct net_icmp_ping_params params; + struct net_icmp_ctx icmp_ctx; + struct net_in_addr gw_addr_4; + struct net_sockaddr_in dst4 = {0}; + int retry = CONFIG_WIFI_PING_ATTEMPTS; + int ret; + int status = 0; + + gw_addr_4 = net_if_ipv4_get_gw(wifi_ctx.iface); + + if (gw_addr_4.s_addr == 0) { + SET_ERR("Gateway address is not set"); + status = -ENOENT; + goto exit; + } + + ret = net_icmp_init_ctx(&icmp_ctx, NET_AF_INET, NET_ICMPV4_ECHO_REPLY, 0, icmp_event); + + if (ret) { + SET_ERR("Cannot init ICMP (%d)", ret); + status = ret; + goto exit; + } + + dst4.sin_family = NET_AF_INET; + memcpy(&dst4.sin_addr, &gw_addr_4, sizeof(gw_addr_4)); + + params.identifier = 1234; + params.sequence = 5678; + params.tc_tos = 1; + params.priority = 2; + params.data = TEST_DATA; + params.data_size = sizeof(TEST_DATA); + + k_sem_reset(&icmp_event_sem); + wifi_ctx.result = -ETIMEDOUT; + + LOG_INF("Pinging the gateway..."); + + do { + if (!wifi_ctx.connected) { + status = -EIO; + SET_ERR("Wi-Fi not connected or dropped"); + goto cleanup; + } + + ret = net_icmp_send_echo_request(&icmp_ctx, wifi_ctx.iface, + (struct net_sockaddr *)&dst4, ¶ms, NULL); + + if (ret) { + SET_ERR("Cannot send ICMP echo request (%d)", ret); + status = ret; + goto cleanup; + } + + if (k_sem_take(&icmp_event_sem, K_SECONDS(CONFIG_WIFI_PING_TIMEOUT)) == 0) { + break; + } + + retry--; + + if (wifi_ctx.connected) { + LOG_INF("No reply, retry %d", CONFIG_WIFI_PING_ATTEMPTS - retry); + } + + } while (retry); + + if (retry <= 0) { + SET_ERR("Gateway ping (ICMP) timed out"); + status = -ETIMEDOUT; + goto cleanup; + } + + if (wifi_ctx.result != 0) { + SET_ERR("ICMP data error (%d)", wifi_ctx.result); + status = wifi_ctx.result; + } + +cleanup: + net_icmp_cleanup_ctx(&icmp_ctx); +exit: + return status; +} + ZTEST(wifi, test_0_scan) { int ret; @@ -240,7 +338,7 @@ ZTEST(wifi, test_0_scan) zassert_equal(ret, 0, "Scan request failed"); zassert_equal(k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_SCAN_TIMEOUT)), 0, - "Wifi scan failed or timed out"); + "Wi-Fi scan failed or timed out"); LOG_INF("Scan done"); } @@ -258,7 +356,7 @@ ZTEST(wifi, test_1_connect) zassert_equal(ret, 0, "Connect request failed"); zassert_equal(k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_CONNECT_TIMEOUT)), 0, - "Wifi connect timed out"); + "Wi-Fi connect timed out"); if (wifi_ctx.result) { zassert(--retry, "Connect failed"); @@ -281,61 +379,44 @@ ZTEST(wifi, test_1_connect) ZTEST(wifi, test_2_icmp) { - struct net_icmp_ping_params params; - struct net_icmp_ctx icmp_ctx; - struct net_in_addr gw_addr_4; - struct net_sockaddr_in dst4 = {0}; - int retry = CONFIG_WIFI_PING_ATTEMPTS; + char err[WIFI_PING_ERR_LEN] = ""; int ret; - gw_addr_4 = net_if_ipv4_get_gw(wifi_ctx.iface); - zassert_not_equal(gw_addr_4.s_addr, 0, "Gateway address is not set"); - - ret = net_icmp_init_ctx(&icmp_ctx, NET_AF_INET, NET_ICMPV4_ECHO_REPLY, 0, icmp_event); - zassert_equal(ret, 0, "Cannot init ICMP (%d)", ret); + ret = wifi_ping_gw(err); - dst4.sin_family = NET_AF_INET; - memcpy(&dst4.sin_addr, &gw_addr_4, sizeof(gw_addr_4)); - - params.identifier = 1234; - params.sequence = 5678; - params.tc_tos = 1; - params.priority = 2; - params.data = TEST_DATA; - params.data_size = sizeof(TEST_DATA); - - LOG_INF("Pinging the gateway..."); - - do { - ret = net_icmp_send_echo_request(&icmp_ctx, wifi_ctx.iface, - (struct net_sockaddr *)&dst4, ¶ms, NULL); - zassert_equal(ret, 0, "Cannot send ICMP echo request (%d)", ret); + zassert_equal(ret, 0, "%s", err); +} - int timeout = k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_PING_TIMEOUT)); +#if defined(CONFIG_PM) +ZTEST(wifi, test_3_icmp_pm) +{ + char err[WIFI_PING_ERR_LEN] = ""; + int ret; - if (timeout) { - zassert(--retry, "Gateway ping (ICMP) timed out on all attempts"); - LOG_INF("No reply, retry %d", CONFIG_WIFI_PING_ATTEMPTS - retry); - } else { - break; - } - } while (retry); + LOG_INF("Enter sleep then ping gateway again..."); + k_sleep(K_SECONDS(2)); - /* check result */ - zassert_equal(wifi_ctx.result, 0, "ICMP data error"); + ret = wifi_ping_gw(err); - net_icmp_cleanup_ctx(&icmp_ctx); + zassert_equal(ret, 0, "%s", err); } +#endif +#if defined(CONFIG_PM) +ZTEST(wifi, test_4_disconnect) +#else ZTEST(wifi, test_3_disconnect) +#endif { int ret; + zassert(wifi_ctx.connected, "Wi-Fi already disconnected"); + ret = wifi_disconnect(); zassert_equal(ret, 0, "Disconnect request failed"); zassert_equal(k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_DISCONNECT_TIMEOUT)), 0, - "Wifi disconnect timed out"); + "Wi-Fi disconnect timed out"); zassert_equal(wifi_ctx.result, 0, "Disconnect failed"); } diff --git a/tests/boards/espressif/wifi/tests.yaml b/tests/boards/espressif/wifi/tests.yaml index e222860b1e14..faf17e7bf96f 100644 --- a/tests/boards/espressif/wifi/tests.yaml +++ b/tests/boards/espressif/wifi/tests.yaml @@ -22,6 +22,22 @@ tests: - esp32s3_devkitc/esp32s3/procpu - esp32c3_devkitc - esp8684_devkitm + esp.wifi.sec.wpa2.pm: + tags: wifi + filter: CONFIG_WIFI_ESP32 + extra_args: + - SNIPPET=espressif-pm + extra_configs: + - CONFIG_WIFI_TEST_AUTH_MODE_WPA2=y + - CONFIG_HEAP_MEM_POOL_ADD_SIZE_WIFI_TEST=16384 + platform_allow: + - esp32_devkitc/esp32/procpu + - esp32s2_devkitc + - esp32s3_devkitc/esp32s3/procpu + - esp32c3_devkitc + - esp32c5_devkitc/esp32c5/hpcore + - esp32c6_devkitc/esp32c6/hpcore + - esp8684_devkitm esp.wifi.sec.wpa3: tags: wifi filter: CONFIG_WIFI_ESP32 From bfd11e789f7a039266f48c15d02a6f85fa1804e4 Mon Sep 17 00:00:00 2001 From: Eric Mechin Date: Fri, 17 Jul 2026 10:50:55 +0200 Subject: [PATCH 104/455] samples: bluetooth: st_ble_sensor: fix STM32WB5MM-DK usage #94275 Update st_ble_sensor sample to manage the STM32WB5MM-DK RGB LED. Update SPDX headers and copyright lines. Remove trailing whitespace and add space before "(" Signed-off-by: Eric Mechin --- .../bluetooth/st_ble_sensor/CMakeLists.txt | 13 ++-- .../boards/stm32wb5mm_dk.overlay | 13 ++++ .../boards/stm32wb5mm_dk_stm32wb55xx.conf | 5 ++ samples/bluetooth/st_ble_sensor/src/led_svc.c | 2 +- .../bluetooth/st_ble_sensor/src/rgb_led_svc.c | 70 +++++++++++++++++++ 5 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay create mode 100644 samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf create mode 100644 samples/bluetooth/st_ble_sensor/src/rgb_led_svc.c diff --git a/samples/bluetooth/st_ble_sensor/CMakeLists.txt b/samples/bluetooth/st_ble_sensor/CMakeLists.txt index 2e77f2692abc..ee6917181a26 100644 --- a/samples/bluetooth/st_ble_sensor/CMakeLists.txt +++ b/samples/bluetooth/st_ble_sensor/CMakeLists.txt @@ -4,9 +4,14 @@ cmake_minimum_required(VERSION 3.28.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(st_ble_sensor) -FILE(GLOB app_sources src/*.c) -target_sources(app PRIVATE - ${app_sources} - ) +target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE src/button_svc.c) + +if(CONFIG_BOARD_STM32WB5MM_DK) + target_sources(app PRIVATE src/rgb_led_svc.c) +else() + target_sources(app PRIVATE src/led_svc.c) +endif() + zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) diff --git a/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay b/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay new file mode 100644 index 000000000000..534ad0c1dcec --- /dev/null +++ b/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 STMicroelectronics + */ + +&rgb_led_strip { + status = "okay"; +}; + +&rgb_cs { + status = "okay"; +}; diff --git a/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf b/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf new file mode 100644 index 000000000000..f8bcd5181728 --- /dev/null +++ b/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf @@ -0,0 +1,5 @@ +# Stack sizes for Bluetooth operations on stm32wb5mm_dk +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +CONFIG_LED_STRIP=y diff --git a/samples/bluetooth/st_ble_sensor/src/led_svc.c b/samples/bluetooth/st_ble_sensor/src/led_svc.c index ea1aed304268..37634a536e95 100644 --- a/samples/bluetooth/st_ble_sensor/src/led_svc.c +++ b/samples/bluetooth/st_ble_sensor/src/led_svc.c @@ -1,5 +1,5 @@ /** @file - * @brief Button Service sample + * @brief LED Service sample */ /* diff --git a/samples/bluetooth/st_ble_sensor/src/rgb_led_svc.c b/samples/bluetooth/st_ble_sensor/src/rgb_led_svc.c new file mode 100644 index 000000000000..8881cb500d8f --- /dev/null +++ b/samples/bluetooth/st_ble_sensor/src/rgb_led_svc.c @@ -0,0 +1,70 @@ +/** @file + * @brief RGB LED Service sample + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 STMicroelectronics + */ + +#include "led_svc.h" + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(led_svc); + +#define STRIP_NODE DT_ALIAS(led_strip) + +#define RGB(_r, _g, _b) { .r = (_r), .g = (_g), .b = (_b) } + +static const struct led_rgb blue = { + .r = 0x00, + .g = 0x00, + .b = 0x10, +}; + +static struct led_rgb led; + +static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE); + +static bool led_state; /* Tracking state here supports GPIO expander-based LEDs. */ +static bool led_ok; + +void led_update(void) +{ + int rc; + + if (!led_ok) { + return; + } + + led_state = !led_state; + LOG_INF("Turn %s LED", led_state ? "on" : "off"); + + memset(&led, 0x00, sizeof(struct led_rgb)); + if (led_state) { + memcpy(&led, &blue, sizeof(struct led_rgb)); + } + + rc = led_strip_update_rgb(strip, &led, 1); + if (rc) { + LOG_ERR("couldn't update strip: %d", rc); + } +} + +int led_init(void) +{ + led_ok = device_is_ready(strip); + if (led_ok) { + LOG_INF("Found LED strip device %s", strip->name); + } else { + LOG_ERR("LED strip device %s is not ready", strip->name); + return 1; + } + + return 0; +} From 6d2bba47227f23914cc416d5b441cc38178a791c Mon Sep 17 00:00:00 2001 From: Eric Mechin Date: Mon, 24 Aug 2026 16:40:44 +0200 Subject: [PATCH 105/455] samples: bluetooth: st_ble_sensor: fix STM32WB5MM-DK usage #94275 Move st_ble_sensor sample to samples/boards/st/bluetooth folder. Signed-off-by: Eric Mechin --- samples/{ => boards/st}/bluetooth/st_ble_sensor/CMakeLists.txt | 2 +- samples/{ => boards/st}/bluetooth/st_ble_sensor/README.rst | 2 +- .../st}/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay | 0 .../st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf | 0 samples/{ => boards/st}/bluetooth/st_ble_sensor/prj.conf | 0 .../{ => boards/st}/bluetooth/st_ble_sensor/src/button_svc.c | 0 .../{ => boards/st}/bluetooth/st_ble_sensor/src/button_svc.h | 0 samples/{ => boards/st}/bluetooth/st_ble_sensor/src/led_svc.c | 0 samples/{ => boards/st}/bluetooth/st_ble_sensor/src/led_svc.h | 0 samples/{ => boards/st}/bluetooth/st_ble_sensor/src/main.c | 0 .../{ => boards/st}/bluetooth/st_ble_sensor/src/rgb_led_svc.c | 0 samples/{ => boards/st}/bluetooth/st_ble_sensor/tests.yaml | 2 +- 12 files changed, 3 insertions(+), 3 deletions(-) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/CMakeLists.txt (82%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/README.rst (95%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/prj.conf (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/src/button_svc.c (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/src/button_svc.h (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/src/led_svc.c (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/src/led_svc.h (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/src/main.c (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/src/rgb_led_svc.c (100%) rename samples/{ => boards/st}/bluetooth/st_ble_sensor/tests.yaml (86%) diff --git a/samples/bluetooth/st_ble_sensor/CMakeLists.txt b/samples/boards/st/bluetooth/st_ble_sensor/CMakeLists.txt similarity index 82% rename from samples/bluetooth/st_ble_sensor/CMakeLists.txt rename to samples/boards/st/bluetooth/st_ble_sensor/CMakeLists.txt index ee6917181a26..5489ef0a4da5 100644 --- a/samples/bluetooth/st_ble_sensor/CMakeLists.txt +++ b/samples/boards/st/bluetooth/st_ble_sensor/CMakeLists.txt @@ -14,4 +14,4 @@ else() endif() -zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) +zephyr_library_include_directories(${ZEPHYR_BASE}/samples/boards/st/bluetooth) diff --git a/samples/bluetooth/st_ble_sensor/README.rst b/samples/boards/st/bluetooth/st_ble_sensor/README.rst similarity index 95% rename from samples/bluetooth/st_ble_sensor/README.rst rename to samples/boards/st/bluetooth/st_ble_sensor/README.rst index b50f5cdfdaa0..d2f60a2c783d 100644 --- a/samples/bluetooth/st_ble_sensor/README.rst +++ b/samples/boards/st/bluetooth/st_ble_sensor/README.rst @@ -24,7 +24,7 @@ Building and Running Build and flash the sample as follows, replacing ```` with your target board: .. zephyr-app-commands:: - :zephyr-app: samples/bluetooth/st_ble_sensor + :zephyr-app: samples/boards/st/bluetooth/st_ble_sensor :board: :goals: build flash :compact: diff --git a/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay b/samples/boards/st/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay similarity index 100% rename from samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay rename to samples/boards/st/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk.overlay diff --git a/samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf b/samples/boards/st/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf similarity index 100% rename from samples/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf rename to samples/boards/st/bluetooth/st_ble_sensor/boards/stm32wb5mm_dk_stm32wb55xx.conf diff --git a/samples/bluetooth/st_ble_sensor/prj.conf b/samples/boards/st/bluetooth/st_ble_sensor/prj.conf similarity index 100% rename from samples/bluetooth/st_ble_sensor/prj.conf rename to samples/boards/st/bluetooth/st_ble_sensor/prj.conf diff --git a/samples/bluetooth/st_ble_sensor/src/button_svc.c b/samples/boards/st/bluetooth/st_ble_sensor/src/button_svc.c similarity index 100% rename from samples/bluetooth/st_ble_sensor/src/button_svc.c rename to samples/boards/st/bluetooth/st_ble_sensor/src/button_svc.c diff --git a/samples/bluetooth/st_ble_sensor/src/button_svc.h b/samples/boards/st/bluetooth/st_ble_sensor/src/button_svc.h similarity index 100% rename from samples/bluetooth/st_ble_sensor/src/button_svc.h rename to samples/boards/st/bluetooth/st_ble_sensor/src/button_svc.h diff --git a/samples/bluetooth/st_ble_sensor/src/led_svc.c b/samples/boards/st/bluetooth/st_ble_sensor/src/led_svc.c similarity index 100% rename from samples/bluetooth/st_ble_sensor/src/led_svc.c rename to samples/boards/st/bluetooth/st_ble_sensor/src/led_svc.c diff --git a/samples/bluetooth/st_ble_sensor/src/led_svc.h b/samples/boards/st/bluetooth/st_ble_sensor/src/led_svc.h similarity index 100% rename from samples/bluetooth/st_ble_sensor/src/led_svc.h rename to samples/boards/st/bluetooth/st_ble_sensor/src/led_svc.h diff --git a/samples/bluetooth/st_ble_sensor/src/main.c b/samples/boards/st/bluetooth/st_ble_sensor/src/main.c similarity index 100% rename from samples/bluetooth/st_ble_sensor/src/main.c rename to samples/boards/st/bluetooth/st_ble_sensor/src/main.c diff --git a/samples/bluetooth/st_ble_sensor/src/rgb_led_svc.c b/samples/boards/st/bluetooth/st_ble_sensor/src/rgb_led_svc.c similarity index 100% rename from samples/bluetooth/st_ble_sensor/src/rgb_led_svc.c rename to samples/boards/st/bluetooth/st_ble_sensor/src/rgb_led_svc.c diff --git a/samples/bluetooth/st_ble_sensor/tests.yaml b/samples/boards/st/bluetooth/st_ble_sensor/tests.yaml similarity index 86% rename from samples/bluetooth/st_ble_sensor/tests.yaml rename to samples/boards/st/bluetooth/st_ble_sensor/tests.yaml index 164bad2fcf35..59a0cc0a228c 100644 --- a/samples/bluetooth/st_ble_sensor/tests.yaml +++ b/samples/boards/st/bluetooth/st_ble_sensor/tests.yaml @@ -3,7 +3,7 @@ sample: description: Demonstrates Bluetooth LE peripheral by exposing vendor-specific GATT services tests: - sample.bluetooth.st_ble_sensor: + sample.boards.st.bluetooth.st_ble_sensor: harness: bluetooth platform_allow: - nucleo_wb55rg From 584e1bac85b4781f4f6fa1dcce706738605ccb02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 18 Jul 2026 08:20:29 +0000 Subject: [PATCH 106/455] drivers: i3c: adopt driver_ops convention for target device API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce documented typedefs for the I3C target device driver operations, group the backend definitions in a dedicated backend API group and document the driver API structure with the driver_ops Doxygen commands, tagging each operation as mandatory. Signed-off-by: Benjamin Cabé --- include/zephyr/drivers/i3c/target_device.h | 32 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/include/zephyr/drivers/i3c/target_device.h b/include/zephyr/drivers/i3c/target_device.h index d8706be106e5..44a997a3ce45 100644 --- a/include/zephyr/drivers/i3c/target_device.h +++ b/include/zephyr/drivers/i3c/target_device.h @@ -272,11 +272,39 @@ struct i3c_target_callbacks { int (*controller_handoff_cb)(struct i3c_target_config *config); }; +/** + * @def_driverbackendgroup{I3C Target Device,i3c_target_device} + * @{ + */ + +/** + * @brief Instruct the I3C target device driver to register itself with its bus controller. + */ +typedef int (*i3c_target_api_driver_register_t)(const struct device *dev); + +/** + * @brief Instruct the I3C target device driver to unregister itself from its bus controller. + */ +typedef int (*i3c_target_api_driver_unregister_t)(const struct device *dev); + +/** + * @driver_ops{I3C Target Device} + */ __subsystem struct i3c_target_driver_api { - int (*driver_register)(const struct device *dev); - int (*driver_unregister)(const struct device *dev); + /** + * @driver_ops_mandatory Instruct the I3C target device driver to register itself with + * its bus controller. + */ + i3c_target_api_driver_register_t driver_register; + /** + * @driver_ops_mandatory Instruct the I3C target device driver to unregister itself from + * its bus controller. + */ + i3c_target_api_driver_unregister_t driver_unregister; }; +/** @} */ + /** * @brief Accept or Decline Controller Handoffs * From 3add82a0caaaf49439a55bec29fe85ffd4f51666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Fri, 31 Jul 2026 16:57:48 +0200 Subject: [PATCH 107/455] dts: add DT_ANY_COMPAT/DT_ALL_COMPAT macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add DT_ANY_COMPAT/DT_ALL_COMPAT macros, similar to the DT_ANY_INST/DT_ALL_INST macros. Also based the inst versions on the compat versions. Assisted-by: Codex:GPT-5.4 Signed-off-by: Fin Maaß --- include/zephyr/devicetree.h | 202 ++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 102 deletions(-) diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index a83ee2ee2e6a..00372e08c25f 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -5734,12 +5734,11 @@ * DT_ANY_INST_HAS_PROP_STATUS_OKAY(baz) // 0 * @endcode */ -#define DT_ANY_INST_HAS_PROP_STATUS_OKAY(prop) \ - UTIL_NOT(IS_EMPTY( \ - DT_INST_FOREACH_STATUS_OKAY_VARGS(DT_ANY_INST_HAS_PROP_STATUS_OKAY_, prop))) +#define DT_ANY_INST_HAS_PROP_STATUS_OKAY(prop) \ + DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(DT_DRV_COMPAT, prop) /** - * @brief Check if all `DT_DRV_COMPAT` node with status `okay` has a given + * @brief Check if all `DT_DRV_COMPAT` nodes with status `okay` have a given * property. If all nodes are disabled, this will return 1. * * @param prop lowercase-and-underscores property name @@ -5783,10 +5782,10 @@ * @endcode */ #define DT_ALL_INST_HAS_PROP_STATUS_OKAY(prop) \ - IS_EMPTY(DT_INST_FOREACH_STATUS_OKAY_VARGS(DT_ALL_INST_HAS_PROP_STATUS_OKAY_, prop)) + DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(DT_DRV_COMPAT, prop) /** - * @brief Check if any device node with status `okay` has a given + * @brief Check if any device node with compatible @p compat and status `okay` has a given * property. * * @param compat lowercase-and-underscores devicetree compatible @@ -5829,12 +5828,55 @@ * DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_some_sensor, baz) // 0 * @endcode */ -#define DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(compat, prop) \ - (DT_COMPAT_FOREACH_STATUS_OKAY_VARGS(compat, DT_COMPAT_NODE_HAS_PROP_AND_OR, prop) 0) +#define DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(compat, prop) \ + UTIL_NOT(IS_EMPTY(DT_COMPAT_FOREACH_STATUS_OKAY_VARGS( \ + compat, DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY_INTERNAL, prop))) + +/** + * @brief Check if all device nodes with compatible @p compat and status `okay` have a given + * property. If all nodes with compatible @p compat are disabled, this will return 1. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param prop lowercase-and-underscores property name + */ +#define DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(compat, prop) \ + IS_EMPTY(DT_COMPAT_FOREACH_STATUS_OKAY_VARGS( \ + compat, DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY_INTERNAL, prop)) + +/** + * @brief Check if any device node with compatible @p compat and status `okay` has a given + * boolean property that exists and is enabled. + * + * This differs from @ref DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY because even when + * not present on a node, the boolean property is generated with a value of 0 + * and therefore exists. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param prop lowercase-and-underscores property name + */ +#define DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(compat, prop) \ + UTIL_NOT(IS_EMPTY(DT_COMPAT_FOREACH_STATUS_OKAY_VARGS( \ + compat, DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY_INTERNAL, prop))) + +/** + * @brief Check if all device nodes with compatible @p compat and status `okay` have a given boolean + * property that exists and is enabled. If all nodes with compatible @p compat are disabled, + * this will return 1. + * + * This differs from @ref DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY because even when + * not present on a node, the boolean property is generated with a value of 0 + * and therefore exists. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param prop lowercase-and-underscores property name + */ +#define DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(compat, prop) \ + IS_EMPTY(DT_COMPAT_FOREACH_STATUS_OKAY_VARGS( \ + compat, DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY_INTERNAL, prop)) /** * @brief Check if any `DT_DRV_COMPAT` node with status `okay` has a given - * boolean property that exists. + * boolean property that exists and is enabled. * * This differs from @ref DT_ANY_INST_HAS_PROP_STATUS_OKAY because even when not present * on a node, the boolean property is generated with a value of 0 and therefore exists. @@ -5879,15 +5921,14 @@ * DT_ANY_INST_HAS_BOOL_STATUS_OKAY(baz) // 0 * @endcode */ -#define DT_ANY_INST_HAS_BOOL_STATUS_OKAY(prop) \ - UTIL_NOT(IS_EMPTY( \ - DT_INST_FOREACH_STATUS_OKAY_VARGS(DT_ANY_INST_HAS_BOOL_STATUS_OKAY_, prop))) +#define DT_ANY_INST_HAS_BOOL_STATUS_OKAY(prop) \ + DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(DT_DRV_COMPAT, prop) /** - * @brief Check if all `DT_DRV_COMPAT` node with status `okay` has a given - * boolean property that exists. If all nodes are disabled, this + * @brief Check if all `DT_DRV_COMPAT` nodes with status `okay` have a given + * boolean property that exists and is enabled. If all nodes are disabled, this * will return 1. - * * + * * @param prop lowercase-and-underscores property name * * Example devicetree overlay: @@ -5929,7 +5970,29 @@ * @endcode */ #define DT_ALL_INST_HAS_BOOL_STATUS_OKAY(prop) \ - IS_EMPTY(DT_INST_FOREACH_STATUS_OKAY_VARGS(DT_ALL_INST_HAS_BOOL_STATUS_OKAY_, prop)) + DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(DT_DRV_COMPAT, prop) + +/** + * @brief Check if any device node with compatible @p compat and status `okay` has a given register + * name. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param name lowercase-and-underscores register name + */ +#define DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(compat, name) \ + UTIL_NOT(IS_EMPTY(DT_COMPAT_FOREACH_STATUS_OKAY_VARGS( \ + compat, DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY_INTERNAL, name))) + +/** + * @brief Check if all device nodes with compatible @p compat and status `okay` have a given + * register name. If all nodes with compatible @p compat are disabled, this will return 1. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param name lowercase-and-underscores register name + */ +#define DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(compat, name) \ + IS_EMPTY(DT_COMPAT_FOREACH_STATUS_OKAY_VARGS( \ + compat, DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY_INTERNAL, name)) /** * @brief Check if any `DT_DRV_COMPAT` node with status `okay` has a given @@ -5937,9 +6000,8 @@ * * @param name lowercase-and-underscores register name */ -#define DT_ANY_INST_REG_HAS_NAME_STATUS_OKAY(name) \ - UTIL_NOT(IS_EMPTY( \ - DT_INST_FOREACH_STATUS_OKAY_VARGS(DT_ANY_INST_REG_HAS_NAME_STATUS_OKAY_, name))) +#define DT_ANY_INST_REG_HAS_NAME_STATUS_OKAY(name) \ + DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(DT_DRV_COMPAT, name) /** * @brief Check if all `DT_DRV_COMPAT` node with status `okay` has a given @@ -5948,7 +6010,7 @@ * @param name lowercase-and-underscores register name */ #define DT_ALL_INST_REG_HAS_NAME_STATUS_OKAY(name) \ - IS_EMPTY(DT_INST_FOREACH_STATUS_OKAY_VARGS(DT_ALL_INST_REG_HAS_NAME_STATUS_OKAY_, name)) + DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(DT_DRV_COMPAT, name) /** * @brief Call @p fn on all nodes with compatible `DT_DRV_COMPAT` @@ -6219,93 +6281,29 @@ /** @cond INTERNAL_HIDDEN */ -/** @brief Helper for DT_ANY_INST_HAS_PROP_STATUS_OKAY - * - * This macro generates token "1," for instance of a device, - * identified by index @p inst, if instance has property @p prop. - * - * @param inst instance number - * @param prop property to check for - * - * @return Macro evaluates to `1,` if instance has the property, - * otherwise it evaluates to literal nothing. - */ -#define DT_ANY_INST_HAS_PROP_STATUS_OKAY_(inst, prop) \ - IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, prop), (1,)) +/** @brief Helper for DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY */ +#define DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY_INTERNAL(inst, compat, prop) \ + IF_ENABLED(DT_NODE_HAS_PROP(DT_INST(inst, compat), prop), (1,)) -/** @brief Helper for DT_ANY_INST_HAS_BOOL_STATUS_OKAY - * - * This macro generates token "1," for instance of a device, - * identified by index @p inst, if instance has boolean property - * @p prop with value 1. - * - * @param inst instance number - * @param prop property to check for - * - * @return Macro evaluates to `1,` if instance property value is 1, - * otherwise it evaluates to literal nothing. - */ -#define DT_ANY_INST_HAS_BOOL_STATUS_OKAY_(inst, prop) \ - IF_ENABLED(DT_INST_PROP(inst, prop), (1,)) +/** @brief Helper for DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY */ +#define DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY_INTERNAL(inst, compat, prop) \ + IF_ENABLED(DT_PROP(DT_INST(inst, compat), prop), (1,)) -/** @brief Helper for DT_ANY_INST_REG_HAS_NAME_STATUS_OKAY - * - * This macro generates token "1," for instance of a device, - * identified by index @p inst, if instance has named register - * @p name. - * - * @param inst instance number - * @param name register name to check for - * - * @return Macro evaluates to `1,` if instance register name exists, - * otherwise it evaluates to literal nothing. - */ -#define DT_ANY_INST_REG_HAS_NAME_STATUS_OKAY_(inst, name) \ - IF_ENABLED(DT_INST_REG_HAS_NAME(inst, name), (1,)) +/** @brief Helper for DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY */ +#define DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY_INTERNAL(inst, compat, name) \ + IF_ENABLED(DT_REG_HAS_NAME(DT_INST(inst, compat), name), (1,)) -/** @brief Helper for DT_ALL_INST_HAS_PROP_STATUS_OKAY - * - * This macro generates token "1," for instance of a device, - * identified by index @p inst, if instance has no property @p prop. - * - * @param inst instance number - * @param prop property to check for - * - * @return Macro evaluates to `1,` if instance has the property, - * otherwise it evaluates to literal nothing. - */ -#define DT_ALL_INST_HAS_PROP_STATUS_OKAY_(inst, prop) \ - IF_DISABLED(DT_INST_NODE_HAS_PROP(inst, prop), (1,)) +/** @brief Helper for DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY */ +#define DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY_INTERNAL(inst, compat, prop) \ + IF_DISABLED(DT_NODE_HAS_PROP(DT_INST(inst, compat), prop), (1,)) -/** @brief Helper for DT_ALL_INST_HAS_BOOL_STATUS_OKAY - * - * This macro generates token "1," for instance of a device, - * identified by index @p inst, if instance has no boolean property - * @p prop with value 1. - * - * @param inst instance number - * @param prop property to check for - * - * @return Macro evaluates to `1,` if instance property value is 0, - * otherwise it evaluates to literal nothing. - */ -#define DT_ALL_INST_HAS_BOOL_STATUS_OKAY_(inst, prop) \ - IF_DISABLED(DT_INST_PROP(inst, prop), (1,)) +/** @brief Helper for DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY */ +#define DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY_INTERNAL(inst, compat, prop) \ + IF_DISABLED(DT_PROP(DT_INST(inst, compat), prop), (1,)) -/** @brief Helper for DT_ALL_INST_REG_HAS_NAME_STATUS_OKAY - * - * This macro generates token "1," for instance of a device, - * identified by index @p inst, if instance has no named register - * @p name. - * - * @param inst instance number - * @param name register name to check for - * - * @return Macro evaluates to `1,` if instance register name exists, - * otherwise it evaluates to literal nothing. - */ -#define DT_ALL_INST_REG_HAS_NAME_STATUS_OKAY_(inst, name) \ - IF_DISABLED(DT_INST_REG_HAS_NAME(inst, name), (1,)) +/** @brief Helper for DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY */ +#define DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY_INTERNAL(inst, compat, name) \ + IF_DISABLED(DT_REG_HAS_NAME(DT_INST(inst, compat), name), (1,)) #define DT_PATH_INTERNAL(...) \ UTIL_CAT(DT_ROOT, MACRO_MAP_CAT(DT_S_PREFIX, __VA_ARGS__)) From d31aaca474a012e16efb0efce435c605d877df78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 19 Aug 2026 19:02:12 +0200 Subject: [PATCH 108/455] dts: add notice to not use `_HAS_PROP_STATUS_OKAY on boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add a notice to not use the *_HAS_PROP_STATUS_OKAY macros on boolean properties. Signed-off-by: Fin Maaß --- include/zephyr/devicetree.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index 00372e08c25f..3dd009c3327a 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -5694,6 +5694,10 @@ * @brief Check if any `DT_DRV_COMPAT` node with status `okay` has a given * property. * + * Don't use this macro with a boolean property, use @ref DT_ANY_INST_HAS_BOOL_STATUS_OKAY + * instead. Boolean properties defined for a compatible node always exist, as they are generated + * with a value of 0 when not present on a node. + * * @param prop lowercase-and-underscores property name * * Example devicetree overlay: @@ -5741,6 +5745,10 @@ * @brief Check if all `DT_DRV_COMPAT` nodes with status `okay` have a given * property. If all nodes are disabled, this will return 1. * + * Don't use this macro with a boolean property, use @ref DT_ALL_INST_HAS_BOOL_STATUS_OKAY + * instead. Boolean properties defined for a compatible node always exist, as they are generated + * with a value of 0 when not present on a node. + * * @param prop lowercase-and-underscores property name * * Example devicetree overlay: @@ -5788,6 +5796,10 @@ * @brief Check if any device node with compatible @p compat and status `okay` has a given * property. * + * Don't use this macro with a boolean property, use @ref DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY + * instead. Boolean properties defined for a compatible node always exist, as they are generated + * with a value of 0 when not present on a node. + * * @param compat lowercase-and-underscores devicetree compatible * @param prop lowercase-and-underscores property name * @@ -5836,6 +5848,10 @@ * @brief Check if all device nodes with compatible @p compat and status `okay` have a given * property. If all nodes with compatible @p compat are disabled, this will return 1. * + * Don't use this macro with a boolean property, use @ref DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY + * instead. Boolean properties defined for a compatible node always exist, as they are generated + * with a value of 0 when not present on a node. + * * @param compat lowercase-and-underscores devicetree compatible * @param prop lowercase-and-underscores property name */ From f9c79718e3a1dd3c271a23ecb939b0fffc890e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Fri, 31 Jul 2026 17:16:19 +0200 Subject: [PATCH 109/455] dts: tests: add for DT_ANY_COMPAT/DT_ALL_COMPAT macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add tests for the DT_ANY_COMPAT/DT_ALL_COMPAT macros. Assisted-by: Codex:GPT-5.4 Signed-off-by: Fin Maaß --- tests/lib/devicetree/api/src/main.c | 79 +++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/tests/lib/devicetree/api/src/main.c b/tests/lib/devicetree/api/src/main.c index 797a4dc729ab..65b5333aa6b0 100644 --- a/tests/lib/devicetree/api/src/main.c +++ b/tests/lib/devicetree/api/src/main.c @@ -246,6 +246,30 @@ ZTEST(devicetree_api, test_inst_props) #undef DT_DRV_COMPAT #define DT_DRV_COMPAT vnd_reg_holder_2 +ZTEST(devicetree_api, test_any_compat_reg_names) +{ + zexpect_equal(DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, foo), 1, ""); + zexpect_equal(DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, bar), 1, ""); + zexpect_equal(DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, baz), 0, ""); + zexpect_equal(DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, does_not_exist), 0, + ""); + zexpect_equal(COND_CODE_1(DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, foo), + (5), (6)), 5, ""); + zexpect_true(IS_ENABLED(DT_ANY_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, foo)), ""); +} + +ZTEST(devicetree_api, test_all_compat_reg_names) +{ + zexpect_equal(DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, foo), 1, ""); + zexpect_equal(DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, bar), 0, ""); + zexpect_equal(DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, baz), 0, ""); + zexpect_equal(DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, does_not_exist), 0, + ""); + zexpect_equal(COND_CODE_1(DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, foo), + (5), (6)), 5, ""); + zexpect_true(IS_ENABLED(DT_ALL_COMPAT_REG_HAS_NAME_STATUS_OKAY(vnd_reg_holder_2, foo)), ""); +} + ZTEST(devicetree_api, test_any_inst_reg_names) { zassert_equal(DT_ANY_INST_REG_HAS_NAME_STATUS_OKAY(foo), 1, ""); @@ -347,15 +371,62 @@ ZTEST(devicetree_api, test_all_inst_prop) #undef DT_DRV_COMPAT ZTEST(devicetree_api, test_any_compat_inst_prop) { - zassert_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo), 1, ""); - zassert_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, bar), 1, ""); - zassert_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, baz), 0, ""); - zassert_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, does_not_exist), + zexpect_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo), 1, ""); + zexpect_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, bar), 1, ""); + zexpect_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, baz), 0, ""); + zexpect_equal(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, does_not_exist), + 0, ""); + zexpect_equal(COND_CODE_1(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo), + (5), (6)), 5, ""); + zexpect_true(IS_ENABLED(DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo)), + ""); +} + +ZTEST(devicetree_api, test_all_compat_inst_prop) +{ + zexpect_equal(DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo), 1, ""); + zexpect_equal(DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, bar), 0, ""); + zexpect_equal(DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, baz), 0, ""); + zexpect_equal(DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, does_not_exist), 0, ""); + zexpect_equal(COND_CODE_1(DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo), + (5), (6)), 5, ""); + zexpect_true(IS_ENABLED(DT_ALL_COMPAT_HAS_PROP_STATUS_OKAY(vnd_device_with_props, foo)), + ""); } #undef DT_DRV_COMPAT #define DT_DRV_COMPAT vnd_device_with_props +ZTEST(devicetree_api, test_any_compat_bool) +{ + zexpect_equal(DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_foo), 1, ""); + zexpect_equal(DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_bar), 1, ""); + zexpect_equal(DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_baz), 0, ""); + zexpect_equal(DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, does_not_exist), 0, + ""); + zexpect_equal( + COND_CODE_1(DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_foo), + (5), (6)), 5, ""); + zexpect_true( + IS_ENABLED(DT_ANY_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_foo)), + ""); +} + +ZTEST(devicetree_api, test_all_compat_bool) +{ + zexpect_equal(DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_foo), 1, ""); + zexpect_equal(DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_bar), 0, ""); + zexpect_equal(DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_baz), 0, ""); + zexpect_equal(DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, does_not_exist), 0, + ""); + zexpect_equal( + COND_CODE_1(DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_foo), + (5), (6)), 5, ""); + zexpect_true( + IS_ENABLED(DT_ALL_COMPAT_HAS_BOOL_STATUS_OKAY(vnd_device_with_props, bool_foo)), + ""); +} + ZTEST(devicetree_api, test_any_inst_bool) { zassert_equal(DT_ANY_INST_HAS_BOOL_STATUS_OKAY(bool_foo), 1, ""); From a763943345793986c837312a21b96c80dfe75106 Mon Sep 17 00:00:00 2001 From: Dmitry Rantovov Date: Tue, 25 Aug 2026 14:32:27 +0300 Subject: [PATCH 110/455] include: storage: flash_map: fix `fic` param name in sha256 check `flash_area_check_int_sha256()` takes `fac`, the docblock documents `fic`. Signed-off-by: Dmitry Rantovov Assisted-by: Claude:claude-opus-5 --- include/zephyr/storage/flash_map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/zephyr/storage/flash_map.h b/include/zephyr/storage/flash_map.h index f6c6d1b608ae..51985187fa45 100644 --- a/include/zephyr/storage/flash_map.h +++ b/include/zephyr/storage/flash_map.h @@ -103,7 +103,7 @@ struct flash_area_check { * point is indicated by an offset value. * * @param[in] fa Flash area - * @param[in] fic Flash area check integrity data + * @param[in] fac Flash area check integrity data * * @return 0 on success, negative errno code on fail */ From 985c83cb79cb404968c1df3cf379541a9f356765 Mon Sep 17 00:00:00 2001 From: Sven Haedrich Date: Sun, 9 Aug 2026 18:51:24 +0200 Subject: [PATCH 111/455] boards: shields: Add Mikroe DALI 2 click shield The product photo is taken at sevenlab engineering. Tested with the command in the index.rst file. Hardware tested with Arduino UNO click shield, nRF52840-DK and nucleo_f091rc. Signed-off-by: Sven Haedrich --- .../mikroe_dali_2_click/Kconfig.shield | 5 ++ .../doc/images/dali_2_click.webp | Bin 0 -> 30468 bytes .../shields/mikroe_dali_2_click/doc/index.rst | 47 ++++++++++++++++++ .../mikroe_dali_2_click.overlay | 27 ++++++++++ boards/shields/mikroe_dali_2_click/shield.yml | 9 ++++ 5 files changed, 88 insertions(+) create mode 100644 boards/shields/mikroe_dali_2_click/Kconfig.shield create mode 100644 boards/shields/mikroe_dali_2_click/doc/images/dali_2_click.webp create mode 100644 boards/shields/mikroe_dali_2_click/doc/index.rst create mode 100644 boards/shields/mikroe_dali_2_click/mikroe_dali_2_click.overlay create mode 100644 boards/shields/mikroe_dali_2_click/shield.yml diff --git a/boards/shields/mikroe_dali_2_click/Kconfig.shield b/boards/shields/mikroe_dali_2_click/Kconfig.shield new file mode 100644 index 000000000000..b4d3b2f9366a --- /dev/null +++ b/boards/shields/mikroe_dali_2_click/Kconfig.shield @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright (c) 2026 sevenlab engineering GmbH + +config SHIELD_MIKROE_DALI_2_CLICK + def_bool $(shields_list_contains,mikroe_dali_2_click) diff --git a/boards/shields/mikroe_dali_2_click/doc/images/dali_2_click.webp b/boards/shields/mikroe_dali_2_click/doc/images/dali_2_click.webp new file mode 100644 index 0000000000000000000000000000000000000000..c0aa5c43e7c9f8af5f6cd402bbd6e8c40c0a1c31 GIT binary patch literal 30468 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 LZ%PP-zyJUMpK4a8 literal 0 HcmV?d00001 diff --git a/boards/shields/mikroe_dali_2_click/doc/index.rst b/boards/shields/mikroe_dali_2_click/doc/index.rst new file mode 100644 index 000000000000..58bafecc1b24 --- /dev/null +++ b/boards/shields/mikroe_dali_2_click/doc/index.rst @@ -0,0 +1,47 @@ +.. _mikroe_dali_2_click_shield: + +MikroElektronika DALI 2 Click +############################# + +Overview +******** + +The DALI 2 Click shield provides a physical interface to a DALI bus. +The DALI bus interface uses separate Rx and Tx signals to communicate with the DALI bus. +The board uses optocouplers to isolate the DALI bus from the host board. + +More information about the shield can be found at +`Mikroe DALI 2 click`_. + +.. figure:: images/dali_2_click.webp + :align: center + :height: 300px + :alt: MikroElektronika DALI 2 Click + + MikroElektronika DALI 2 Click + +Requirements +************ + +The shield uses a mikroBUS interface. +The target board must define ``mikrobus_header`` node labels +(see :ref:`shields` for more details). + +Programming +*********** + +Set ``--shield mikroe_dali_2_click`` when you invoke ``west build``. For example: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/dali/ + :board: + :shield: mikroe_dali_2_click + :goals: build flash + +References +********** + +.. target-notes:: + +.. _Mikroe DALI 2 click: + https://www.mikroe.com/dali-2-click diff --git a/boards/shields/mikroe_dali_2_click/mikroe_dali_2_click.overlay b/boards/shields/mikroe_dali_2_click/mikroe_dali_2_click.overlay new file mode 100644 index 000000000000..f2eaf43f6e3e --- /dev/null +++ b/boards/shields/mikroe_dali_2_click/mikroe_dali_2_click.overlay @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Copyright (c) 2026 sevenlab engineering GmbH + */ + +/ { + aliases { + dali = &mikroe_dali2_click; + }; +}; + +/ { + mikroe_dali2_click: dali2_click { + status = "okay"; + compatible = "zephyr,dali-pwm"; + + rx-gpios = <&mikrobus_header 7 GPIO_ACTIVE_HIGH>; + tx-flank-shift-us = <(-46)>; + rx-flank-shift-us = <(-68)>; + + /* override the following based on your board */ + counter = <&timer2>; + chan-id-rx = <0>; + chan-id-tx = <1>; + pwms = <&pwm0 0 PWM_USEC(41) PWM_POLARITY_NORMAL>; + }; +}; diff --git a/boards/shields/mikroe_dali_2_click/shield.yml b/boards/shields/mikroe_dali_2_click/shield.yml new file mode 100644 index 000000000000..724541fecc6e --- /dev/null +++ b/boards/shields/mikroe_dali_2_click/shield.yml @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright (c) 2026 sevenlab engineering GmbH + +shield: + name: mikroe_dali_2_click + full_name: DALI 2 Click + vendor: mikroe + supported_features: + - dali From 32ac391680ed042a1de56b5655548347072b9531 Mon Sep 17 00:00:00 2001 From: Sven Haedrich Date: Sun, 9 Aug 2026 19:05:59 +0200 Subject: [PATCH 112/455] boards: st: configure UART for aduino header The arduino header connections D0 and D1 are routed to UART1. Without the routing the Arduino UNO click shield fails to build correctly. Signed-off-by: Sven Haedrich --- boards/st/nucleo_f091rc/arduino_r3_connector.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boards/st/nucleo_f091rc/arduino_r3_connector.dtsi b/boards/st/nucleo_f091rc/arduino_r3_connector.dtsi index 90741ad5bdff..8fb6cc20ab22 100644 --- a/boards/st/nucleo_f091rc/arduino_r3_connector.dtsi +++ b/boards/st/nucleo_f091rc/arduino_r3_connector.dtsi @@ -40,3 +40,5 @@ arduino_i2c: &i2c1 {}; arduino_spi: &spi1 {}; + +arduino_serial: &usart1 {}; From 7e2e8df4f593d6b252e30bf9d300ec64351b7271 Mon Sep 17 00:00:00 2001 From: Sven Haedrich Date: Sun, 9 Aug 2026 19:10:07 +0200 Subject: [PATCH 113/455] drivers: dali: use shield definition Use the definition for the Mikroe DALI 2 click shield. Signed-off-by: Sven Haedrich --- samples/drivers/dali/README.rst | 2 + .../dali/boards/nrf52840dk_nrf52840.overlay | 41 +++++-------------- .../drivers/dali/boards/nucleo_f091rc.overlay | 27 ++++-------- samples/drivers/dali/src/main.c | 2 +- samples/drivers/dali/tests.yaml | 20 +++++++-- 5 files changed, 39 insertions(+), 53 deletions(-) diff --git a/samples/drivers/dali/README.rst b/samples/drivers/dali/README.rst index 335c5e8f0746..ae1a8ddf6a2a 100644 --- a/samples/drivers/dali/README.rst +++ b/samples/drivers/dali/README.rst @@ -32,6 +32,7 @@ The sample can be build and executed for the :zephyr-app: samples/drivers/dali :board: nucleo_f091rc :goals: build flash + :shield: arduino_uno_click,mikroe_dali_2_click :compact: Building and Running for Nordic nRF52840 @@ -49,6 +50,7 @@ The sample can be build and executed for the :zephyr-app: samples/drivers/dali :board: nrf52840dk :goals: build flash + :shield: arduino_uno_click,mikroe_dali_2_click :compact: Sample output diff --git a/samples/drivers/dali/boards/nrf52840dk_nrf52840.overlay b/samples/drivers/dali/boards/nrf52840dk_nrf52840.overlay index 0f6532b0d075..01ff20d012be 100644 --- a/samples/drivers/dali/boards/nrf52840dk_nrf52840.overlay +++ b/samples/drivers/dali/boards/nrf52840dk_nrf52840.overlay @@ -2,48 +2,29 @@ * Copyright 2025 by Markus Becker * SPDX-License-Identifier: Apache-2.0 * - * add DALI 2 Click (MIKROE-2672) - * via Arduino UNO click shield (MIKROE-1581) - * to nRF52840 DK (pca10056) - * DALI 2 Click - * DALI TX 2 - * DALI RX 15 - * Arduino UNO click shield (slot 1) - * DALI TX 2 > A3 - * DALI RX 15 > D2 - * nRF52840 DK: - * DALI TX A3 > P0.29 - * DALI RX D2 > P1.03 + * You need to include the following shields: + * arduino_uno_click + * mikroe_dali_2_click + * */ -/ { - dali0: dali { - compatible = "zephyr,dali-pwm"; - status = "okay"; - counter = <&timer2>; - chan-id-rx = <0>; - chan-id-tx = <1>; - pwms = <&pwm0 0 PWM_USEC(41) PWM_POLARITY_NORMAL>; - rx-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; // D2 - tx-flank-shift-us = <(-38)>; - rx-flank-shift-us = <(-59)>; - tx-rx-propagation-max-us = <2400>; - tx-prog-delay-us = <(-320)>; - rx-max-latency-us = <80>; - rx-grey-area-us = <800>; - }; +&mikroe_dali2_click { + tx-rx-propagation-max-us = <2400>; + tx-prog-delay-us = <(-320)>; + rx-max-latency-us = <80>; + rx-grey-area-us = <800>; }; &pinctrl { pwm0_default_dali: pwm0_default_dali { group1 { - psels = ; // A3 + psels = ; // mikrobus_header_1 2 }; }; pwm0_sleep_dali: pwm0_sleep_dali { group1 { - psels = ; // A3 + psels = ; // mikrobus_header_1 2 low-power-enable; }; }; diff --git a/samples/drivers/dali/boards/nucleo_f091rc.overlay b/samples/drivers/dali/boards/nucleo_f091rc.overlay index c7313748d324..97a6d4031ab8 100644 --- a/samples/drivers/dali/boards/nucleo_f091rc.overlay +++ b/samples/drivers/dali/boards/nucleo_f091rc.overlay @@ -8,7 +8,7 @@ * DALI 2 Click * DALI TX 2 * DALI RX 15 - * Arduino UNO click shield (slot 2) + * Arduino UNO click shield (slot 1) * DALI TX 2 > A3 * DALI RX 15 > D2 * STM32F091 @@ -16,22 +16,13 @@ * DALI RX D2 > PA_10 */ -/ { - dali0: dali { - compatible = "zephyr,dali-pwm"; - status = "okay"; - counter = <&counter2>; - chan-id-rx = <0>; - chan-id-tx = <1>; - pwms = <&pwm3 3 PWM_USEC(41) PWM_POLARITY_NORMAL>; - rx-gpios = <&gpioa 10 GPIO_ACTIVE_HIGH>; - tx-flank-shift-us = <(-46)>; - rx-flank-shift-us = <(-68)>; - tx-rx-propagation-max-us = <2400>; - tx-prog-delay-us = <(-200)>; - rx-max-latency-us = <80>; - rx-grey-area-us = <800>; - }; +&mikroe_dali2_click { + counter = <&counter2>; + pwms = <&pwm3 3 PWM_USEC(41) PWM_POLARITY_NORMAL>; + tx-rx-propagation-max-us = <2400>; + tx-prog-delay-us = <(-200)>; + rx-max-latency-us = <80>; + rx-grey-area-us = <800>; }; &timers2 { @@ -52,7 +43,7 @@ status = "okay"; pwm3: pwm { - pinctrl-0 = <&tim3_ch3_pb0>; + pinctrl-0 = <&tim3_ch3_pb0>; // microbus header 1 pinctrl-names = "default"; status = "okay"; }; diff --git a/samples/drivers/dali/src/main.c b/samples/drivers/dali/src/main.c index 51f1bb3d9012..a3cf1ef269ff 100644 --- a/samples/drivers/dali/src/main.c +++ b/samples/drivers/dali/src/main.c @@ -12,7 +12,7 @@ #include LOG_MODULE_REGISTER(main); -#define DALI_NODE DT_NODELABEL(dali0) +#define DALI_NODE DT_ALIAS(dali) int main(void) { diff --git a/samples/drivers/dali/tests.yaml b/samples/drivers/dali/tests.yaml index 5b0981356d59..26569a592f75 100644 --- a/samples/drivers/dali/tests.yaml +++ b/samples/drivers/dali/tests.yaml @@ -1,15 +1,27 @@ sample: description: Dali Zephyr driver application name: dali-zephyr +common: + tags: dali + filter: dt_nodelabel_enabled("dali0") tests: - sample.drivers.dali: - tags: dali - integration_platforms: + sample.drivers.dali.nucleo_f091rc: + platform_allow: - nucleo_f091rc + harness: console + harness_config: + type: one_line + regex: + - "Target board: (.*)" + extra_args: + - SHIELD=arduino_uno_click;mikroe_dali_2_click + sample.drivers.dali.nrf52840dk: + platform_allow: - nrf52840dk/nrf52840 - filter: dt_nodelabel_enabled("dali0") harness: console harness_config: type: one_line regex: - "Target board: (.*)" + extra_args: + - SHIELD=arduino_uno_click;mikroe_dali_2_click From f74cde92f6ea5a23e28d84f4564e442d416039aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 17:51:27 +0000 Subject: [PATCH 114/455] drivers: sensor: iis2iclx: fix undeclared 'data' in LPS22HH attr path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iis2iclx_lps22hh_conf() passed an undeclared identifier 'data' to iis2iclx_lps22hh_odr_set(), breaking any build with CONFIG_IIS2ICLX_EXT_LPS22HH enabled. Pass the function's 'dev' parameter instead, matching the callee signature and the sibling ST sensor-hub drivers. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/iis2iclx/iis2iclx_shub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sensor/st/iis2iclx/iis2iclx_shub.c b/drivers/sensor/st/iis2iclx/iis2iclx_shub.c index 3153ef8aab49..0ba02f5470b5 100644 --- a/drivers/sensor/st/iis2iclx/iis2iclx_shub.c +++ b/drivers/sensor/st/iis2iclx/iis2iclx_shub.c @@ -334,7 +334,7 @@ static int iis2iclx_lps22hh_conf(const struct device *dev, uint8_t i2c_addr, { switch (attr) { case SENSOR_ATTR_SAMPLING_FREQUENCY: - return iis2iclx_lps22hh_odr_set(data, i2c_addr, val->val1); + return iis2iclx_lps22hh_odr_set(dev, i2c_addr, val->val1); default: LOG_ERR("shub: LPS22HH attribute not supported."); return -ENOTSUP; From 658b4237c3682f8acef553193464b76067a686f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 17:52:23 +0000 Subject: [PATCH 115/455] drivers: sensor: iis2iclx: reject ACCEL_Z and propagate get errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iis2iclx_channel_get() accepted SENSOR_CHAN_ACCEL_Z on this 2-axis part and discarded the -ENOTSUP returned by its helpers, so callers got success with an untouched sensor_value. Drop the Z case, return the helper status, and fill the third slot of ACCEL_XYZ with zero. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/iis2iclx/iis2iclx.c | 43 ++++++++++++++------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/sensor/st/iis2iclx/iis2iclx.c b/drivers/sensor/st/iis2iclx/iis2iclx.c index 06a7b5493be2..c6db5161b00a 100644 --- a/drivers/sensor/st/iis2iclx/iis2iclx.c +++ b/drivers/sensor/st/iis2iclx/iis2iclx.c @@ -295,6 +295,9 @@ static inline int iis2iclx_accel_get_channel(enum sensor_channel chan, for (i = 0; i < 2; i++) { iis2iclx_accel_convert(val++, data->acc[i], sensitivity); } + /* 2-axis part: there is no Z output register, report a defined 0 */ + val->val1 = 0; + val->val2 = 0; break; default: return -ENOTSUP; @@ -375,8 +378,8 @@ static inline int iis2iclx_magn_get_channel(enum sensor_channel chan, return 0; } -static inline void iis2iclx_hum_convert(struct sensor_value *val, - struct iis2iclx_data *data) +static inline int iis2iclx_hum_convert(struct sensor_value *val, + struct iis2iclx_data *data) { float rh; int16_t raw_val; @@ -386,7 +389,7 @@ static inline void iis2iclx_hum_convert(struct sensor_value *val, idx = iis2iclx_shub_get_idx(data->dev, SENSOR_CHAN_HUMIDITY); if (idx < 0) { LOG_DBG("external press/temp not supported"); - return; + return -ENOTSUP; } raw_val = ((int16_t)(data->ext_data[idx][0] | @@ -399,10 +402,12 @@ static inline void iis2iclx_hum_convert(struct sensor_value *val, /* convert humidity to integer and fractional part */ val->val1 = rh; val->val2 = rh * 1000000; + + return 0; } -static inline void iis2iclx_press_convert(struct sensor_value *val, - struct iis2iclx_data *data) +static inline int iis2iclx_press_convert(struct sensor_value *val, + struct iis2iclx_data *data) { int32_t raw_val; int idx; @@ -410,7 +415,7 @@ static inline void iis2iclx_press_convert(struct sensor_value *val, idx = iis2iclx_shub_get_idx(data->dev, SENSOR_CHAN_PRESS); if (idx < 0) { LOG_DBG("external press/temp not supported"); - return; + return -ENOTSUP; } raw_val = (int32_t)(data->ext_data[idx][0] | @@ -422,10 +427,12 @@ static inline void iis2iclx_press_convert(struct sensor_value *val, val->val1 = (raw_val >> 12) / 10; val->val2 = (raw_val >> 12) % 10 * 100000 + (((int32_t)((raw_val) & 0x0FFF) * 100000L) >> 12); + + return 0; } -static inline void iis2iclx_temp_convert(struct sensor_value *val, - struct iis2iclx_data *data) +static inline int iis2iclx_temp_convert(struct sensor_value *val, + struct iis2iclx_data *data) { int16_t raw_val; int idx; @@ -433,7 +440,7 @@ static inline void iis2iclx_temp_convert(struct sensor_value *val, idx = iis2iclx_shub_get_idx(data->dev, SENSOR_CHAN_PRESS); if (idx < 0) { LOG_DBG("external press/temp not supported"); - return; + return -ENOTSUP; } raw_val = (int16_t)(data->ext_data[idx][3] | @@ -442,6 +449,8 @@ static inline void iis2iclx_temp_convert(struct sensor_value *val, /* Temperature sensitivity is 100 LSB/deg C */ val->val1 = raw_val / 100; val->val2 = (int32_t)raw_val % 100 * (10000); + + return 0; } #endif @@ -454,10 +463,8 @@ static int iis2iclx_channel_get(const struct device *dev, switch (chan) { case SENSOR_CHAN_ACCEL_X: case SENSOR_CHAN_ACCEL_Y: - case SENSOR_CHAN_ACCEL_Z: case SENSOR_CHAN_ACCEL_XYZ: - iis2iclx_accel_channel_get(chan, val, data); - break; + return iis2iclx_accel_channel_get(chan, val, data); #if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) case SENSOR_CHAN_DIE_TEMP: iis2iclx_temp_channel_get(val, data); @@ -473,8 +480,7 @@ static int iis2iclx_channel_get(const struct device *dev, return -ENOTSUP; } - iis2iclx_magn_get_channel(chan, val, data); - break; + return iis2iclx_magn_get_channel(chan, val, data); case SENSOR_CHAN_HUMIDITY: if (!data->shub_inited) { @@ -482,8 +488,7 @@ static int iis2iclx_channel_get(const struct device *dev, return -ENOTSUP; } - iis2iclx_hum_convert(val, data); - break; + return iis2iclx_hum_convert(val, data); case SENSOR_CHAN_PRESS: if (!data->shub_inited) { @@ -491,8 +496,7 @@ static int iis2iclx_channel_get(const struct device *dev, return -ENOTSUP; } - iis2iclx_press_convert(val, data); - break; + return iis2iclx_press_convert(val, data); case SENSOR_CHAN_AMBIENT_TEMP: if (!data->shub_inited) { @@ -500,8 +504,7 @@ static int iis2iclx_channel_get(const struct device *dev, return -ENOTSUP; } - iis2iclx_temp_convert(val, data); - break; + return iis2iclx_temp_convert(val, data); #endif default: return -ENOTSUP; From 3eb7da570d7a31f383de37fff838f6b87337cc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 17:54:21 +0000 Subject: [PATCH 116/455] drivers: sensor: iis2iclx: drop unsupported ODR entries from map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ODR map was copied from the LSM6DSO driver and listed 1.66/3.33/ 6.66 kHz, but the IIS2ICLX ODR_XL field only encodes 0..7 (up to 833 Hz), so requesting a higher rate programmed a reserved code and reported success. Truncate the map so such requests return -EINVAL. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/iis2iclx/iis2iclx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/sensor/st/iis2iclx/iis2iclx.c b/drivers/sensor/st/iis2iclx/iis2iclx.c index c6db5161b00a..cae6e2d23867 100644 --- a/drivers/sensor/st/iis2iclx/iis2iclx.c +++ b/drivers/sensor/st/iis2iclx/iis2iclx.c @@ -22,8 +22,7 @@ LOG_MODULE_REGISTER(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL); -static const uint16_t iis2iclx_odr_map[] = {0, 12, 26, 52, 104, 208, 416, 833, - 1660, 3330, 6660}; +static const uint16_t iis2iclx_odr_map[] = {0, 12, 26, 52, 104, 208, 416, 833}; static int iis2iclx_freq_to_odr_val(uint16_t freq) { From 65188c932c7e20b17c7ad59a1921d2d48f9a467d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 24 Aug 2026 16:13:29 +0200 Subject: [PATCH 117/455] tests: drivers: build_all: sensor: cover IIS2ICLX LPS22HH shub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the IIS2ICLX sensor-hub LPS22HH path, which was broken until now and is not covered by any other build test. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- tests/drivers/build_all/sensor/sensors_shub.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/drivers/build_all/sensor/sensors_shub.conf b/tests/drivers/build_all/sensor/sensors_shub.conf index df5e48184a28..d38386d99b37 100644 --- a/tests/drivers/build_all/sensor/sensors_shub.conf +++ b/tests/drivers/build_all/sensor/sensors_shub.conf @@ -2,6 +2,7 @@ CONFIG_IIS2ICLX_EXT_HTS221=y CONFIG_IIS2ICLX_EXT_LIS2MDL=y CONFIG_IIS2ICLX_EXT_LPS22HB=y +CONFIG_IIS2ICLX_EXT_LPS22HH=y CONFIG_IIS2ICLX_SENSORHUB=y CONFIG_ISM330DHCX_EXT_HTS221=y CONFIG_ISM330DHCX_EXT_LIS2MDL=y From f169f217f2c35646ec1aca326bde87a9e2f9eef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 12 Aug 2026 09:34:06 +0000 Subject: [PATCH 118/455] drivers: sensor: lis2dw12: honour odr=0 power off state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LIS2DW12_ODR_TO_REG() had no encoding for the power off state, so a cached ODR of 0 was mapped to 1.6 Hz and PM_DEVICE_ACTION_RESUME restarted conversions on a sensor configured with odr = <0>. Add an OFF arm to the macro and keep the cached ODR in sync when a runtime power off succeeds. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lis2dw12/lis2dw12.c | 7 ++++++- drivers/sensor/st/lis2dw12/lis2dw12.h | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/sensor/st/lis2dw12/lis2dw12.c b/drivers/sensor/st/lis2dw12/lis2dw12.c index d416dfa93ec0..a9b2cc240e73 100644 --- a/drivers/sensor/st/lis2dw12/lis2dw12.c +++ b/drivers/sensor/st/lis2dw12/lis2dw12.c @@ -69,7 +69,12 @@ static int lis2dw12_set_odr(const struct device *dev, uint16_t odr) /* check if power off */ if (odr == 0U) { - return lis2dw12_data_rate_set(ctx, LIS2DW12_XL_ODR_OFF); + int ret = lis2dw12_data_rate_set(ctx, LIS2DW12_XL_ODR_OFF); + + if (ret == 0) { + lis2dw12->odr = 0; + } + return ret; } val = LIS2DW12_ODR_TO_REG(odr); diff --git a/drivers/sensor/st/lis2dw12/lis2dw12.h b/drivers/sensor/st/lis2dw12/lis2dw12.h index 895443445707..5a7195fd16c3 100644 --- a/drivers/sensor/st/lis2dw12/lis2dw12.h +++ b/drivers/sensor/st/lis2dw12/lis2dw12.h @@ -27,7 +27,8 @@ /* Return ODR reg value based on data rate set */ #define LIS2DW12_ODR_TO_REG(_odr) \ - ((_odr <= 1) ? LIS2DW12_XL_ODR_1Hz6_LP_ONLY : \ + ((_odr == 0) ? LIS2DW12_XL_ODR_OFF : \ + (_odr <= 1) ? LIS2DW12_XL_ODR_1Hz6_LP_ONLY : \ (_odr <= 12) ? LIS2DW12_XL_ODR_12Hz5 : \ ((31 - __builtin_clz(_odr / 25))) + 3) From f6d665ea7f0661a2f6b7f6eae7164571b5beec9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 12 Aug 2026 09:34:37 +0000 Subject: [PATCH 119/455] drivers: sensor: lis2dw12: clamp wake-up threshold to 6-bit max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wake-up threshold was only validated against full scale, but WK_THS is a 6-bit field, so thresholds just below full scale computed a register value of 64 that the HAL masked down to an almost-zero threshold. Clamp the computed value to 63 LSBs instead. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lis2dw12/lis2dw12.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/sensor/st/lis2dw12/lis2dw12.c b/drivers/sensor/st/lis2dw12/lis2dw12.c index a9b2cc240e73..6fad6955a320 100644 --- a/drivers/sensor/st/lis2dw12/lis2dw12.c +++ b/drivers/sensor/st/lis2dw12/lis2dw12.c @@ -216,6 +216,9 @@ static int lis2dw12_config(const struct device *dev, enum sensor_channel chan, #define THRESHOLD_MG_TO_WK_THS_REG(thr_mg, lsb_mg) \ ((thr_mg + (lsb_mg / 2)) / lsb_mg) +/* Maximum value of the 6-bit WK_THS field */ +#define WK_THS_MAX 63U + static int lis2dw12_attr_set_thresh(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, @@ -256,6 +259,12 @@ static int lis2dw12_attr_set_thresh(const struct device *dev, lsb_mg = MG_TO_WK_THS_LSB(FS_RANGE_TO_MG(range)); reg = THRESHOLD_MG_TO_WK_THS_REG(thr_mg, lsb_mg); + /* rounding can push a threshold just below full scale to 64, which the HAL masks to 0 */ + if (reg > WK_THS_MAX) { + LOG_WRN("Threshold %u mg clamped to %u mg", thr_mg, WK_THS_MAX * (uint32_t)lsb_mg); + reg = WK_THS_MAX; + } + LOG_DBG("Threshold %d mg -> fs: %u mg -> reg = %d LSBs", thr_mg, FS_RANGE_TO_MG(range), reg); ret = 0; From a059dc3f171ace3de1053016897cee880622bbec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 12 Aug 2026 09:42:43 +0000 Subject: [PATCH 120/455] drivers: sensor: akm09918c: track power-down after RTIO measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RTIO submit path forces a single measurement via CNTL2 but never updated the cached mode, so after the device automatically returned to power-down the blocking fetch path skipped its CNTL2 write and failed with -EBUSY forever. Update the cached mode to power-down accordingly. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c index 8b983adbf54a..b45e3bb6254b 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c @@ -64,6 +64,8 @@ void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe writeByte_sqe->flags |= RTIO_SQE_CHAINED; rtio_sqe_prep_callback_no_cqe(cb_sqe, akm09918_after_start_cb, (void *)iodev_sqe, NULL); + /* The device returns to power-down mode after a single measurement */ + data->mode = AKM09918C_CNTL2_PWR_DOWN; rtio_submit(data->rtio_ctx, 0); } else { rtio_sqe_drop_all(data->rtio_ctx); From d943840e1b66486bd9cd6f52841d73ba3a377c45 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 13 Aug 2026 11:23:14 +0300 Subject: [PATCH 121/455] drivers: uaol: re-initialize the link after a power cycle The link initialization - programming the target BDF and aligning the UAOL frame counter to the xHCI micro-frame counter - runs only once, guarded by is_initialized, but the flag is never cleared. The link power domain retains neither of the two, so on the first runtime PM cycle both are lost while the driver still believes the link is initialized. The link then runs with a frame counter that no longer matches the xHCI one, placing the isochronous data in the wrong micro-frame. The device receives or produces nothing and no error is reported anywhere. Clear the flag when the link is powered down, so that the next config() initializes the link again. Fixes: b50fcbd6eeeb ("drivers: uaol: add a driver for Intel UAOL IP") Signed-off-by: Peter Ujfalusi --- drivers/uaol/uaol_intel_adsp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/uaol/uaol_intel_adsp.c b/drivers/uaol/uaol_intel_adsp.c index 0248ffa56679..6189632e8057 100644 --- a/drivers/uaol/uaol_intel_adsp.c +++ b/drivers/uaol/uaol_intel_adsp.c @@ -209,6 +209,11 @@ static int uaol_intel_adsp_set_power(const struct device *dev, bool power) dp->is_powered_up = power; + /* The link power domain retains neither the BDF nor the frame alignment */ + if (!power) { + dp->is_initialized = false; + } + return 0; } From ef1bbd2fd1f56291e91a3fdc3035ca2a83d9f133 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 13 Aug 2026 11:23:30 +0300 Subject: [PATCH 122/455] drivers: uaol: log the link and stream configuration Neither the link initialization nor the values programmed for a stream are visible at runtime, which makes any mismatch between the host, the gateway configuration and the link hard to narrow down. Log both at debug level. Signed-off-by: Peter Ujfalusi --- drivers/uaol/uaol_intel_adsp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/uaol/uaol_intel_adsp.c b/drivers/uaol/uaol_intel_adsp.c index 6189632e8057..9f2b8f8e57cc 100644 --- a/drivers/uaol/uaol_intel_adsp.c +++ b/drivers/uaol/uaol_intel_adsp.c @@ -668,6 +668,8 @@ static int uaol_intel_adsp_config(const struct device *dev, int stream, struct u goto out; } + LOG_DBG("link initialized, frame counter aligned"); + dp->is_initialized = true; } @@ -675,6 +677,10 @@ static int uaol_intel_adsp_config(const struct device *dev, int stream, struct u sys_write16(cfg->fifo_start_offset, UAOLxPCMSyFSA_ADDR(dp, stream)); sys_write16(cfg->channel_map, UAOLxPCMSyCM_ADDR(dp, stream)); + LOG_DBG("stream %d: FSA 0x%04x, CM 0x%04x, rate %u, chan %u, bits %u, mps %u", + stream, cfg->fifo_start_offset, cfg->channel_map, cfg->sample_rate, + cfg->channels, cfg->sample_bits, cfg->sio_credit_size); + uaol_intel_adsp_program_format(dev, stream, cfg->sample_rate, cfg->channels, cfg->sample_bits, cfg->sio_credit_size, cfg->service_interval); From fa6c87eeb1f94f87280e7df9709eb487425e8172 Mon Sep 17 00:00:00 2001 From: Dmitry Rantovov Date: Tue, 25 Aug 2026 14:32:22 +0300 Subject: [PATCH 123/455] include: net: hdlc_rcp_if: drop `@param none` from void callbacks `deinit` and `deferred_init` take no arguments, but their docblocks carry `@param none`, which Doxygen reads as a parameter named "none". Signed-off-by: Dmitry Rantovov Assisted-by: Claude:claude-opus-5 --- include/zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h b/include/zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h index 9935966d464f..7fcee4c5a787 100644 --- a/include/zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h +++ b/include/zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h @@ -62,8 +62,6 @@ struct hdlc_api { /** * @brief Deinitialize the device. * - * @param none - * * @retval 0 The interface was successfully stopped. * @retval -EIO The interface could not be stopped. */ @@ -73,8 +71,6 @@ struct hdlc_api { * @brief Optional: complete the RCP interface initialization in deferred init mode. * If NULL, the interface is started automatically at init. * - * @param none - * * @retval 0 The interface was successfully started. * @retval -EIO The interface could not be started. */ From 5524bbc3e4f326be413a6f309b94636f3918c1c7 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 22:44:59 -0400 Subject: [PATCH 124/455] arch: add portable IRQ pending-state operations Drivers that need to touch an interrupt's latched pending state currently reach into the interrupt controller themselves. NVIC_ClearPendingIRQ() alone appears at 88 call sites across 50 files under drivers/, NVIC_SetPendingIRQ() at 34 (26 of them in drivers/counter/, which pends its own IRQ to fire an alarm whose deadline has already passed) and NVIC_GetPendingIRQ() at 11. Each site pulls cmsis_core.h and a CONFIG_CPU_CORTEX_M guard into otherwise portable code, and several counter drivers had already written private GIC-or-NVIC dispatch helpers for exactly these operations. Add k_irq_set_pending(), k_irq_clear_pending() and k_irq_is_pending() as ALWAYS_INLINE wrappers over matching arch_irq_* functions, and implement them for the ARM NVIC (AArch32 Cortex-M) and the GIC (AArch32 Cortex-A/R and AArch64), where the GIC driver already exported all three as arm_gic_irq_*. The k_ prefix is deliberate: vendor HAL headers already declare plain irq_set_pending()/irq_clear_pending() as their own functions (the Realtek Ameba ROM ABI, pico-sdk's hardware/irq.h), and several drivers carry private static helpers or API struct members with the bare names. A namespaced API collides with none of them, needs no macro tricks, and no renaming of existing code. The operations are gated on a single capability symbol, CONFIG_ARCH_HAS_IRQ_PENDING_OPS, so an unported target fails to build instead of silently doing nothing. Both implemented backends support all three operations; should an architecture with partial support materialize (a RISC-V PLIC can report pending state but only clears it by claiming, and cannot latch from software), the symbol can be split then. Assisted-by: Claude:claude-fable-5 Signed-off-by: Anas Nashif --- arch/Kconfig | 10 +++++ arch/arm/core/Kconfig | 10 +++++ arch/arm/core/cortex_a_r/irq_manage.c | 23 ++++++++++++ arch/arm/core/cortex_m/irq_manage.c | 23 ++++++++++++ arch/arm64/core/irq_manage.c | 24 ++++++++++++ include/zephyr/arch/arch_interface.h | 23 ++++++++++++ include/zephyr/arch/arm/irq.h | 33 +++++++++++++++++ include/zephyr/arch/arm64/irq.h | 17 +++++++++ include/zephyr/irq.h | 53 +++++++++++++++++++++++++++ 9 files changed, 216 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index 46e0cb3193f1..8eec10f7345d 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -69,6 +69,8 @@ config ARM64 select ARCH_HAS_ISR_TABLES_LOCAL_DECLARATION select ARCH_SUPPORTS_ROM_OFFSET select ARCH_HAS_CPU_IDLE_HOOKS + # Backed by the GIC distributor ICPENDR/ISPENDR registers. + select ARCH_HAS_IRQ_PENDING_OPS if !ARM_CUSTOM_INTERRUPT_CONTROLLER help ARM64 (AArch64) architecture @@ -852,6 +854,14 @@ config ARCH_HAS_IRQ_OFFLOAD_NESTED synchronous nested interrupt on the current CPU. It gates the default value of CONFIG_IRQ_OFFLOAD_NESTED. +config ARCH_HAS_IRQ_PENDING_OPS + bool + help + Selected by architectures whose interrupt controller can set, clear + and query the latched pending state of an individual interrupt + line, providing arch_irq_set_pending(), arch_irq_clear_pending() + and arch_irq_is_pending() and therefore the k_irq_*_pending() API. + config ARCH_HAS_SEMIHOST bool help diff --git a/arch/arm/core/Kconfig b/arch/arm/core/Kconfig index 258484d0ad93..1032e7048a7f 100644 --- a/arch/arm/core/Kconfig +++ b/arch/arm/core/Kconfig @@ -27,6 +27,10 @@ config CPU_CORTEX_M select ARCH_HAS_RAMFUNC_SUPPORT select ARCH_HAS_VECTOR_TABLE_RELOCATION if CPU_CORTEX_M_HAS_VTOR select ARCH_HAS_NESTED_EXCEPTION_DETECTION + # The NVIC exposes per-line pending state via ISPR/ICPR. With a custom + # or multi-level controller these operations belong to the SoC layer. + select ARCH_HAS_IRQ_PENDING_OPS if !ARM_CUSTOM_INTERRUPT_CONTROLLER && \ + !MULTI_LEVEL_INTERRUPTS select SWAP_NONATOMIC select ARCH_HAS_EXTRA_EXCEPTION_INFO select ARCH_HAS_TIMING_FUNCTIONS if CPU_CORTEX_M_HAS_DWT @@ -48,6 +52,9 @@ config CPU_AARCH32_CORTEX_R select ARCH_HAS_USERSPACE if ARM_MPU && !USE_SWITCH select ARCH_HAS_EXTRA_EXCEPTION_INFO if !USE_SWITCH select ARCH_HAS_CODE_DATA_RELOCATION + # Backed by the GIC distributor ICPENDR/ISPENDR registers. + select ARCH_HAS_IRQ_PENDING_OPS if !ARM_CUSTOM_INTERRUPT_CONTROLLER && \ + !MULTI_LEVEL_INTERRUPTS select ARCH_HAS_NOCACHE_MEMORY_SUPPORT if ARM_MPU && CPU_HAS_ARM_MPU && CPU_HAS_DCACHE select ARCH_SUPPORTS_ROM_START select USE_SWITCH_SUPPORTED @@ -69,6 +76,9 @@ config CPU_AARCH32_CORTEX_A select ARCH_HAS_BIT_REV select ARCH_HAS_EXTRA_EXCEPTION_INFO if !USE_SWITCH select ARCH_HAS_NOCACHE_MEMORY_SUPPORT + # Backed by the GIC distributor ICPENDR/ISPENDR registers. + select ARCH_HAS_IRQ_PENDING_OPS if !ARM_CUSTOM_INTERRUPT_CONTROLLER && \ + !MULTI_LEVEL_INTERRUPTS select USE_SWITCH_SUPPORTED # GDBSTUB has not yet been tested on Cortex M or R SoCs select ARCH_HAS_GDBSTUB diff --git a/arch/arm/core/cortex_a_r/irq_manage.c b/arch/arm/core/cortex_a_r/irq_manage.c index 34d3d3502f9d..f0a024d85f80 100644 --- a/arch/arm/core/cortex_a_r/irq_manage.c +++ b/arch/arm/core/cortex_a_r/irq_manage.c @@ -63,6 +63,29 @@ int arm_irq_is_enabled(unsigned int irq) return arm_gic_irq_is_enabled(irq); } +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +void arm_irq_clear_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + arm_gic_irq_clear_pending(irq); +} + +void arm_irq_set_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + arm_gic_irq_set_pending(irq); +} + +bool arm_irq_is_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + return arm_gic_irq_is_pending(irq); +} +#endif + /** * @internal * diff --git a/arch/arm/core/cortex_m/irq_manage.c b/arch/arm/core/cortex_m/irq_manage.c index 80d2f1bc3d0c..249628da4ba6 100644 --- a/arch/arm/core/cortex_m/irq_manage.c +++ b/arch/arm/core/cortex_m/irq_manage.c @@ -68,6 +68,29 @@ int arm_irq_is_enabled(unsigned int irq) return NVIC->ISER[REG_FROM_IRQ(irq)] & BIT(BIT_FROM_IRQ(irq)); } +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +void arm_irq_clear_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + NVIC_ClearPendingIRQ((IRQn_Type)irq); +} + +void arm_irq_set_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + NVIC_SetPendingIRQ((IRQn_Type)irq); +} + +bool arm_irq_is_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + return NVIC_GetPendingIRQ((IRQn_Type)irq) != 0U; +} +#endif + /** * @internal * diff --git a/arch/arm64/core/irq_manage.c b/arch/arm64/core/irq_manage.c index 6344d1e3696c..306f71c31f95 100644 --- a/arch/arm64/core/irq_manage.c +++ b/arch/arm64/core/irq_manage.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -47,6 +48,29 @@ int arch_irq_is_enabled(unsigned int irq) return arm_gic_irq_is_enabled(irq); } +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +void arch_irq_clear_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + arm_gic_irq_clear_pending(irq); +} + +void arch_irq_set_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + arm_gic_irq_set_pending(irq); +} + +bool arch_irq_is_pending(unsigned int irq) +{ + __ASSERT(irq < CONFIG_NUM_IRQS, "IRQ %u out of range", irq); + + return arm_gic_irq_is_pending(irq); +} +#endif + void z_arm64_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags) { arm_gic_irq_set_priority(irq, prio, flags); diff --git a/include/zephyr/arch/arch_interface.h b/include/zephyr/arch/arch_interface.h index ed82b1d55dc7..3b923017b435 100644 --- a/include/zephyr/arch/arch_interface.h +++ b/include/zephyr/arch/arch_interface.h @@ -412,6 +412,29 @@ void arch_irq_enable(unsigned int irq); */ int arch_irq_is_enabled(unsigned int irq); +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) || defined(__DOXYGEN__) +/** + * Clear the pending state of the specified interrupt line + * + * @see k_irq_clear_pending() + */ +void arch_irq_clear_pending(unsigned int irq); + +/** + * Set the pending state of the specified interrupt line + * + * @see k_irq_set_pending() + */ +void arch_irq_set_pending(unsigned int irq); + +/** + * Test if the specified interrupt line is pending + * + * @see k_irq_is_pending() + */ +bool arch_irq_is_pending(unsigned int irq); +#endif + /** * Arch-specific hook to install a dynamic interrupt. * diff --git a/include/zephyr/arch/arm/irq.h b/include/zephyr/arch/arm/irq.h index fcf390dd3811..b4ddd3aed8e0 100644 --- a/include/zephyr/arch/arm/irq.h +++ b/include/zephyr/arch/arm/irq.h @@ -32,10 +32,16 @@ extern "C" { #define arch_irq_enable z_soc_irq_enable #define arch_irq_disable z_soc_irq_disable #define arch_irq_is_enabled z_soc_irq_is_enabled +#define arch_irq_clear_pending z_soc_irq_clear_pending +#define arch_irq_set_pending z_soc_irq_set_pending +#define arch_irq_is_pending z_soc_irq_is_pending #else #define arch_irq_enable arm_irq_enable #define arch_irq_disable arm_irq_disable #define arch_irq_is_enabled arm_irq_is_enabled +#define arch_irq_clear_pending arm_irq_clear_pending +#define arch_irq_set_pending arm_irq_set_pending +#define arch_irq_is_pending arm_irq_is_pending #endif #ifndef CONFIG_USE_SWITCH GTEXT(z_arm_int_exit); @@ -43,6 +49,11 @@ GTEXT(z_arm_int_exit); GTEXT(arch_irq_enable) GTEXT(arch_irq_disable) GTEXT(arch_irq_is_enabled) +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +GTEXT(arch_irq_clear_pending) +GTEXT(arch_irq_set_pending) +GTEXT(arch_irq_is_pending) +#endif #if defined(CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER) GTEXT(z_soc_irq_get_active) GTEXT(z_soc_irq_eoi) @@ -54,11 +65,21 @@ extern void arm_irq_enable(unsigned int irq); extern void arm_irq_disable(unsigned int irq); extern int arm_irq_is_enabled(unsigned int irq); extern void arm_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags); +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +extern void arm_irq_clear_pending(unsigned int irq); +extern void arm_irq_set_pending(unsigned int irq); +extern bool arm_irq_is_pending(unsigned int irq); +#endif #if !defined(CONFIG_MULTI_LEVEL_INTERRUPTS) #define arch_irq_enable(irq) arm_irq_enable(irq) #define arch_irq_disable(irq) arm_irq_disable(irq) #define arch_irq_is_enabled(irq) arm_irq_is_enabled(irq) #define z_arm_irq_priority_set(irq, prio, flags) arm_irq_priority_set(irq, prio, flags) +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +#define arch_irq_clear_pending(irq) arm_irq_clear_pending(irq) +#define arch_irq_set_pending(irq) arm_irq_set_pending(irq) +#define arch_irq_is_pending(irq) arm_irq_is_pending(irq) +#endif #endif #endif @@ -80,10 +101,22 @@ void z_soc_irq_priority_set( unsigned int z_soc_irq_get_active(void); void z_soc_irq_eoi(unsigned int irq); +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +void z_soc_irq_clear_pending(unsigned int irq); +void z_soc_irq_set_pending(unsigned int irq); +bool z_soc_irq_is_pending(unsigned int irq); +#endif + #define arch_irq_enable(irq) z_soc_irq_enable(irq) #define arch_irq_disable(irq) z_soc_irq_disable(irq) #define arch_irq_is_enabled(irq) z_soc_irq_is_enabled(irq) +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +#define arch_irq_clear_pending(irq) z_soc_irq_clear_pending(irq) +#define arch_irq_set_pending(irq) z_soc_irq_set_pending(irq) +#define arch_irq_is_pending(irq) z_soc_irq_is_pending(irq) +#endif + #define z_arm_irq_priority_set(irq, prio, flags) \ z_soc_irq_priority_set(irq, prio, flags) diff --git a/include/zephyr/arch/arm64/irq.h b/include/zephyr/arch/arm64/irq.h index fc89fddda817..f7020e294f0c 100644 --- a/include/zephyr/arch/arm64/irq.h +++ b/include/zephyr/arch/arm64/irq.h @@ -38,6 +38,11 @@ GTEXT(z_soc_irq_eoi) extern void arch_irq_enable(unsigned int irq); extern void arch_irq_disable(unsigned int irq); extern int arch_irq_is_enabled(unsigned int irq); +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +extern void arch_irq_clear_pending(unsigned int irq); +extern void arch_irq_set_pending(unsigned int irq); +extern bool arch_irq_is_pending(unsigned int irq); +#endif /* internal routine documented in C file, needed by IRQ_CONNECT() macro */ extern void z_arm64_irq_priority_set(unsigned int irq, unsigned int prio, @@ -61,10 +66,22 @@ void z_soc_irq_priority_set( unsigned int z_soc_irq_get_active(void); void z_soc_irq_eoi(unsigned int irq); +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +void z_soc_irq_clear_pending(unsigned int irq); +void z_soc_irq_set_pending(unsigned int irq); +bool z_soc_irq_is_pending(unsigned int irq); +#endif + #define arch_irq_enable(irq) z_soc_irq_enable(irq) #define arch_irq_disable(irq) z_soc_irq_disable(irq) #define arch_irq_is_enabled(irq) z_soc_irq_is_enabled(irq) +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) +#define arch_irq_clear_pending(irq) z_soc_irq_clear_pending(irq) +#define arch_irq_set_pending(irq) z_soc_irq_set_pending(irq) +#define arch_irq_is_pending(irq) z_soc_irq_is_pending(irq) +#endif + #define z_arm64_irq_priority_set(irq, prio, flags) \ z_soc_irq_priority_set(irq, prio, flags) diff --git a/include/zephyr/irq.h b/include/zephyr/irq.h index c8a2ed528afa..5e44df1e9b8f 100644 --- a/include/zephyr/irq.h +++ b/include/zephyr/irq.h @@ -15,6 +15,7 @@ #include #ifndef _ASMLANGUAGE +#include #include #include @@ -317,6 +318,58 @@ void z_smp_global_unlock(unsigned int key); */ #define irq_is_enabled(irq) arch_irq_is_enabled(irq) +#if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) || defined(__DOXYGEN__) +/** + * @brief Clear the pending state of an IRQ. + * + * Discard a latched, not yet serviced interrupt from source @a irq. Only the + * controller's latched state is affected: a level-triggered line still + * asserted by the peripheral pends again immediately. + * + * @kconfig_dep{CONFIG_ARCH_HAS_IRQ_PENDING_OPS} + * + * @param irq IRQ line. + */ +static ALWAYS_INLINE void k_irq_clear_pending(unsigned int irq) +{ + arch_irq_clear_pending(irq); +} + +/** + * @brief Set the pending state of an IRQ. + * + * Latch source @a irq in the interrupt controller as if the hardware had + * raised it. The peripheral behind @a irq knows nothing about a + * software-raised interrupt. + * + * @kconfig_dep{CONFIG_ARCH_HAS_IRQ_PENDING_OPS} + * + * @param irq IRQ line. + */ +static ALWAYS_INLINE void k_irq_set_pending(unsigned int irq) +{ + arch_irq_set_pending(irq); +} + +/** + * @brief Get IRQ pending state. + * + * Report whether an interrupt from source @a irq is latched in the interrupt + * controller and has not been serviced yet. The result is a momentary + * snapshot. + * + * @kconfig_dep{CONFIG_ARCH_HAS_IRQ_PENDING_OPS} + * + * @param irq IRQ line. + * + * @return interrupt pending state, true or false + */ +static ALWAYS_INLINE bool k_irq_is_pending(unsigned int irq) +{ + return arch_irq_is_pending(irq); +} +#endif /* CONFIG_ARCH_HAS_IRQ_PENDING_OPS */ + /** * @} */ From bef4b0c61411062fc986e6ac31e3a31aae809652 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 22:44:59 -0400 Subject: [PATCH 125/455] tests: arch: cover the IRQ pending-state operations Add a test application exercising k_irq_set_pending(), k_irq_is_pending() and k_irq_clear_pending() on one reserved interrupt line: a software-latched interrupt is delivered exactly once when the line is enabled, the pending query tracks the not-pending/pending/cleared transitions, and a cleared interrupt is never delivered. A control case latches an interrupt without clearing it and checks that it is delivered, so the clear test cannot pass simply because nothing was ever latched. Latching an interrupt on a disabled line needs a software trigger with a clearable pending state, which get_available_nvic_line() provides on Cortex-M. GIC SGIs hold their pending state outside ICPENDR and are not a substitute, so the scenario is filtered to Cortex-M for now. Assisted-by: Claude:claude-fable-5 Signed-off-by: Anas Nashif --- tests/arch/common/irq_pending/CMakeLists.txt | 7 + tests/arch/common/irq_pending/prj.conf | 3 + tests/arch/common/irq_pending/src/main.c | 148 +++++++++++++++++++ tests/arch/common/irq_pending/tests.yaml | 8 + 4 files changed, 166 insertions(+) create mode 100644 tests/arch/common/irq_pending/CMakeLists.txt create mode 100644 tests/arch/common/irq_pending/prj.conf create mode 100644 tests/arch/common/irq_pending/src/main.c create mode 100644 tests/arch/common/irq_pending/tests.yaml diff --git a/tests/arch/common/irq_pending/CMakeLists.txt b/tests/arch/common/irq_pending/CMakeLists.txt new file mode 100644 index 000000000000..21052e56dc3f --- /dev/null +++ b/tests/arch/common/irq_pending/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(irq_pending) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/arch/common/irq_pending/prj.conf b/tests/arch/common/irq_pending/prj.conf new file mode 100644 index 000000000000..42356c46d5ca --- /dev/null +++ b/tests/arch/common/irq_pending/prj.conf @@ -0,0 +1,3 @@ +CONFIG_ZTEST=y +CONFIG_DYNAMIC_INTERRUPTS=y +CONFIG_MP_MAX_NUM_CPUS=1 diff --git a/tests/arch/common/irq_pending/src/main.c b/tests/arch/common/irq_pending/src/main.c new file mode 100644 index 000000000000..fca6f9d70461 --- /dev/null +++ b/tests/arch/common/irq_pending/src/main.c @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static volatile unsigned int handler_runs; + +static void pending_isr(const void *arg) +{ + ARG_UNUSED(arg); + handler_runs++; +} + +/* + * Reserve an interrupt line and keep it disabled, so that a latched interrupt + * survives until the line is enabled. The caller decides what to latch on it + * and when to enable. + * + * This test application deliberately has no other statically connected + * interrupts, because get_available_nvic_line() can hand back a line that is + * owned by a currently disabled IRQ_CONNECT() entry. + */ +static unsigned int connect_disabled_irq_line(void) +{ + unsigned int irq = get_available_nvic_line(CONFIG_NUM_IRQS); + + handler_runs = 0; + + zassert_true(irq_connect_dynamic(irq, 1, pending_isr, NULL, 0) > 0, + "irq connect dynamic failed"); + + irq_disable(irq); + + return irq; +} + +/* As above, plus an interrupt already latched by the hardware trigger path. */ +static unsigned int pend_disabled_irq_line(void) +{ + unsigned int irq = connect_disabled_irq_line(); + + trigger_irq(irq); + + zassert_equal(handler_runs, 0, "handler ran while the line was disabled"); + + return irq; +} + +/** + * @brief Test that a latched interrupt is delivered once the line is enabled + * + * @ingroup kernel_interrupt_tests + * + * @details Control case for test_irq_clear_pending(). Without it the clear + * test could pass simply because nothing was ever latched. + * + * @see irq_enable() + */ +ZTEST(irq_pending, test_irq_pending_without_clear) +{ + unsigned int irq = pend_disabled_irq_line(); + + irq_enable(irq); + irq_disable(irq); + + zassert_equal(handler_runs, 1, "latched interrupt was not delivered (%u)", handler_runs); +} + +/** + * @brief Test that k_irq_clear_pending() discards a latched interrupt + * + * @ingroup kernel_interrupt_tests + * + * @details Latches an interrupt on a disabled line, clears it, then enables + * the line. The handler must not run, because the pending state was dropped + * before delivery could happen. + * + * @see k_irq_clear_pending() + */ +ZTEST(irq_pending, test_irq_clear_pending) +{ + unsigned int irq = pend_disabled_irq_line(); + + k_irq_clear_pending(irq); + + irq_enable(irq); + irq_disable(irq); + + zassert_equal(handler_runs, 0, "cleared interrupt was still delivered (%u)", handler_runs); +} + +/** + * @brief Test that k_irq_set_pending() latches an interrupt from software + * + * @ingroup kernel_interrupt_tests + * + * @details Latches an interrupt with k_irq_set_pending() on a disabled line, + * then enables the line and checks the handler ran exactly once, without the + * peripheral trigger path being involved at all. + * + * @see k_irq_set_pending() + */ +ZTEST(irq_pending, test_irq_set_pending) +{ + unsigned int irq = connect_disabled_irq_line(); + + k_irq_set_pending(irq); + + zassert_equal(handler_runs, 0, "handler ran while the line was disabled"); + + irq_enable(irq); + irq_disable(irq); + + zassert_equal(handler_runs, 1, + "software-latched interrupt was not delivered (%u)", handler_runs); +} + +/** + * @brief Test that k_irq_is_pending() tracks the latched state + * + * @ingroup kernel_interrupt_tests + * + * @details Walks one interrupt line through not-pending, pending and + * cleared states, checking k_irq_is_pending() reports each transition. + * + * @see k_irq_is_pending() + */ +ZTEST(irq_pending, test_irq_is_pending) +{ + unsigned int irq = connect_disabled_irq_line(); + + zassert_false(k_irq_is_pending(irq), "line pending before anything was latched"); + + k_irq_set_pending(irq); + + zassert_true(k_irq_is_pending(irq), "latched interrupt not reported as pending"); + + k_irq_clear_pending(irq); + + zassert_false(k_irq_is_pending(irq), "cleared interrupt still reported as pending"); + + zassert_equal(handler_runs, 0, "handler ran while the line was disabled"); +} + +ZTEST_SUITE(irq_pending, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/arch/common/irq_pending/tests.yaml b/tests/arch/common/irq_pending/tests.yaml new file mode 100644 index 000000000000..fec3b7f709d5 --- /dev/null +++ b/tests/arch/common/irq_pending/tests.yaml @@ -0,0 +1,8 @@ +common: + tags: + - kernel + - interrupt +tests: + arch.interrupt.pending: + # Needs a software-triggerable line with clearable pending state (NVIC). + filter: CONFIG_ARCH_HAS_IRQ_PENDING_OPS and CONFIG_CPU_CORTEX_M From 0916a30c7486bcf922086f2dd7fae7d975ad2acd Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Sat, 15 Aug 2026 09:35:30 -0400 Subject: [PATCH 126/455] arch: add per-CPU active-IRQ tracking on Cortex-A/R Vendor HAL interrupt handlers are often entered without their vector number and have to discover it themselves. On Cortex-M the IPSR register answers that, but on Cortex-A/R with a GIC the INTID exists only at acknowledge time inside the ISR wrapper. The Renesas RZ SoCs work around this today by selecting ARM_CUSTOM_INTERRUPT_CONTROLLER with pass-through z_soc_irq_* wrappers around the standard GIC driver, purely to intercept the ack/eoi moments and log the INTID into a fixed-depth side stack for the FSP HAL, duplicated across six SoCs. Add k_irq_get_active(), backed by arch_irq_get_active(): the interrupt line whose handler is executing on the current CPU, or K_IRQ_ACTIVE_NONE outside interrupt context. The capability symbol ARCH_HAS_IRQ_GET_ACTIVE only promises the query; whether an architecture reads it from a register or records it in the dispatch path is its own business. On Cortex-A/R recording is the only option, so the implementation is gated behind an arch-level opt-in, ARM_TRACK_ACTIVE_IRQ, keeping the cost decision where the cost lives: the shared ISR wrapper publishes the acknowledged INTID in a per-CPU field of _cpu_arch and keeps the previous value on the exception stack across the dispatch, so nested interrupts unwind to the preempted INTID and the outermost exit back to "none". The stored value is biased by one so the zero-initialized boot state reads as "none" on every CPU without explicit initialization, including secondary SMP cores. The wrapper records whatever get_active returned, so it works with both the GIC and a custom interrupt controller. Cost is zero when the option is off and a few instructions per interrupt entry and exit when on. Assisted-by: Claude:claude-fable-5 Signed-off-by: Anas Nashif --- arch/Kconfig | 9 ++++++ arch/arm/core/Kconfig | 11 +++++++ arch/arm/core/cortex_a_r/irq_manage.c | 13 ++++++++ arch/arm/core/cortex_a_r/isr_wrapper.S | 40 +++++++++++++++++++++++++ arch/arm/core/offsets/offsets_aarch32.c | 3 ++ arch/arm/include/offsets_short_arch.h | 5 ++++ include/zephyr/arch/arch_interface.h | 9 ++++++ include/zephyr/arch/arm/structs.h | 8 +++++ include/zephyr/irq.h | 26 ++++++++++++++++ 9 files changed, 124 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index 8eec10f7345d..9a9624ca0511 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -862,6 +862,15 @@ config ARCH_HAS_IRQ_PENDING_OPS line, providing arch_irq_set_pending(), arch_irq_clear_pending() and arch_irq_is_pending() and therefore the k_irq_*_pending() API. +config ARCH_HAS_IRQ_GET_ACTIVE + bool + help + Selected by architectures that can report the interrupt line being + serviced on the local CPU through arch_irq_get_active() and + therefore k_irq_get_active(). Some interrupt controllers expose + this in a register; others record it in the interrupt dispatch + path. + config ARCH_HAS_SEMIHOST bool help diff --git a/arch/arm/core/Kconfig b/arch/arm/core/Kconfig index 1032e7048a7f..7f5c9c8d5108 100644 --- a/arch/arm/core/Kconfig +++ b/arch/arm/core/Kconfig @@ -87,6 +87,17 @@ config CPU_AARCH32_CORTEX_A help This option signifies the use of a CPU of the Cortex-A family. +config ARM_TRACK_ACTIVE_IRQ + bool "Track the active IRQ in the ISR wrapper" + depends on CPU_AARCH32_CORTEX_A || CPU_AARCH32_CORTEX_R + select ARCH_HAS_IRQ_GET_ACTIVE + help + Provide k_irq_get_active() on Cortex-A/R. The GIC has no register + that reports the INTID being serviced after acknowledge, so the + ISR wrapper records it per CPU, at the cost of a few instructions + on every interrupt entry and exit. Works with both the GIC and a + custom interrupt controller. + config GDBSTUB_BUF_SZ # GDB for ARM expects up to 18 4-byte plus 8 12-byte # registers - 336 HEX letters diff --git a/arch/arm/core/cortex_a_r/irq_manage.c b/arch/arm/core/cortex_a_r/irq_manage.c index f0a024d85f80..4d082fff78dd 100644 --- a/arch/arm/core/cortex_a_r/irq_manage.c +++ b/arch/arm/core/cortex_a_r/irq_manage.c @@ -103,6 +103,19 @@ void arm_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags) #endif /* !CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER */ +#if defined(CONFIG_ARM_TRACK_ACTIVE_IRQ) +unsigned int arch_irq_get_active(void) +{ + /* + * The ISR wrapper stores the INTID biased by one so that the + * zero-initialized boot state reads as "none" on every CPU. + */ + uint32_t biased = _current_cpu->arch.active_irq; + + return (biased == 0U) ? K_IRQ_ACTIVE_NONE : (biased - 1U); +} +#endif + void z_arm_fatal_error(unsigned int reason, const struct arch_esf *esf); /** diff --git a/arch/arm/core/cortex_a_r/isr_wrapper.S b/arch/arm/core/cortex_a_r/isr_wrapper.S index 62f2aa8caf5e..78fe3060d591 100644 --- a/arch/arm/core/cortex_a_r/isr_wrapper.S +++ b/arch/arm/core/cortex_a_r/isr_wrapper.S @@ -183,6 +183,22 @@ _idle_state_cleared: #endif /* !CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER */ push {r0, r1} +#ifdef CONFIG_ARM_TRACK_ACTIVE_IRQ + /* + * Publish the acknowledged INTID for arch_irq_get_active(). The value + * is stored biased by one so that the zero-initialized boot state + * reads as "no interrupt active" on every CPU. The previous value + * and the CPU pointer are kept on the stack so the exit path + * unwinds a nested interrupt to the preempted INTID and the + * outermost exit back to "none". + */ + get_cpu r2 + ldr r1, [r2, #_cpu_offset_to_active_irq] + add r3, r0, #1 + str r3, [r2, #_cpu_offset_to_active_irq] + push {r1, r2} +#endif /* CONFIG_ARM_TRACK_ACTIVE_IRQ */ + #if CONFIG_GIC_VER >= 3 /* * Ignore GIC special INTIDs (1020-1023), which do not represent @@ -230,6 +246,12 @@ oob: cpsid i spurious_continue: +#ifdef CONFIG_ARM_TRACK_ACTIVE_IRQ + /* Unwind the published active INTID to the preempted context's */ + pop {r1, r2} + str r1, [r2, #_cpu_offset_to_active_irq] +#endif /* CONFIG_ARM_TRACK_ACTIVE_IRQ */ + /* Signal end-of-interrupt */ pop {r0, r1} #if !defined(CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER) @@ -314,6 +336,18 @@ _idle_state_cleared: push {r0, r1} +#ifdef CONFIG_ARM_TRACK_ACTIVE_IRQ + /* + * Publish the acknowledged INTID for arch_irq_get_active(). See the + * comment in the non-USE_SWITCH variant above. + */ + get_cpu r2 + ldr r1, [r2, #_cpu_offset_to_active_irq] + add r3, r0, #1 + str r3, [r2, #_cpu_offset_to_active_irq] + push {r1, r2} +#endif /* CONFIG_ARM_TRACK_ACTIVE_IRQ */ + #if CONFIG_GIC_VER >= 3 /* * Ignore Special INTIDs 1020..1023 see 2.2.1 of Arm Generic Interrupt Controller @@ -351,6 +385,12 @@ oob: cpsid i spurious_continue: +#ifdef CONFIG_ARM_TRACK_ACTIVE_IRQ + /* Unwind the published active INTID to the preempted context's */ + pop {r1, r2} + str r1, [r2, #_cpu_offset_to_active_irq] +#endif /* CONFIG_ARM_TRACK_ACTIVE_IRQ */ + /* Signal end-of-interrupt */ pop {r0, r1} #if !defined(CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER) diff --git a/arch/arm/core/offsets/offsets_aarch32.c b/arch/arm/core/offsets/offsets_aarch32.c index a407d17f839d..f26d0a65cc7e 100644 --- a/arch/arm/core/offsets/offsets_aarch32.c +++ b/arch/arm/core/offsets/offsets_aarch32.c @@ -42,6 +42,9 @@ GEN_OFFSET_SYM(_thread_arch_t, pac_keys); #if defined(CONFIG_CPU_AARCH32_CORTEX_A) || defined(CONFIG_CPU_AARCH32_CORTEX_R) GEN_OFFSET_SYM(_thread_arch_t, exception_depth); GEN_OFFSET_SYM(_cpu_arch_t, exc_depth); +#if defined(CONFIG_ARM_TRACK_ACTIVE_IRQ) +GEN_OFFSET_SYM(_cpu_arch_t, active_irq); +#endif #endif #if defined(CONFIG_ARM_STORE_EXC_RETURN) || defined(CONFIG_USERSPACE) diff --git a/arch/arm/include/offsets_short_arch.h b/arch/arm/include/offsets_short_arch.h index 43c7b4d433b5..c3975b58d3db 100644 --- a/arch/arm/include/offsets_short_arch.h +++ b/arch/arm/include/offsets_short_arch.h @@ -30,6 +30,11 @@ #define _cpu_offset_to_exc_depth \ (___cpu_t_arch_OFFSET + ___cpu_arch_t_exc_depth_OFFSET) + +#if defined(CONFIG_ARM_TRACK_ACTIVE_IRQ) +#define _cpu_offset_to_active_irq \ + (___cpu_t_arch_OFFSET + ___cpu_arch_t_active_irq_OFFSET) +#endif #endif #if defined(CONFIG_USERSPACE) || defined(CONFIG_FPU_SHARING) diff --git a/include/zephyr/arch/arch_interface.h b/include/zephyr/arch/arch_interface.h index 3b923017b435..53f54f851984 100644 --- a/include/zephyr/arch/arch_interface.h +++ b/include/zephyr/arch/arch_interface.h @@ -435,6 +435,15 @@ void arch_irq_set_pending(unsigned int irq); bool arch_irq_is_pending(unsigned int irq); #endif +#if defined(CONFIG_ARCH_HAS_IRQ_GET_ACTIVE) || defined(__DOXYGEN__) +/** + * Report the interrupt line being serviced on the current CPU + * + * @see k_irq_get_active() + */ +unsigned int arch_irq_get_active(void); +#endif + /** * Arch-specific hook to install a dynamic interrupt. * diff --git a/include/zephyr/arch/arm/structs.h b/include/zephyr/arch/arm/structs.h index c9e8a0b3280a..65bf180fa4fd 100644 --- a/include/zephyr/arch/arm/structs.h +++ b/include/zephyr/arch/arm/structs.h @@ -12,6 +12,14 @@ /* Per CPU architecture specifics */ struct _cpu_arch { int8_t exc_depth; +#if defined(CONFIG_ARM_TRACK_ACTIVE_IRQ) + /* + * INTID currently being serviced on this CPU, biased by one so + * that the zero-initialized boot state reads as "none". Maintained + * by the ISR wrapper; read through arch_irq_get_active(). + */ + uint32_t active_irq; +#endif }; #else diff --git a/include/zephyr/irq.h b/include/zephyr/irq.h index 5e44df1e9b8f..e32f031ac18c 100644 --- a/include/zephyr/irq.h +++ b/include/zephyr/irq.h @@ -15,6 +15,7 @@ #include #ifndef _ASMLANGUAGE +#include #include #include #include @@ -370,6 +371,31 @@ static ALWAYS_INLINE bool k_irq_is_pending(unsigned int irq) } #endif /* CONFIG_ARCH_HAS_IRQ_PENDING_OPS */ +#if defined(CONFIG_ARCH_HAS_IRQ_GET_ACTIVE) || defined(__DOXYGEN__) +/** + * @brief Value returned by k_irq_get_active() outside interrupt context. + */ +#define K_IRQ_ACTIVE_NONE UINT_MAX + +/** + * @brief Get the IRQ currently being serviced on this CPU. + * + * Report the interrupt line whose handler is executing on the current CPU, + * the innermost one when interrupts nest. Only hardware interrupts + * dispatched through the architecture interrupt path are reported; + * irq_offload() handlers and CPU exceptions are not. + * + * @kconfig_dep{CONFIG_ARCH_HAS_IRQ_GET_ACTIVE} + * + * @return the active IRQ line, or @ref K_IRQ_ACTIVE_NONE if no interrupt is + * being serviced + */ +static ALWAYS_INLINE unsigned int k_irq_get_active(void) +{ + return arch_irq_get_active(); +} +#endif /* CONFIG_ARCH_HAS_IRQ_GET_ACTIVE */ + /** * @} */ From e204189774a6cef47fdcd7b5da6fe0a345e7a7fd Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Sat, 15 Aug 2026 09:35:39 -0400 Subject: [PATCH 127/455] tests: arch: cover the active-IRQ query Add a test application for k_irq_get_active() exercising the three states it distinguishes: thread context reports K_IRQ_ACTIVE_NONE, a handler sees the line it was connected to, and a high-priority interrupt raised from a low-priority handler preempts it, with the inner handler seeing its own line and the outer handler seeing its own line restored after the nested interrupt returns, which exercises the save/restore of the tracked value across nesting. Interrupts are raised as GIC SGIs through trigger_irq() on lines 5-7, following the line and priority choices of the existing nested interrupt test: SGIs 0-2 are reserved for SMP IPIs and 8-15 are not accessible from the Non-Secure state, and IRQ_DEFAULT_PRIORITY against 0x0 guarantees preemption for any legal GICC BPR setting. Assisted-by: Claude:claude-fable-5 Signed-off-by: Anas Nashif --- tests/arch/common/irq_active/CMakeLists.txt | 7 + tests/arch/common/irq_active/prj.conf | 3 + tests/arch/common/irq_active/src/main.c | 159 ++++++++++++++++++++ tests/arch/common/irq_active/tests.yaml | 14 ++ 4 files changed, 183 insertions(+) create mode 100644 tests/arch/common/irq_active/CMakeLists.txt create mode 100644 tests/arch/common/irq_active/prj.conf create mode 100644 tests/arch/common/irq_active/src/main.c create mode 100644 tests/arch/common/irq_active/tests.yaml diff --git a/tests/arch/common/irq_active/CMakeLists.txt b/tests/arch/common/irq_active/CMakeLists.txt new file mode 100644 index 000000000000..23f4a1700e51 --- /dev/null +++ b/tests/arch/common/irq_active/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(irq_active) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/arch/common/irq_active/prj.conf b/tests/arch/common/irq_active/prj.conf new file mode 100644 index 000000000000..5cfca3fd91a4 --- /dev/null +++ b/tests/arch/common/irq_active/prj.conf @@ -0,0 +1,3 @@ +CONFIG_ZTEST=y +CONFIG_ARM_TRACK_ACTIVE_IRQ=y +CONFIG_MP_MAX_NUM_CPUS=1 diff --git a/tests/arch/common/irq_active/src/main.c b/tests/arch/common/irq_active/src/main.c new file mode 100644 index 000000000000..afd011e2ebd7 --- /dev/null +++ b/tests/arch/common/irq_active/src/main.c @@ -0,0 +1,159 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* + * The test uses GIC SGI (software generated interrupt) lines, raised through + * trigger_irq(). SGIs 0-2 are used by Zephyr for SMP IPIs and SGIs 8-15 are + * inaccessible from the Non-Secure state, so use lines 5-7. + * + * For the nested case the inner line must preempt the outer handler: + * IRQ_DEFAULT_PRIORITY and the highest possible priority 0x0 are far enough + * apart to guarantee preemption for any legal GICC BPR setting. + */ +#define SIMPLE_LINE 5 +#define OUTER_LINE 6 +#define INNER_LINE 7 + +#define SIMPLE_PRIO IRQ_DEFAULT_PRIORITY +#define OUTER_PRIO IRQ_DEFAULT_PRIORITY +#define INNER_PRIO 0x0 + +/* Generous bound on how long the outer handler waits to be preempted */ +#define PREEMPT_WAIT_US 10000 + +static volatile unsigned int simple_seen; +static volatile bool simple_ran; + +static volatile unsigned int outer_seen_before; +static volatile unsigned int outer_seen_after; +static volatile unsigned int inner_seen; +static volatile bool inner_ran; +static volatile bool inner_ran_during_outer; +static volatile bool outer_ran; + +static void simple_isr(const void *arg) +{ + ARG_UNUSED(arg); + + simple_seen = k_irq_get_active(); + simple_ran = true; +} + +static void inner_isr(const void *arg) +{ + ARG_UNUSED(arg); + + inner_seen = k_irq_get_active(); + inner_ran = true; +} + +static void outer_isr(const void *arg) +{ + ARG_UNUSED(arg); + + outer_seen_before = k_irq_get_active(); + + trigger_irq(INNER_LINE); + + /* + * Interrupts are re-enabled while a registered handler runs, so the + * higher-priority inner SGI preempts this handler right here. + */ + for (unsigned int i = 0U; i < PREEMPT_WAIT_US && !inner_ran; i++) { + k_busy_wait(1); + } + inner_ran_during_outer = inner_ran; + + outer_seen_after = k_irq_get_active(); + outer_ran = true; +} + +/** + * @brief Test that no active IRQ is reported outside interrupt context + * + * @ingroup kernel_interrupt_tests + * + * @see k_irq_get_active() + */ +ZTEST(irq_active_tracking, test_irq_active_none_in_thread) +{ + zassert_equal(k_irq_get_active(), K_IRQ_ACTIVE_NONE, + "an active IRQ is reported in thread context"); +} + +/** + * @brief Test that a handler observes its own interrupt line as active + * + * @ingroup kernel_interrupt_tests + * + * @details Trigger a software generated interrupt and let its handler call + * k_irq_get_active(): it must see the line it was connected to, and the thread + * must see no active line once the handler has returned. + * + * @see k_irq_get_active() + */ +ZTEST(irq_active_tracking, test_irq_active_in_isr) +{ + IRQ_CONNECT(SIMPLE_LINE, SIMPLE_PRIO, simple_isr, NULL, 0); + irq_enable(SIMPLE_LINE); + + trigger_irq(SIMPLE_LINE); + + for (unsigned int i = 0U; i < PREEMPT_WAIT_US && !simple_ran; i++) { + k_busy_wait(1); + } + + zassert_true(simple_ran, "interrupt was not delivered"); + zassert_equal(simple_seen, SIMPLE_LINE, + "handler saw active line %u, not %u", simple_seen, SIMPLE_LINE); + zassert_equal(k_irq_get_active(), K_IRQ_ACTIVE_NONE, + "an active IRQ is reported after the handler returned"); +} + +/** + * @brief Test that nested interrupts unwind the active line correctly + * + * @ingroup kernel_interrupt_tests + * + * @details A low-priority handler raises a high-priority interrupt that + * preempts it. The inner handler must see its own line as active, and once + * it returns the outer handler must see its own line again, exercising the + * save/restore of the tracked value across a nested interrupt. + * + * @see k_irq_get_active() + */ +ZTEST(irq_active_tracking, test_irq_active_nested) +{ + IRQ_CONNECT(OUTER_LINE, OUTER_PRIO, outer_isr, NULL, 0); + IRQ_CONNECT(INNER_LINE, INNER_PRIO, inner_isr, NULL, 0); + irq_enable(OUTER_LINE); + irq_enable(INNER_LINE); + + trigger_irq(OUTER_LINE); + + for (unsigned int i = 0U; i < PREEMPT_WAIT_US && !outer_ran; i++) { + k_busy_wait(1); + } + + zassert_true(outer_ran, "outer interrupt was not delivered"); + zassert_true(inner_ran_during_outer, + "inner interrupt did not preempt the outer handler"); + zassert_equal(outer_seen_before, OUTER_LINE, + "outer handler saw active line %u, not %u", + outer_seen_before, OUTER_LINE); + zassert_equal(inner_seen, INNER_LINE, + "inner handler saw active line %u, not %u", + inner_seen, INNER_LINE); + zassert_equal(outer_seen_after, OUTER_LINE, + "active line was not restored to %u after nesting, got %u", + OUTER_LINE, outer_seen_after); + zassert_equal(k_irq_get_active(), K_IRQ_ACTIVE_NONE, + "an active IRQ is reported after the handlers returned"); +} + +ZTEST_SUITE(irq_active_tracking, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/arch/common/irq_active/tests.yaml b/tests/arch/common/irq_active/tests.yaml new file mode 100644 index 000000000000..ac9b05e068ca --- /dev/null +++ b/tests/arch/common/irq_active/tests.yaml @@ -0,0 +1,14 @@ +common: + tags: + - kernel + - interrupt +tests: + arch.interrupt.active: + # trigger_irq() raises GIC SGIs, and the tracking itself is implemented + # in the AArch32 Cortex-A/R ISR wrapper, so the scenario needs both. + filter: CONFIG_ARCH_HAS_IRQ_GET_ACTIVE and CONFIG_GIC + arch_allow: + - arm + integration_platforms: + - qemu_cortex_r5 + - qemu_cortex_a9 From 55d0693e658455dad58dd5f80554ec9f62de6faf Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 18 Aug 2026 08:48:53 -0400 Subject: [PATCH 128/455] kernel: irq: provide namespaced k_irq_* equivalents The new pending-state and active-IRQ operations use the k_irq_ namespace, leaving the seven legacy run-time interrupt APIs as the only unprefixed ones in irq.h. Provide namespaced equivalents so the API family is uniform and new code has a collision-free spelling: k_irq_lock(), k_irq_unlock(), k_irq_enable(), k_irq_disable(), k_irq_is_enabled(), k_irq_connect_dynamic() and k_irq_disconnect_dynamic(), all ALWAYS_INLINE wrappers with behaviour identical to the legacy names. The unprefixed names remain fully supported and are not deprecated: they have on the order of five thousand call sites in the tree alone and far more out of tree, so any deprecation decision needs migration data and belongs to a wider discussion. They also must stay function-like macros rather than become inline functions: vendor HAL headers declare functions with these exact names (the Realtek Ameba ROM ABI declares irq_enable(IRQn_Type)), and a macro coexists with such a declaration where a function definition would conflict. The compile-time constructs (IRQ_CONNECT, IRQ_DIRECT_CONNECT, ISR_DIRECT_*) are left as they are: they live outside the contested function namespace, so renaming them would be churn without benefit. Document the aliases in the interrupts service documentation and the release notes, and switch the irq_pending and irq_active test applications to the namespaced spellings so the aliases have build and runtime coverage on Cortex-M and Cortex-A/R. Assisted-by: Claude:claude-fable-5 Signed-off-by: Anas Nashif --- doc/kernel/services/interrupts.rst | 6 ++ doc/releases/release-notes-4.5.rst | 5 ++ include/zephyr/irq.h | 98 ++++++++++++++++++++++++ tests/arch/common/irq_active/src/main.c | 6 +- tests/arch/common/irq_pending/src/main.c | 18 ++--- 5 files changed, 121 insertions(+), 12 deletions(-) diff --git a/doc/kernel/services/interrupts.rst b/doc/kernel/services/interrupts.rst index 077d96f2c690..90362685718a 100644 --- a/doc/kernel/services/interrupts.rst +++ b/doc/kernel/services/interrupts.rst @@ -258,6 +258,12 @@ Defining a regular ISR An ISR is defined at runtime by calling :c:macro:`IRQ_CONNECT`. It must then be enabled by calling :c:func:`irq_enable`. +.. note:: + The unprefixed interrupt control APIs such as :c:func:`irq_enable` and + :c:func:`irq_lock` are the legacy spelling. New code should use their + namespaced equivalents, :c:func:`k_irq_enable`, :c:func:`k_irq_lock` and + so on. The unprefixed names remain fully supported. + .. important:: IRQ_CONNECT() is not a C function and does some inline assembly magic behind the scenes. All its arguments must be known at build time. diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index fd57d4a18ad2..b6532299029a 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -396,6 +396,11 @@ New APIs and options * :c:macro:`K_MSGQ_DEFINE_TYPE` * :c:macro:`K_MSGQ_DEFINE_STATIC_TYPE` * :c:func:`k_sleep_ticks` + * Namespaced equivalents of the interrupt control APIs, preferred for new + code; the unprefixed names remain fully supported: + :c:func:`k_irq_lock`, :c:func:`k_irq_unlock`, :c:func:`k_irq_enable`, + :c:func:`k_irq_disable`, :c:func:`k_irq_is_enabled`, + :c:func:`k_irq_connect_dynamic` and :c:func:`k_irq_disconnect_dynamic` * LoRa diff --git a/include/zephyr/irq.h b/include/zephyr/irq.h index e32f031ac18c..4be0ffc0ad5a 100644 --- a/include/zephyr/irq.h +++ b/include/zephyr/irq.h @@ -319,6 +319,104 @@ void z_smp_global_unlock(unsigned int key); */ #define irq_is_enabled(irq) arch_irq_is_enabled(irq) +/* + * Namespaced equivalents of the interrupt APIs above. New code should + * prefer these. The unprefixed names remain fully supported and must stay + * function-like macros: vendor HAL headers declare functions with those + * names, and a macro coexists with such a declaration where a function + * definition would conflict. + */ + +/** + * @brief Lock interrupts; namespaced equivalent of irq_lock(). + * + * @return An architecture-dependent lock-out key to pass to k_irq_unlock(). + */ +static ALWAYS_INLINE unsigned int k_irq_lock(void) +{ + return irq_lock(); +} + +/** + * @brief Unlock interrupts; namespaced equivalent of irq_unlock(). + * + * @param key Lock-out key returned by k_irq_lock(). + */ +static ALWAYS_INLINE void k_irq_unlock(unsigned int key) +{ + irq_unlock(key); +} + +/** + * @brief Enable an IRQ; namespaced equivalent of irq_enable(). + * + * @param irq IRQ line. + */ +static ALWAYS_INLINE void k_irq_enable(unsigned int irq) +{ + irq_enable(irq); +} + +/** + * @brief Disable an IRQ; namespaced equivalent of irq_disable(). + * + * @param irq IRQ line. + */ +static ALWAYS_INLINE void k_irq_disable(unsigned int irq) +{ + irq_disable(irq); +} + +/** + * @brief Get IRQ enable state; namespaced equivalent of irq_is_enabled(). + * + * @param irq IRQ line. + * + * @return interrupt enable state, true or false + */ +static ALWAYS_INLINE int k_irq_is_enabled(unsigned int irq) +{ + return irq_is_enabled(irq); +} + +/** + * @brief Configure a dynamic interrupt; namespaced equivalent of + * irq_connect_dynamic(). + * + * @param irq IRQ line number + * @param priority Interrupt priority + * @param routine Interrupt service routine + * @param parameter ISR parameter + * @param flags Arch-specific IRQ configuration flags + * + * @return The vector assigned to this interrupt + */ +static ALWAYS_INLINE int k_irq_connect_dynamic(unsigned int irq, unsigned int priority, + void (*routine)(const void *parameter), + const void *parameter, uint32_t flags) +{ + return irq_connect_dynamic(irq, priority, routine, parameter, flags); +} + +/** + * @brief Disconnect a dynamic interrupt; namespaced equivalent of + * irq_disconnect_dynamic(). + * + * @param irq IRQ line number + * @param priority Interrupt priority + * @param routine Interrupt service routine + * @param parameter ISR parameter + * @param flags Arch-specific IRQ configuration flags + * + * @return 0 in case of success, negative value otherwise + */ +static ALWAYS_INLINE int k_irq_disconnect_dynamic(unsigned int irq, unsigned int priority, + void (*routine)(const void *parameter), + const void *parameter, uint32_t flags) +{ + return irq_disconnect_dynamic(irq, priority, routine, parameter, flags); +} + #if defined(CONFIG_ARCH_HAS_IRQ_PENDING_OPS) || defined(__DOXYGEN__) /** * @brief Clear the pending state of an IRQ. diff --git a/tests/arch/common/irq_active/src/main.c b/tests/arch/common/irq_active/src/main.c index afd011e2ebd7..275bdf586efc 100644 --- a/tests/arch/common/irq_active/src/main.c +++ b/tests/arch/common/irq_active/src/main.c @@ -100,7 +100,7 @@ ZTEST(irq_active_tracking, test_irq_active_none_in_thread) ZTEST(irq_active_tracking, test_irq_active_in_isr) { IRQ_CONNECT(SIMPLE_LINE, SIMPLE_PRIO, simple_isr, NULL, 0); - irq_enable(SIMPLE_LINE); + k_irq_enable(SIMPLE_LINE); trigger_irq(SIMPLE_LINE); @@ -131,8 +131,8 @@ ZTEST(irq_active_tracking, test_irq_active_nested) { IRQ_CONNECT(OUTER_LINE, OUTER_PRIO, outer_isr, NULL, 0); IRQ_CONNECT(INNER_LINE, INNER_PRIO, inner_isr, NULL, 0); - irq_enable(OUTER_LINE); - irq_enable(INNER_LINE); + k_irq_enable(OUTER_LINE); + k_irq_enable(INNER_LINE); trigger_irq(OUTER_LINE); diff --git a/tests/arch/common/irq_pending/src/main.c b/tests/arch/common/irq_pending/src/main.c index fca6f9d70461..b4e0aeb56928 100644 --- a/tests/arch/common/irq_pending/src/main.c +++ b/tests/arch/common/irq_pending/src/main.c @@ -29,10 +29,10 @@ static unsigned int connect_disabled_irq_line(void) handler_runs = 0; - zassert_true(irq_connect_dynamic(irq, 1, pending_isr, NULL, 0) > 0, + zassert_true(k_irq_connect_dynamic(irq, 1, pending_isr, NULL, 0) > 0, "irq connect dynamic failed"); - irq_disable(irq); + k_irq_disable(irq); return irq; } @@ -57,14 +57,14 @@ static unsigned int pend_disabled_irq_line(void) * @details Control case for test_irq_clear_pending(). Without it the clear * test could pass simply because nothing was ever latched. * - * @see irq_enable() + * @see k_irq_enable() */ ZTEST(irq_pending, test_irq_pending_without_clear) { unsigned int irq = pend_disabled_irq_line(); - irq_enable(irq); - irq_disable(irq); + k_irq_enable(irq); + k_irq_disable(irq); zassert_equal(handler_runs, 1, "latched interrupt was not delivered (%u)", handler_runs); } @@ -86,8 +86,8 @@ ZTEST(irq_pending, test_irq_clear_pending) k_irq_clear_pending(irq); - irq_enable(irq); - irq_disable(irq); + k_irq_enable(irq); + k_irq_disable(irq); zassert_equal(handler_runs, 0, "cleared interrupt was still delivered (%u)", handler_runs); } @@ -111,8 +111,8 @@ ZTEST(irq_pending, test_irq_set_pending) zassert_equal(handler_runs, 0, "handler ran while the line was disabled"); - irq_enable(irq); - irq_disable(irq); + k_irq_enable(irq); + k_irq_disable(irq); zassert_equal(handler_runs, 1, "software-latched interrupt was not delivered (%u)", handler_runs); From f42c415af8af9aa54625c4028b92e2e3db334123 Mon Sep 17 00:00:00 2001 From: Guillaume Gautier Date: Mon, 6 Jul 2026 12:04:42 +0200 Subject: [PATCH 129/455] dts: arm: st: u0: add pwr node and wakeup pins Add pwr node and wakeup pins for STM32U0 dtsi. Signed-off-by: Guillaume Gautier --- dts/arm/st/u0/stm32u0.dtsi | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/dts/arm/st/u0/stm32u0.dtsi b/dts/arm/st/u0/stm32u0.dtsi index 48808725e2e2..eba422ea6393 100644 --- a/dts/arm/st/u0/stm32u0.dtsi +++ b/dts/arm/st/u0/stm32u0.dtsi @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -253,6 +254,51 @@ }; }; + pwr: power@40007000 { + compatible = "st,stm32-pwr"; + reg = <0x40007000 0x400>; + + wakeup-controller { + compatible = "st,stm32-pwr-wkupctrl"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + st,max-wkup-line-idx = <7>; + st,has-pwr-full-pupd; + + wkup@1 { + reg = <0x1>; + wkup-gpios = <&gpioa 0 STM32_PWR_WKUP_PIN_NOT_MUXED>; + }; + + wkup@2 { + reg = <0x2>; + wkup-gpios = <&gpioc 13 STM32_PWR_WKUP_PIN_NOT_MUXED>; + }; + + wkup@3 { + reg = <0x3>; + wkup-gpios = <&gpioa 1 STM32_PWR_WKUP_PIN_NOT_MUXED>; + }; + + wkup@4 { + reg = <0x4>; + wkup-gpios = <&gpioa 2 STM32_PWR_WKUP_PIN_NOT_MUXED>; + }; + + wkup@5 { + reg = <0x5>; + wkup-gpios = <&gpioc 5 STM32_PWR_WKUP_PIN_NOT_MUXED>; + }; + + wkup@7 { + reg = <0x7>; + wkup-gpios = <&gpiob 15 STM32_PWR_WKUP_PIN_NOT_MUXED>; + }; + }; + }; + usart1: serial@40013800 { compatible = "st,stm32-usart", "st,stm32-uart"; reg = <0x40013800 0x400>; From d4da8a69cf43adda6afc1ef56c47bce5a25598a5 Mon Sep 17 00:00:00 2001 From: Guillaume Gautier Date: Wed, 8 Jul 2026 17:06:29 +0200 Subject: [PATCH 130/455] soc: st: stm32: u0: add power support Add poweroff support for STM32U0. Signed-off-by: Guillaume Gautier --- soc/st/stm32/stm32u0x/CMakeLists.txt | 1 + soc/st/stm32/stm32u0x/Kconfig | 1 + soc/st/stm32/stm32u0x/poweroff.c | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 soc/st/stm32/stm32u0x/poweroff.c diff --git a/soc/st/stm32/stm32u0x/CMakeLists.txt b/soc/st/stm32/stm32u0x/CMakeLists.txt index 52f0c322037a..f737c62ce24d 100644 --- a/soc/st/stm32/stm32u0x/CMakeLists.txt +++ b/soc/st/stm32/stm32u0x/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_sources( ) zephyr_sources_ifdef(CONFIG_PM power.c) +zephyr_sources_ifdef(CONFIG_POWEROFF poweroff.c) zephyr_include_directories(.) diff --git a/soc/st/stm32/stm32u0x/Kconfig b/soc/st/stm32/stm32u0x/Kconfig index c8f37341c1ac..9c7183d5d4cd 100644 --- a/soc/st/stm32/stm32u0x/Kconfig +++ b/soc/st/stm32/stm32u0x/Kconfig @@ -13,6 +13,7 @@ config SOC_SERIES_STM32U0X select CPU_HAS_ARM_MPU # Software options select HAS_PM + select HAS_POWEROFF select PM_STATE_SET_IRQ_UNLOCKED select SOC_EARLY_INIT_HOOK # STM32-specific options diff --git a/soc/st/stm32/stm32u0x/poweroff.c b/soc/st/stm32/stm32u0x/poweroff.c new file mode 100644 index 000000000000..5fbefc586f23 --- /dev/null +++ b/soc/st/stm32/stm32u0x/poweroff.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +void z_sys_poweroff(void) +{ + if (IS_ENABLED(CONFIG_STM32_WKUP_PINS)) { + LL_PWR_EnablePUPDCfg(); + } + + /* No LL function to clear all flags at once; do it ourselves */ + stm32_reg_set_bits(&PWR->SCR, + PWR_SCR_CWUF1 | PWR_SCR_CWUF2 | PWR_SCR_CWUF3 | + PWR_SCR_CWUF4 | PWR_SCR_CWUF5 | PWR_SCR_CWUF7); + LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + + stm32_enter_poweroff(); +} From a204c1fcdab6060dec29a27a1e2b37a938a7c13e Mon Sep 17 00:00:00 2001 From: Guillaume Gautier Date: Mon, 17 Aug 2026 16:23:09 +0200 Subject: [PATCH 131/455] boards: st: nucleo_u083rc: button is active low Push button on Nucleo-U083RC is active low and not active high. Also add a pull-up, otherwise the input is floating. Signed-off-by: Guillaume Gautier --- boards/st/nucleo_u083rc/nucleo_u083rc.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/st/nucleo_u083rc/nucleo_u083rc.dts b/boards/st/nucleo_u083rc/nucleo_u083rc.dts index 77538eacaff7..e0d42639ad5f 100644 --- a/boards/st/nucleo_u083rc/nucleo_u083rc.dts +++ b/boards/st/nucleo_u083rc/nucleo_u083rc.dts @@ -36,7 +36,7 @@ user_button: button { label = "User"; - gpios = <&gpioc 13 GPIO_ACTIVE_HIGH>; + gpios = <&gpioc 13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; }; From c2bedf2241567825586e8a7e6645b56bb9af8979 Mon Sep 17 00:00:00 2001 From: Guillaume Gautier Date: Mon, 17 Aug 2026 16:24:57 +0200 Subject: [PATCH 132/455] samples: boards: st: power_mgmt: wkup_pins: add stm32u083c_dk overlay Add an overlay for the Nucleo-U083RC board and add it to the list of boards so that it's tested in CI. Signed-off-by: Guillaume Gautier --- .../wkup_pins/boards/nucleo_u083rc.overlay | 17 +++++++++++++++++ .../boards/st/power_mgmt/wkup_pins/tests.yaml | 1 + 2 files changed, 18 insertions(+) create mode 100644 samples/boards/st/power_mgmt/wkup_pins/boards/nucleo_u083rc.overlay diff --git a/samples/boards/st/power_mgmt/wkup_pins/boards/nucleo_u083rc.overlay b/samples/boards/st/power_mgmt/wkup_pins/boards/nucleo_u083rc.overlay new file mode 100644 index 000000000000..ba2c71bebebb --- /dev/null +++ b/samples/boards/st/power_mgmt/wkup_pins/boards/nucleo_u083rc.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + wkup-src = &user_button; + }; +}; + +&pwr { + wakeup-controller { + status = "okay"; + }; +}; diff --git a/samples/boards/st/power_mgmt/wkup_pins/tests.yaml b/samples/boards/st/power_mgmt/wkup_pins/tests.yaml index 80db1f2d39d6..516a74b070aa 100644 --- a/samples/boards/st/power_mgmt/wkup_pins/tests.yaml +++ b/samples/boards/st/power_mgmt/wkup_pins/tests.yaml @@ -11,6 +11,7 @@ tests: - nucleo_g031k8 - nucleo_l152re - nucleo_l4r5zi + - nucleo_u083rc - nucleo_u575zi_q - nucleo_u5a5zj_q - nucleo_wba55cg From 2b0b932315d589d10dc1c1a6cf1dbaaacc72c404 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Thu, 20 Aug 2026 09:54:47 -0700 Subject: [PATCH 133/455] boards: qemu: Update x86 Kconfig to be more HWMv2-y Instead of creating board symbols directly, use them from the build system. Signed-off-by: Ederson de Souza --- boards/qemu/x86/Kconfig | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/boards/qemu/x86/Kconfig b/boards/qemu/x86/Kconfig index 092f34f453d5..68963c8b81a4 100644 --- a/boards/qemu/x86/Kconfig +++ b/boards/qemu/x86/Kconfig @@ -4,16 +4,6 @@ config BOARD_QEMU_X86 bool - select CPU_HAS_FPU - -config BOARD_QEMU_X86_64 - bool - select X86_64 - -config BOARD_QEMU_X86_LAKEMONT - bool - select CPU_HAS_FPU - -config BOARD_QEMU_X86_TINY - bool - select CPU_HAS_FPU + default y + select CPU_HAS_FPU if BOARD_QEMU_X86 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY + select X86_64 if BOARD_QEMU_X86_64 From c0a8c419bf3b465e35ca9f1a009dc2f5c8bebf4d Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Tue, 25 Mar 2025 12:19:22 -0700 Subject: [PATCH 134/455] boards/qemu/x86: Add qemu_x86_64_kvm New kvm-enabled qemu board definition, which also includes CET support. Note that to have CET support on qemu, one needs a fairly recent Linux kernel (at least 6.18) and qemu (at least version 11.0). Also note that current Zephyr SDK (as of this patch, 1.0.1) qemu-x86_64 does not support CET (as it's still on version 10.x). So to use it, one needs to point to a supporting qemu (for instance, by using `QEMU_BIN_PATH` when building). Signed-off-by: Ederson de Souza --- boards/qemu/x86/Kconfig | 3 ++- boards/qemu/x86/Kconfig.defconfig | 14 +++++++++++--- boards/qemu/x86/Kconfig.qemu_x86_64_kvm | 6 ++++++ boards/qemu/x86/board.cmake | 6 +++++- boards/qemu/x86/board.yml | 7 +++++++ boards/qemu/x86/qemu_x86_64_kvm.dts | 9 +++++++++ boards/qemu/x86/qemu_x86_64_kvm.yaml | 16 ++++++++++++++++ boards/qemu/x86/qemu_x86_64_kvm_defconfig | 13 +++++++++++++ 8 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 boards/qemu/x86/Kconfig.qemu_x86_64_kvm create mode 100644 boards/qemu/x86/qemu_x86_64_kvm.dts create mode 100644 boards/qemu/x86/qemu_x86_64_kvm.yaml create mode 100644 boards/qemu/x86/qemu_x86_64_kvm_defconfig diff --git a/boards/qemu/x86/Kconfig b/boards/qemu/x86/Kconfig index 68963c8b81a4..11aa917c9e6a 100644 --- a/boards/qemu/x86/Kconfig +++ b/boards/qemu/x86/Kconfig @@ -6,4 +6,5 @@ config BOARD_QEMU_X86 bool default y select CPU_HAS_FPU if BOARD_QEMU_X86 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY - select X86_64 if BOARD_QEMU_X86_64 + select X86_64 if BOARD_QEMU_X86_64 || BOARD_QEMU_X86_64_KVM + select X86_CPU_HAS_CET if BOARD_QEMU_X86_64_KVM diff --git a/boards/qemu/x86/Kconfig.defconfig b/boards/qemu/x86/Kconfig.defconfig index 72c6eb89820f..55caeb084e47 100644 --- a/boards/qemu/x86/Kconfig.defconfig +++ b/boards/qemu/x86/Kconfig.defconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2019 Intel Corp. -if BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY +if BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY || \ + BOARD_QEMU_X86_64_KVM # The EEPROM emulator must be initialized after the flash simulator config EEPROM_INIT_PRIORITY @@ -17,7 +18,7 @@ config QEMU_TARGET config HAS_COVERAGE_SUPPORT default y -endif # BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY +endif # BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY... if BOARD_QEMU_X86 @@ -47,7 +48,7 @@ config QEMU_ICOUNT_SHIFT endif # BOARD_QEMU_X86 -if BOARD_QEMU_X86_64 +if BOARD_QEMU_X86_64 || BOARD_QEMU_X86_64_KVM config KERNEL_VM_SIZE default 0x10000000 if ACPI @@ -107,3 +108,10 @@ config DEMAND_PAGING_PAGE_FRAMES_RESERVE default 6 if NEWLIB_LIBC || (COMMON_LIBC_MALLOC && COMMON_LIBC_MALLOC_ARENA_SIZE != 0) endif # BOARD_QEMU_X86_TINY + +if BOARD_QEMU_X86_64_KVM + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default $(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency) + +endif # BOARD_QEMU_X86_64_KVM diff --git a/boards/qemu/x86/Kconfig.qemu_x86_64_kvm b/boards/qemu/x86/Kconfig.qemu_x86_64_kvm new file mode 100644 index 000000000000..16feee51c41e --- /dev/null +++ b/boards/qemu/x86/Kconfig.qemu_x86_64_kvm @@ -0,0 +1,6 @@ +# Copyright (c) 2026 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_QEMU_X86_64_KVM + select SOC_ATOM diff --git a/boards/qemu/x86/board.cmake b/boards/qemu/x86/board.cmake index e0a1922e42d4..4a9cc9452075 100644 --- a/boards/qemu/x86/board.cmake +++ b/boards/qemu/x86/board.cmake @@ -3,7 +3,11 @@ set(SUPPORTED_EMU_PLATFORMS qemu) -if(CONFIG_X86_64) +if(CONFIG_BOARD_QEMU_X86_64_KVM) + set(QEMU_BINARY_SUFFIX x86_64) + set(QEMU_CPU_TYPE host,+x2apic) + list(APPEND QEMU_EXTRA_FLAGS -rtc clock=vm --enable-kvm) +elseif(CONFIG_X86_64) set(QEMU_BINARY_SUFFIX x86_64) set(QEMU_CPU_TYPE qemu64,+x2apic) if("${CONFIG_MP_MAX_NUM_CPUS}" STREQUAL "1") diff --git a/boards/qemu/x86/board.yml b/boards/qemu/x86/board.yml index cfd84976dae2..7d772b1d3094 100644 --- a/boards/qemu/x86/board.yml +++ b/boards/qemu/x86/board.yml @@ -31,3 +31,10 @@ boards: vendor: intel socs: - name: atom + + - name: qemu_x86_64_kvm + full_name: QEMU Emulation for X86 64bit, KVM enabled + socs: + - name: atom + variants: + - name: 'nokpti' diff --git a/boards/qemu/x86/qemu_x86_64_kvm.dts b/boards/qemu/x86/qemu_x86_64_kvm.dts new file mode 100644 index 000000000000..4002a1511b57 --- /dev/null +++ b/boards/qemu/x86/qemu_x86_64_kvm.dts @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2026 Intel Corp. + * SPDX-License-Identifier: Apache-2.0 + */ +#include "qemu_x86_64.dts" + +&cpu { + clock-frequency = <25000000>; +}; diff --git a/boards/qemu/x86/qemu_x86_64_kvm.yaml b/boards/qemu/x86/qemu_x86_64_kvm.yaml new file mode 100644 index 000000000000..d2ce15211ff6 --- /dev/null +++ b/boards/qemu/x86/qemu_x86_64_kvm.yaml @@ -0,0 +1,16 @@ +identifier: qemu_x86_64_kvm +name: QEMU Emulation for X86_64 (KVM enabled) +type: qemu +arch: x86 +toolchain: + - zephyr +supported: + - smp +simulation: + - name: qemu +testing: + default: false + ignore_tags: + - benchmark + - kernel +vendor: qemu diff --git a/boards/qemu/x86/qemu_x86_64_kvm_defconfig b/boards/qemu/x86/qemu_x86_64_kvm_defconfig new file mode 100644 index 000000000000..2a910bb7cc63 --- /dev/null +++ b/boards/qemu/x86/qemu_x86_64_kvm_defconfig @@ -0,0 +1,13 @@ +CONFIG_PICOLIBC_USE_MODULE=y +CONFIG_PIC_DISABLE=y +CONFIG_LOAPIC=y +CONFIG_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_UART_CONSOLE=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_X86_DEBUG_INFO=y +CONFIG_SMP=y +CONFIG_MP_MAX_NUM_CPUS=2 +CONFIG_X86_MMU=y +CONFIG_X86_VERY_EARLY_CONSOLE=y +CONFIG_QEMU_ICOUNT=n From e97f09891cf0c8bcc3a8e14f421a09c8ff87aef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 08:34:52 +0000 Subject: [PATCH 135/455] dts: bindings: can: remove deprecated bus-speed properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the "bus-speed" and "bus-speed-data" CAN controller devicetree properties, deprecated in Zephyr 3.7 when they were renamed to "bitrate" and "bitrate-data". The nested DT_PROP_OR() fallbacks in CAN_DT_DRIVER_CONFIG_GET() collapse to a single DT_PROP_OR() on the new property names. Both bindings and the header must change together, as can-fd-controller.yaml includes can-controller.yaml. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/migration-guide-4.5.rst | 3 +++ doc/releases/release-notes-4.5.rst | 5 +++++ dts/bindings/can/can-controller.yaml | 8 -------- dts/bindings/can/can-fd-controller.yaml | 8 -------- include/zephyr/drivers/can.h | 7 +++---- 5 files changed, 11 insertions(+), 20 deletions(-) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index dc6b2eeb6c26..2c0ab9c7d9ad 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -290,6 +290,9 @@ Controller Area Network (CAN) are processed in the order received on the bus. Out-of-tree users may want to update any ``bosch,mram-cfg`` devicetree property overrides to allocate all FIFO elements to RX FIFO0. +* The deprecated ``bus-speed`` and ``bus-speed-data`` CAN controller devicetree properties have + been removed. Use ``bitrate`` and ``bitrate-data`` instead. + Counter ======= diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index b6532299029a..2c1c9db6492e 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -119,6 +119,11 @@ Removed APIs and options ``__defconfig`` * Pattern expansion in ``zephyr_code_relocate(FILES ...)``, replaced by ``file(GLOB ...)`` +* CAN + + * ``bus-speed`` + * ``bus-speed-data`` + * Comparator * ``nxp,enable-output-pin``, ``nxp,use-unfiltered-output``, ``nxp,high-speed-mode``, diff --git a/dts/bindings/can/can-controller.yaml b/dts/bindings/can/can-controller.yaml index e2825b6e6763..5a51e4e143f2 100644 --- a/dts/bindings/can/can-controller.yaml +++ b/dts/bindings/can/can-controller.yaml @@ -3,14 +3,6 @@ include: base.yaml properties: - bus-speed: - type: int - deprecated: true - description: | - Deprecated. This property has been renamed to bitrate. - - Initial bitrate in bit/s. If this is unset, the initial bitrate is set to - CONFIG_CAN_DEFAULT_BITRATE. bitrate: type: int description: | diff --git a/dts/bindings/can/can-fd-controller.yaml b/dts/bindings/can/can-fd-controller.yaml index ea357b832240..fc245e515fe8 100644 --- a/dts/bindings/can/can-fd-controller.yaml +++ b/dts/bindings/can/can-fd-controller.yaml @@ -3,14 +3,6 @@ include: can-controller.yaml properties: - bus-speed-data: - type: int - deprecated: true - description: | - Deprecated. This property has been renamed to bitrate-data. - - Initial data phase bitrate in bit/s. If this is unset, the initial data phase bitrate is set - to CONFIG_CAN_DEFAULT_BITRATE_DATA. bitrate-data: type: int description: | diff --git a/include/zephyr/drivers/can.h b/include/zephyr/drivers/can.h index 591081c612c5..3143ae87c501 100644 --- a/include/zephyr/drivers/can.h +++ b/include/zephyr/drivers/can.h @@ -370,12 +370,11 @@ struct can_driver_config { .phy = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(node_id, phys)), \ .min_bitrate = DT_CAN_TRANSCEIVER_MIN_BITRATE(node_id, _min_bitrate), \ .max_bitrate = DT_CAN_TRANSCEIVER_MAX_BITRATE(node_id, _max_bitrate), \ - .bitrate = DT_PROP_OR(node_id, bitrate, \ - DT_PROP_OR(node_id, bus_speed, CONFIG_CAN_DEFAULT_BITRATE)), \ + .bitrate = DT_PROP_OR(node_id, bitrate, CONFIG_CAN_DEFAULT_BITRATE), \ .sample_point = DT_PROP_OR(node_id, sample_point, 0), \ IF_ENABLED(CONFIG_CAN_FD_MODE, \ - (.bitrate_data = DT_PROP_OR(node_id, bitrate_data, \ - DT_PROP_OR(node_id, bus_speed_data, CONFIG_CAN_DEFAULT_BITRATE_DATA)), \ + (.bitrate_data = DT_PROP_OR(node_id, bitrate_data, \ + CONFIG_CAN_DEFAULT_BITRATE_DATA), \ .sample_point_data = DT_PROP_OR(node_id, sample_point_data, 0),)) \ } From 55efdc9fb18cb59408e228d7e0f85e669a30c25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 08:20:33 +0000 Subject: [PATCH 136/455] bluetooth: dis: remove deprecated BT_DIS_MANUF and BT_DIS_MODEL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the CONFIG_BT_DIS_MANUF and CONFIG_BT_DIS_MODEL string options, together with the BT_DIS_MANUF_DEPRECATED_USED and BT_DIS_MODEL_DEPRECATED_USED helper symbols whose only purpose was to detect their use. Use CONFIG_BT_DIS_MANUF_NAME / CONFIG_BT_DIS_MANUF_NAME_STR and CONFIG_BT_DIS_MODEL_NUMBER / CONFIG_BT_DIS_MODEL_NUMBER_STR instead. With the helper symbols gone, the two characteristic bools no longer need their "depends on !*_DEPRECATED_USED" guards and are unconditionally default y again, and the #if/#elif ladders in dis.c collapse to the non-deprecated branch. Deprecated in Zephyr 4.1. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/release-notes-4.5.rst | 5 +++++ subsys/bluetooth/services/Kconfig.dis | 24 ------------------------ subsys/bluetooth/services/dis.c | 21 +++++---------------- 3 files changed, 10 insertions(+), 40 deletions(-) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 2c1c9db6492e..1500f017299a 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -111,6 +111,11 @@ Removed APIs and options * ``CONFIG_BT_AUTO_PHY_UPDATE``, replaced by the ``BT_AUTO_PHY_CENTRAL`` and ``BT_AUTO_PHY_PERIPHERAL`` choices + * Services + + * ``CONFIG_BT_DIS_MANUF`` + * ``CONFIG_BT_DIS_MODEL`` + * Build system * ``CONFIG_BUILD_NO_GAP_FILL`` diff --git a/subsys/bluetooth/services/Kconfig.dis b/subsys/bluetooth/services/Kconfig.dis index d8b14fe56861..d0ccc98139d1 100644 --- a/subsys/bluetooth/services/Kconfig.dis +++ b/subsys/bluetooth/services/Kconfig.dis @@ -25,7 +25,6 @@ config BT_DIS_STR_MAX config BT_DIS_MODEL_NUMBER bool "Model number characteristic" - depends on !BT_DIS_MODEL_DEPRECATED_USED default y help Enable model number characteristic in Device Information Service. @@ -38,20 +37,8 @@ config BT_DIS_MODEL_NUMBER_STR Configure model number string that can be read with the model number characteristic in Device Information Service. -config BT_DIS_MODEL - string "Model name [DEPRECATED]" - help - The device model inside Device Information Service. - This option is deprecated. Use BT_DIS_MODEL_NUMBER and BT_DIS_MODEL_NUMBER_STR instead. - -config BT_DIS_MODEL_DEPRECATED_USED - bool - default y if BT_DIS_MODEL != "" - select DEPRECATED - config BT_DIS_MANUF_NAME bool "Manufacturer name characteristic" - depends on !BT_DIS_MANUF_DEPRECATED_USED default y help Enable manufacturer name characteristic in Device Information Service. @@ -64,17 +51,6 @@ config BT_DIS_MANUF_NAME_STR Configure manufacturer name string that can be read with the manufacturer name characteristic in Device Information Service. -config BT_DIS_MANUF - string "Manufacturer name [DEPRECATED]" - help - The device manufacturer inside Device Information Service. - This option is deprecated. Use BT_DIS_MANUF_NAME and BT_DIS_MANUF_NAME_STR instead. - -config BT_DIS_MANUF_DEPRECATED_USED - bool - default y if BT_DIS_MANUF != "" - select DEPRECATED - config BT_DIS_PNP bool "PnP_ID characteristic" default y diff --git a/subsys/bluetooth/services/dis.c b/subsys/bluetooth/services/dis.c index f3fdccf100e7..ff0fa69e5ec4 100644 --- a/subsys/bluetooth/services/dis.c +++ b/subsys/bluetooth/services/dis.c @@ -61,16 +61,10 @@ static uint8_t dis_system_id[8] = {BT_BYTES_LIST_LE40((uint64_t)CONFIG_BT_DIS_SY #if defined(CONFIG_BT_DIS_MODEL_NUMBER) BUILD_ASSERT(sizeof(CONFIG_BT_DIS_MODEL_NUMBER_STR) <= CONFIG_BT_DIS_STR_MAX + 1); static uint8_t dis_model[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_MODEL_NUMBER_STR; -#elif defined(CONFIG_BT_DIS_MODEL_DEPRECATED_USED) -BUILD_ASSERT(sizeof(CONFIG_BT_DIS_MODEL) <= CONFIG_BT_DIS_STR_MAX + 1); -static uint8_t dis_model[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_MODEL; #endif #if defined(CONFIG_BT_DIS_MANUF_NAME) BUILD_ASSERT(sizeof(CONFIG_BT_DIS_MANUF_NAME_STR) <= CONFIG_BT_DIS_STR_MAX + 1); static uint8_t dis_manuf[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_MANUF_NAME_STR; -#elif defined(CONFIG_BT_DIS_MANUF_DEPRECATED_USED) -BUILD_ASSERT(sizeof(CONFIG_BT_DIS_MANUF) <= CONFIG_BT_DIS_STR_MAX + 1); -static uint8_t dis_manuf[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_MANUF; #endif #if defined(CONFIG_BT_DIS_SERIAL_NUMBER) BUILD_ASSERT(sizeof(CONFIG_BT_DIS_SERIAL_NUMBER_STR) <= CONFIG_BT_DIS_STR_MAX + 1); @@ -126,13 +120,9 @@ static uint8_t dis_ieee_rcdl[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_IEEE_RCD #if defined(CONFIG_BT_DIS_MODEL_NUMBER) #define BT_DIS_MODEL_REF CONFIG_BT_DIS_MODEL_NUMBER_STR -#elif defined(CONFIG_BT_DIS_MODEL_DEPRECATED_USED) -#define BT_DIS_MODEL_REF CONFIG_BT_DIS_MODEL #endif #if defined(CONFIG_BT_DIS_MANUF_NAME) #define BT_DIS_MANUF_REF CONFIG_BT_DIS_MANUF_NAME_STR -#elif defined(CONFIG_BT_DIS_MANUF_DEPRECATED_USED) -#define BT_DIS_MANUF_REF CONFIG_BT_DIS_MANUF #endif #define BT_DIS_SERIAL_NUMBER_STR_REF CONFIG_BT_DIS_SERIAL_NUMBER_STR #define BT_DIS_FW_REV_STR_REF CONFIG_BT_DIS_FW_REV_STR @@ -147,8 +137,7 @@ static uint8_t dis_ieee_rcdl[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_IEEE_RCD #endif /* CONFIG_BT_DIS_SETTINGS */ #define BT_DIS_READ_STR_USED \ - (CONFIG_BT_DIS_MODEL_NUMBER || CONFIG_BT_DIS_MODEL_DEPRECATED_USED || \ - CONFIG_BT_DIS_MANUF_NAME || CONFIG_BT_DIS_MANUF_DEPRECATED_USED || \ + (CONFIG_BT_DIS_MODEL_NUMBER || CONFIG_BT_DIS_MANUF_NAME || \ CONFIG_BT_DIS_SERIAL_NUMBER || CONFIG_BT_DIS_FW_REV || CONFIG_BT_DIS_HW_REV || \ CONFIG_BT_DIS_SW_REV || CONFIG_BT_DIS_IEEE_RCDL) @@ -252,12 +241,12 @@ static ssize_t read_udi(struct bt_conn *conn, const struct bt_gatt_attr *attr, v BT_GATT_SERVICE_DEFINE( dis_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_DIS), -#if defined(CONFIG_BT_DIS_MODEL_NUMBER) || defined(CONFIG_BT_DIS_MODEL_DEPRECATED_USED) +#if defined(CONFIG_BT_DIS_MODEL_NUMBER) BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MODEL_NUMBER, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_str, NULL, BT_DIS_MODEL_REF), #endif -#if defined(CONFIG_BT_DIS_MANUF_NAME) || defined(CONFIG_BT_DIS_MANUF_DEPRECATED_USED) +#if defined(CONFIG_BT_DIS_MANUF_NAME) BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MANUFACTURER_NAME, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_str, NULL, BT_DIS_MANUF_REF), #endif @@ -344,7 +333,7 @@ static int dis_set(const char *name, size_t len_rd, settings_read_cb read_cb, vo ARG_UNUSED(len); nlen = settings_name_next(name, &next); -#if defined(CONFIG_BT_DIS_MANUF_NAME) || defined(CONFIG_BT_DIS_MANUF_DEPRECATED_USED) +#if defined(CONFIG_BT_DIS_MANUF_NAME) if (!strncmp(name, "manuf", nlen)) { len = read_cb(store, &dis_manuf, sizeof(dis_manuf) - 1); if (len < 0) { @@ -357,7 +346,7 @@ static int dis_set(const char *name, size_t len_rd, settings_read_cb read_cb, vo return 0; } #endif -#if defined(CONFIG_BT_DIS_MODEL_NUMBER) || defined(CONFIG_BT_DIS_MODEL_DEPRECATED_USED) +#if defined(CONFIG_BT_DIS_MODEL_NUMBER) if (!strncmp(name, "model", nlen)) { len = read_cb(store, &dis_model, sizeof(dis_model) - 1); if (len < 0) { From f772b74c01db31280489ca70561b8dbe246181c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 08:21:39 +0000 Subject: [PATCH 137/455] bluetooth: gatt: remove deprecated CCC name compatibility macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the ``_bt_gatt_ccc`` and ``BT_GATT_CCC_INITIALIZER`` compatibility macros that kept the pre-rename names alive. Use struct bt_gatt_ccc_managed_user_data and BT_GATT_CCC_MANAGED_USER_DATA_INIT instead; every in-tree user already does. Deprecated in Zephyr 4.2. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/release-notes-4.5.rst | 2 ++ include/zephyr/bluetooth/gatt.h | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 1500f017299a..828b35f3fb1c 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -110,6 +110,8 @@ Removed APIs and options * ``CONFIG_BT_AUTO_PHY_UPDATE``, replaced by the ``BT_AUTO_PHY_CENTRAL`` and ``BT_AUTO_PHY_PERIPHERAL`` choices + * ``_bt_gatt_ccc`` + * ``BT_GATT_CCC_INITIALIZER`` * Services diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index 85d51d48a8cf..9c7b1c76cd39 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -1086,9 +1086,6 @@ struct bt_gatt_ccc_cfg { uint16_t value; }; -/** Macro to keep old name for deprecation period. */ -#define _bt_gatt_ccc bt_gatt_ccc_managed_user_data __DEPRECATED_MACRO - /** @brief Internal representation of CCC value. * * @note Only use this as an argument for @ref BT_GATT_CCC_MANAGED @@ -1185,9 +1182,6 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags); -/** Macro to keep old name for deprecation period. */ -#define BT_GATT_CCC_INITIALIZER BT_GATT_CCC_MANAGED_USER_DATA_INIT __DEPRECATED_MACRO - /** * @brief Initialize Client Characteristic Configuration Declaration Macro. * From ce65ad9e81a8834c10c8520d8e1248800f57c0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 08:22:31 +0000 Subject: [PATCH 138/455] bluetooth: host: remove deprecated BT_CONN_TX_MAX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the CONFIG_BT_CONN_TX_MAX Kconfig option and its only in-tree user, the SiWx91x SoC configdefault. No C code has read the symbol since it was deprecated, so it had no effect on any build; the number of pending TX buffers with a callback follows CONFIG_BT_BUF_ACL_TX_COUNT, which was already both the default and the range floor of the removed option. The SiWx91x default of 15 matched that SoC's BT_BUF_ACL_TX_COUNT default, so behaviour is unchanged there too. Deprecated in Zephyr 4.2. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/migration-guide-4.5.rst | 4 ++++ doc/releases/release-notes-4.5.rst | 1 + soc/silabs/silabs_siwx91x/Kconfig.defconfig | 3 --- subsys/bluetooth/host/Kconfig | 9 --------- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 2c0ab9c7d9ad..08128e773e28 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -1426,6 +1426,10 @@ Bluetooth Host option: the central choice defaults to :kconfig:option:`CONFIG_BT_AUTO_PHY_CENTRAL_2M`, so both roles must be set to ``_NONE`` explicitly. +* The deprecated ``CONFIG_BT_CONN_TX_MAX`` Kconfig option has been removed. It has been + deprecated since Zephyr 4.2, and the number of pending TX buffers with a callback always + follows :kconfig:option:`CONFIG_BT_BUF_ACL_TX_COUNT`. + Bluetooth Services ================== diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 828b35f3fb1c..50b90a27a71b 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -112,6 +112,7 @@ Removed APIs and options ``BT_AUTO_PHY_PERIPHERAL`` choices * ``_bt_gatt_ccc`` * ``BT_GATT_CCC_INITIALIZER`` + * ``CONFIG_BT_CONN_TX_MAX`` * Services diff --git a/soc/silabs/silabs_siwx91x/Kconfig.defconfig b/soc/silabs/silabs_siwx91x/Kconfig.defconfig index 82c13eed2b73..b8444e00560d 100644 --- a/soc/silabs/silabs_siwx91x/Kconfig.defconfig +++ b/soc/silabs/silabs_siwx91x/Kconfig.defconfig @@ -79,9 +79,6 @@ configdefault BT_BUF_ACL_TX_COUNT configdefault BT_BUF_EVT_RX_COUNT default 20 -configdefault BT_CONN_TX_MAX - default 15 - endif rsource "*/Kconfig.defconfig" diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index d62b7016c60c..3a44336f75dc 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -312,15 +312,6 @@ config BT_CONN_FRAG_COUNT if BT_CONN -config BT_CONN_TX_MAX - int "Maximum number of pending TX buffers with a callback [DEPRECATED]" - default BT_BUF_ACL_TX_COUNT - range BT_BUF_ACL_TX_COUNT $(UINT8_MAX) - help - Maximum number of pending TX buffers that have an associated - callback. Normally this can be left to the default value, which - is equal to the number of TX buffers in the controller. - config BT_CONN_PARAM_ANY bool "Accept any values for connection parameters" help From dcc7db008e2069a0a1d8ad40388333756c93b3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 08:23:02 +0000 Subject: [PATCH 139/455] bluetooth: controller: remove deprecated BT_CTRL_ADV_ADI_IN_SCAN_RSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the CONFIG_BT_CTRL_ADV_ADI_IN_SCAN_RSP compatibility symbol, which only selected the renamed CONFIG_BT_CTLR_ADV_ADI_IN_SCAN_RSP. The CTLR-spelled option and its users in ull_adv_aux.c are untouched. Deprecated in Zephyr 4.3. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/release-notes-4.5.rst | 4 ++++ subsys/bluetooth/controller/Kconfig.ll_sw_split | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 50b90a27a71b..52bbece8da6f 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -90,6 +90,10 @@ Removed APIs and options * Bluetooth + * Controller + + * ``CONFIG_BT_CTRL_ADV_ADI_IN_SCAN_RSP`` + * Host * The ``CONFIG_BT_RECV_CONTEXT`` choice and its options ``CONFIG_BT_RECV_WORKQ_SYS`` diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index 0c029fe3dbd4..9499d47187f2 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -533,16 +533,6 @@ config BT_CTLR_ADV_EXT_PDU_EXTRA_DATA_MEMORY must be synchronized with CTEInfo field in extended advertising header that is part of PDU data. -config BT_CTRL_ADV_ADI_IN_SCAN_RSP - bool "Include ADI in AUX_SCAN_RSP PDU [DEPRECATED]" - depends on BT_BROADCASTER && BT_CTLR_ADV_EXT - select BT_CTLR_ADV_ADI_IN_SCAN_RSP - select DEPRECATED - help - DEPRECATED: Renamed as BT_CTLR_ADV_ADI_IN_SCAN_RSP. - - Enable ADI field in AUX_SCAN_RSP PDU. - config BT_CTLR_ADV_ADI_IN_SCAN_RSP bool "Include ADI in AUX_SCAN_RSP PDU" depends on BT_BROADCASTER && BT_CTLR_ADV_EXT From 27739525f3578b03c246e5fe0b5c2626c44821bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 08:23:31 +0000 Subject: [PATCH 140/455] bluetooth: mesh: remove deprecated BLOB IO flash erase options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove CONFIG_BT_MESH_BLOB_IO_FLASH_WITH_ERASE and CONFIG_BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE. There is no replacement: the BLOB IO Flash module stopped reading them in Zephyr 4.3 and instead queries the erase capability at runtime through flash_params_get_erase_cap(), so both options have been no-ops since then. The enclosing "if BT_MESH_BLOB_IO_FLASH" block is kept for BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX. Deprecated in Zephyr 4.3. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/releases/migration-guide-4.5.rst | 8 ++++++++ doc/releases/release-notes-4.5.rst | 5 +++++ subsys/bluetooth/mesh/Kconfig | 17 ----------------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 08128e773e28..7baf8447ba41 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -1430,6 +1430,14 @@ Bluetooth Host deprecated since Zephyr 4.2, and the number of pending TX buffers with a callback always follows :kconfig:option:`CONFIG_BT_BUF_ACL_TX_COUNT`. +Bluetooth Mesh +============== + +* The deprecated ``CONFIG_BT_MESH_BLOB_IO_FLASH_WITH_ERASE`` and + ``CONFIG_BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE`` Kconfig options have been removed, with no + replacement. They have been deprecated since Zephyr 4.3, where the BLOB IO Flash module + started querying the erase capability at runtime, and have had no effect since. + Bluetooth Services ================== diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 52bbece8da6f..68e62812da0e 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -118,6 +118,11 @@ Removed APIs and options * ``BT_GATT_CCC_INITIALIZER`` * ``CONFIG_BT_CONN_TX_MAX`` + * Mesh + + * ``CONFIG_BT_MESH_BLOB_IO_FLASH_WITH_ERASE`` + * ``CONFIG_BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE`` + * Services * ``CONFIG_BT_DIS_MANUF`` diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index c0ac8194fa48..592f59dd9ed9 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -1071,23 +1071,6 @@ config BT_MESH_BLOB_IO_FLASH if BT_MESH_BLOB_IO_FLASH -config BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE - bool "BLOB flash support for devices without erase [DEPRECATED]" - default n - depends on FLASH_HAS_NO_EXPLICIT_ERASE - select DEPRECATED - help - This option is deprecated and is no longer used by the BLOB IO Flash module. - -config BT_MESH_BLOB_IO_FLASH_WITH_ERASE - bool "BLOB flash support for devices with erase [DEPRECATED]" - default n - depends on FLASH_HAS_EXPLICIT_ERASE - depends on FLASH_PAGE_LAYOUT - select DEPRECATED - help - This option is deprecated and is no longer used by the BLOB IO Flash module. - config BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX int "Maximum supported write block size" default 4 From a24d64007a9ee19392e187a2b580d1a17e2a37d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 12:23:38 +0200 Subject: [PATCH 141/455] boards: remove board name aliases deprecated in 4.3 and earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the eleven deprecated board name aliases from boards/deprecated.cmake that reached the end of their two-release deprecation period: arduino_uno_r4_minima -> arduino_uno_r4@minima arduino_uno_r4_wifi -> arduino_uno_r4@wifi esp32c6_devkitc -> esp32c6_devkitc/esp32c6/hpcore esp32_devkitc_wroom/esp32/procpu -> esp32_devkitc/esp32/procpu esp32_devkitc_wroom/esp32/appcpu -> esp32_devkitc/esp32/appcpu esp32_devkitc_wrover/esp32/procpu -> esp32_devkitc/esp32/procpu esp32_devkitc_wrover/esp32/appcpu -> esp32_devkitc/esp32/appcpu neorv32 -> neorv32/neorv32/up5kdemo panb511evb -> panb611evb scobc_module1 -> scobc_a1 xiao_esp32c6 -> xiao_esp32c6/esp32c6/hpcore All but panb511evb were already present in v4.2.0, and panb511evb was added during the 4.3 cycle, so all of them were deprecated in 4.3 or earlier and are due for removal in 4.5. The board directories themselves are untouched; only the old-name redirection goes away. Aliases introduced in 4.4 or later are left in place. This is a follow-up to commit 5b51b03a4839 ("boards: deprecated: remove raytac_an54l15q_db/nrf54l15/cpuapp entry"), which dropped that alias without documenting it; the release notes and migration guide entries added here cover it as well. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- boards/deprecated.cmake | 33 ---------------------------- doc/releases/migration-guide-4.5.rst | 17 ++++++++++++++ doc/releases/release-notes-4.5.rst | 17 ++++++++++++++ 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/boards/deprecated.cmake b/boards/deprecated.cmake index 22027e7133fb..60dad11d71a4 100644 --- a/boards/deprecated.cmake +++ b/boards/deprecated.cmake @@ -13,39 +13,6 @@ # https://docs.zephyrproject.org/latest/develop/api/api_lifecycle.html#deprecated, # so these aliases are eventually removed -set(arduino_uno_r4_minima_DEPRECATED - arduino_uno_r4@minima -) -set(arduino_uno_r4_wifi_DEPRECATED - arduino_uno_r4@wifi -) -set(esp32c6_devkitc_DEPRECATED - esp32c6_devkitc/esp32c6/hpcore -) -set(neorv32_DEPRECATED - neorv32/neorv32/up5kdemo -) -set(panb511evb_DEPRECATED - panb611evb -) -set(xiao_esp32c6_DEPRECATED - xiao_esp32c6/esp32c6/hpcore -) -set(esp32_devkitc_wroom/esp32/procpu_DEPRECATED - esp32_devkitc/esp32/procpu -) -set(esp32_devkitc_wrover/esp32/procpu_DEPRECATED - esp32_devkitc/esp32/procpu -) -set(esp32_devkitc_wroom/esp32/appcpu_DEPRECATED - esp32_devkitc/esp32/appcpu -) -set(esp32_devkitc_wrover/esp32/appcpu_DEPRECATED - esp32_devkitc/esp32/appcpu -) -set(scobc_module1_DEPRECATED - scobc_a1 -) set(fvp_base_revc_2xaemv8a_DEPRECATED fvp_base_revc_2xaem/v8a ) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 7baf8447ba41..ac8ccdec4d43 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -217,6 +217,23 @@ Boards or :c:func:`spi_transceive_cb` without DMA) on an affected board must now explicitly enable :kconfig:option:`CONFIG_SPI_STM32_INTERRUPT` in their own configuration. (:github:`116218`) +* The following board name aliases, deprecated in v4.3 or earlier, have been removed + (:github:`116657`, :github:`116750`). Build for the board target the alias used to + redirect to instead: + + * ``arduino_uno_r4_minima`` → ``arduino_uno_r4@minima`` + * ``arduino_uno_r4_wifi`` → ``arduino_uno_r4@wifi`` + * ``esp32c6_devkitc`` → ``esp32c6_devkitc/esp32c6/hpcore`` + * ``esp32_devkitc_wroom/esp32/procpu`` and ``esp32_devkitc_wrover/esp32/procpu`` → + ``esp32_devkitc/esp32/procpu`` + * ``esp32_devkitc_wroom/esp32/appcpu`` and ``esp32_devkitc_wrover/esp32/appcpu`` → + ``esp32_devkitc/esp32/appcpu`` + * ``neorv32`` → ``neorv32/neorv32/up5kdemo`` + * ``panb511evb`` → ``panb611evb`` + * ``raytac_an54l15q_db/nrf54l15/cpuapp`` → ``raytac_an54lq_db_15/nrf54l15/cpuapp`` + * ``scobc_module1`` → ``scobc_a1`` + * ``xiao_esp32c6`` → ``xiao_esp32c6/esp32c6/hpcore`` + Device Drivers and Devicetree ***************************** diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index 68e62812da0e..f7797c50cecc 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -128,6 +128,23 @@ Removed APIs and options * ``CONFIG_BT_DIS_MANUF`` * ``CONFIG_BT_DIS_MODEL`` +* Boards + + * Dropped the following deprecated board aliases: + + * ``arduino_uno_r4_minima`` + * ``arduino_uno_r4_wifi`` + * ``esp32c6_devkitc`` + * ``esp32_devkitc_wroom/esp32/procpu`` + * ``esp32_devkitc_wroom/esp32/appcpu`` + * ``esp32_devkitc_wrover/esp32/procpu`` + * ``esp32_devkitc_wrover/esp32/appcpu`` + * ``neorv32`` + * ``panb511evb`` + * ``raytac_an54l15q_db/nrf54l15/cpuapp`` + * ``scobc_module1`` + * ``xiao_esp32c6`` + * Build system * ``CONFIG_BUILD_NO_GAP_FILL`` From be39a96bdb2634d7fbd3ef5937ba20e1a0c8e5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 19 Aug 2026 09:01:36 +0000 Subject: [PATCH 142/455] boards: intel: cyclonev_socdk: fix dangling ds3231 compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The removal of the legacy DS3231 counter driver also dropped the dts/bindings/counter/maxim,ds3231.yaml binding, but left this board declaring compatible = "maxim,ds3231". No binding matches that compatible any more, so the node ends up unbound. Point it at maxim,ds3231-mfd, the binding that replaced it at the same I2C address. No maxim,ds3231-rtc child is added: that binding requires isw-gpios, which rtc_ds3231.c dereferences with GPIO_DT_SPEC_INST_GET rather than the _OR variant, and this board has never described such a GPIO. Adding the child would make CONFIG_RTC_DS3231 default to y as soon as CONFIG_RTC=y and then fail to build. A board maintainer who wires the interrupt/square-wave pin can add the child together with its isw-gpios property. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- boards/intel/socfpga_std/cyclonev_socdk/cyclonev_socdk.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/intel/socfpga_std/cyclonev_socdk/cyclonev_socdk.dts b/boards/intel/socfpga_std/cyclonev_socdk/cyclonev_socdk.dts index d2d9b65fabe7..1ca0bd903641 100644 --- a/boards/intel/socfpga_std/cyclonev_socdk/cyclonev_socdk.dts +++ b/boards/intel/socfpga_std/cyclonev_socdk/cyclonev_socdk.dts @@ -79,7 +79,7 @@ }; ds3231: rtc@68 { - compatible = "maxim,ds3231"; + compatible = "maxim,ds3231-mfd"; reg = <0x68>; }; }; From e64f6beca8a4fa12e160138de8aad672fa6b21b1 Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Tue, 25 Aug 2026 21:32:59 -0300 Subject: [PATCH 143/455] drivers: wifi: esp32: keep slp iram opt off in no-blobs builds Without CONFIG_PM the Wi-Fi SLP IRAM optimization path calls esp_wifi_internal_update_modem_sleep_default_params(), which is only provided by the Wi-Fi blobs. Builds on Wi-Fi 6 SoCs with CONFIG_BUILD_ONLY_NO_BLOBS=y fail to link, so keep the option default off there until a stub is available. Signed-off-by: Sylvio Alves --- drivers/wifi/esp32/Kconfig.esp32 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/wifi/esp32/Kconfig.esp32 b/drivers/wifi/esp32/Kconfig.esp32 index cdd9da89592a..035e4cff3888 100644 --- a/drivers/wifi/esp32/Kconfig.esp32 +++ b/drivers/wifi/esp32/Kconfig.esp32 @@ -326,7 +326,9 @@ config ESP32_WIFI_SLP_IRAM_OPT bool "Wi-Fi SLP IRAM speed optimization" select SOC_ESP32_PM_SLP_DEFAULT_PARAMS_OPT if PM && TICKLESS_KERNEL select ESP32_PM_SLP_IRAM_OPT if PM && TICKLESS_KERNEL - default y if SOC_ESP32_WIFI_HE_SUPPORT + # Keep disabled in no-blobs builds: without CONFIG_PM this path + # links a modem sleep function that only the blobs provide. + default y if SOC_ESP32_WIFI_HE_SUPPORT && !BUILD_ONLY_NO_BLOBS help Select this option to place called Wi-Fi library TBTT process and receive beacon functions in IRAM. Some functions can be put in IRAM either by From 3e5b6ace9337398c0f66aec3f5811fec75b76c02 Mon Sep 17 00:00:00 2001 From: Mark Wang Date: Tue, 7 Jul 2026 18:45:25 +0800 Subject: [PATCH 144/455] bluetooth: classic: a2dp: register AVDTP cb in bt_a2dp_register_cb The AVDTP event handlers are now registered when the application registers its A2DP callback, which is a more appropriate time since A2DP cannot function when the application doesn't register a callback to enable the A2DP functionality. Signed-off-by: Mark Wang --- subsys/bluetooth/host/classic/a2dp.c | 43 +++++++++---------- subsys/bluetooth/host/classic/a2dp_internal.h | 13 ------ subsys/bluetooth/host/classic/l2cap_br.c | 5 --- 3 files changed, 21 insertions(+), 40 deletions(-) delete mode 100644 subsys/bluetooth/host/classic/a2dp_internal.h diff --git a/subsys/bluetooth/host/classic/a2dp.c b/subsys/bluetooth/host/classic/a2dp.c index 84fbeadc104f..6b0a1b494ce2 100644 --- a/subsys/bluetooth/host/classic/a2dp.c +++ b/subsys/bluetooth/host/classic/a2dp.c @@ -45,7 +45,6 @@ #include #include #include "avdtp_internal.h" -#include "a2dp_internal.h" #define LOG_LEVEL CONFIG_BT_A2DP_LOG_LEVEL #include @@ -1504,27 +1503,6 @@ static struct bt_avdtp_event_cb avdtp_cb = { .accept = a2dp_accept, }; -void bt_a2dp_init(void) -{ - __maybe_unused int err; - - static bool initialized; - - if (initialized) { - return; - } - - /* Register event handlers with AVDTP */ - err = bt_avdtp_register(&avdtp_cb); - if ((err < 0) && (err != -EALREADY)) { - LOG_ERR("A2DP registration failed (err %d)", err); - return; - } - - LOG_DBG("A2DP Initialized successfully."); - initialized = true; -} - struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn) { struct bt_a2dp *a2dp; @@ -1594,6 +1572,27 @@ struct bt_conn *bt_a2dp_get_conn(struct bt_a2dp *a2dp) int bt_a2dp_register_cb(struct bt_a2dp_cb *cb) { + int err; + + if (cb == NULL) { + return -EINVAL; + } + + if (a2dp_cb != NULL) { + return -EALREADY; + } + a2dp_cb = cb; + + /* Register event handlers with AVDTP */ + err = bt_avdtp_register(&avdtp_cb); + if ((err < 0) && (err != -EALREADY)) { + a2dp_cb = NULL; + LOG_ERR("A2DP registration failed (err %d)", err); + return err; + } + + LOG_DBG("A2DP cbs registered."); + return 0; } diff --git a/subsys/bluetooth/host/classic/a2dp_internal.h b/subsys/bluetooth/host/classic/a2dp_internal.h deleted file mode 100644 index dd4f327ba2f9..000000000000 --- a/subsys/bluetooth/host/classic/a2dp_internal.h +++ /dev/null @@ -1,13 +0,0 @@ -/** @file - * @brief Advance Audio Distribution Profile Internal header. - */ - -/* - * Copyright (c) 2015-2016 Intel Corporation - * Copyright 2025 NXP - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* To be called when first SEP is being registered */ -void bt_a2dp_init(void); diff --git a/subsys/bluetooth/host/classic/l2cap_br.c b/subsys/bluetooth/host/classic/l2cap_br.c index 9be0d1afb823..ab4856916fe3 100644 --- a/subsys/bluetooth/host/classic/l2cap_br.c +++ b/subsys/bluetooth/host/classic/l2cap_br.c @@ -27,7 +27,6 @@ #include #include "l2cap_br_internal.h" #include "avdtp_internal.h" -#include "a2dp_internal.h" #include "avctp_internal.h" #include "avrcp_internal.h" #include "did_internal.h" @@ -6437,10 +6436,6 @@ void bt_l2cap_br_init(void) bt_sdp_init(); - if (IS_ENABLED(CONFIG_BT_A2DP)) { - bt_a2dp_init(); - } - if (IS_ENABLED(CONFIG_BT_AVRCP)) { bt_avrcp_init(); } From 094f7819493826a70e1601483c118e4a6c9fd83a Mon Sep 17 00:00:00 2001 From: Mark Wang Date: Tue, 7 Jul 2026 18:43:16 +0800 Subject: [PATCH 145/455] bluetooth: classic: avdtp: register L2CAP server in bt_avdtp_register The L2CAP server is now registered when the upper layer registers AVDTP callback, which is a more appropriate time since AVDTP cannot function when the upper layer doesn't register callback to enable the AVDTP function. Signed-off-by: Mark Wang --- subsys/bluetooth/host/classic/avdtp.c | 47 ++++++++----------- .../bluetooth/host/classic/avdtp_internal.h | 3 -- subsys/bluetooth/host/classic/l2cap_br.c | 5 -- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/subsys/bluetooth/host/classic/avdtp.c b/subsys/bluetooth/host/classic/avdtp.c index 4f47f59f442c..2fecdd2886af 100644 --- a/subsys/bluetooth/host/classic/avdtp.c +++ b/subsys/bluetooth/host/classic/avdtp.c @@ -2248,8 +2248,19 @@ int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server, /* Application will register its callback */ int bt_avdtp_register(struct bt_avdtp_event_cb *cb) { + int err; + static struct bt_l2cap_server avdtp_l2cap = { + .psm = BT_L2CAP_PSM_AVDTP, + .sec_level = BT_SECURITY_L2, + .accept = bt_avdtp_l2cap_accept, + }; + LOG_DBG(""); + if (cb == NULL) { + return -EINVAL; + } + if (event_cb == cb) { return -EALREADY; } @@ -2260,6 +2271,14 @@ int bt_avdtp_register(struct bt_avdtp_event_cb *cb) event_cb = cb; + /* Register AVDTP PSM with L2CAP */ + err = bt_l2cap_br_server_register(&avdtp_l2cap); + if ((err < 0) && (err != -EEXIST)) { + event_cb = NULL; + LOG_ERR("AVDTP L2CAP Registration failed %d", err); + return err; + } + return 0; } @@ -2304,34 +2323,6 @@ int bt_avdtp_register_sep(uint8_t media_type, uint8_t sep_type, struct bt_avdtp_ return 0; } -/* init function */ -void bt_avdtp_init(void) -{ - int err; - - static bool initialized; - static struct bt_l2cap_server avdtp_l2cap = { - .psm = BT_L2CAP_PSM_AVDTP, - .sec_level = BT_SECURITY_L2, - .accept = bt_avdtp_l2cap_accept, - }; - - LOG_DBG(""); - - if (initialized) { - return; - } - - /* Register AVDTP PSM with L2CAP */ - err = bt_l2cap_br_server_register(&avdtp_l2cap); - if ((err < 0) && (err != -EEXIST)) { - LOG_ERR("AVDTP L2CAP Registration failed %d", err); - return; - } - - initialized = true; -} - /* AVDTP Discover Request */ int bt_avdtp_discover(struct bt_avdtp *session, struct bt_avdtp_discover_params *param) { diff --git a/subsys/bluetooth/host/classic/avdtp_internal.h b/subsys/bluetooth/host/classic/avdtp_internal.h index e9758570b03a..cb7ded30dac7 100644 --- a/subsys/bluetooth/host/classic/avdtp_internal.h +++ b/subsys/bluetooth/host/classic/avdtp_internal.h @@ -278,9 +278,6 @@ struct bt_avdtp_event_cb { int (*accept)(struct bt_conn *conn, struct bt_avdtp **session); }; -/* Initialize AVDTP layer*/ -void bt_avdtp_init(void); - /* Application register with AVDTP layer */ int bt_avdtp_register(struct bt_avdtp_event_cb *cb); diff --git a/subsys/bluetooth/host/classic/l2cap_br.c b/subsys/bluetooth/host/classic/l2cap_br.c index ab4856916fe3..681baa3c4d96 100644 --- a/subsys/bluetooth/host/classic/l2cap_br.c +++ b/subsys/bluetooth/host/classic/l2cap_br.c @@ -26,7 +26,6 @@ #include #include #include "l2cap_br_internal.h" -#include "avdtp_internal.h" #include "avctp_internal.h" #include "avrcp_internal.h" #include "did_internal.h" @@ -6426,10 +6425,6 @@ void bt_l2cap_br_init(void) bt_rfcomm_init(); } - if (IS_ENABLED(CONFIG_BT_AVDTP)) { - bt_avdtp_init(); - } - if (IS_ENABLED(CONFIG_BT_AVCTP)) { bt_avctp_init(); } From 92cf2c9932df60cad8ce5745eecaa2d49ee54e5f Mon Sep 17 00:00:00 2001 From: Mark Wang Date: Thu, 6 Aug 2026 11:29:20 +0800 Subject: [PATCH 146/455] samples: bluetooth: classic: a2dp: check bt_a2dp_register_cb return Check the return value of bt_a2dp_register_cb() in the a2dp samples since the function can fail. Signed-off-by: Mark Wang --- samples/bluetooth/classic/a2dp_sink/src/main.c | 4 +++- samples/bluetooth/classic/a2dp_source/src/main.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/samples/bluetooth/classic/a2dp_sink/src/main.c b/samples/bluetooth/classic/a2dp_sink/src/main.c index 34b97a572fb1..442575b9957e 100644 --- a/samples/bluetooth/classic/a2dp_sink/src/main.c +++ b/samples/bluetooth/classic/a2dp_sink/src/main.c @@ -229,7 +229,9 @@ static void bt_ready(int err) bt_sdp_register_service(&a2dp_sink_rec); bt_a2dp_register_ep(&sbc_sink_ep, BT_AVDTP_AUDIO, BT_AVDTP_SINK); - bt_a2dp_register_cb(&a2dp_cb); + + err = bt_a2dp_register_cb(&a2dp_cb); + __ASSERT(err == 0, "Failed to register A2DP callbacks"); err = bt_br_set_connectable(true, NULL); if (err != 0) { diff --git a/samples/bluetooth/classic/a2dp_source/src/main.c b/samples/bluetooth/classic/a2dp_source/src/main.c index 05153da353ca..e28688624886 100644 --- a/samples/bluetooth/classic/a2dp_source/src/main.c +++ b/samples/bluetooth/classic/a2dp_source/src/main.c @@ -783,7 +783,8 @@ static void bt_ready(int err) bt_a2dp_register_ep(&sbc_source_ep, BT_AVDTP_AUDIO, BT_AVDTP_SOURCE); - bt_a2dp_register_cb(&a2dp_cb); + err = bt_a2dp_register_cb(&a2dp_cb); + __ASSERT(err == 0, "Failed to register A2DP callbacks"); k_work_queue_init(&audio_play_work_q); k_work_queue_start(&audio_play_work_q, audio_play_work_q_thread_stack, From 9de54fa5f4b66334c10784bf6268202517a7a90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 1 Aug 2026 00:46:41 +0000 Subject: [PATCH 147/455] mgmt: ec_host_cmd: add @file Doxygen block with group info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a missing @file block so the EC host command header shows up properly in the generated API documentation, attached to its Doxygen group. Assisted-by: Claude:fable-5 Signed-off-by: Benjamin Cabé --- include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h b/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h index 9abd7b91c958..12e50521945d 100644 --- a/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h +++ b/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h @@ -4,6 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** + * @file + * @brief Header file for the embedded controller (EC) host command APIs. + * @ingroup ec_host_cmd_interface + */ + #ifndef ZEPHYR_INCLUDE_MGMT_EC_HOST_CMD_EC_HOST_CMD_H_ #define ZEPHYR_INCLUDE_MGMT_EC_HOST_CMD_EC_HOST_CMD_H_ From bce3104bfd85446c986206f0654db8bd0a91b0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Sat, 1 Aug 2026 06:41:15 +0000 Subject: [PATCH 148/455] mgmt: ec_host_cmd: document backend API struct and context members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the remaining host command context members and adopt the driver backend API Doxygen convention for the backend interface: the backend group becomes a "Driver Backend API" subgroup and the operations structure is annotated with @driver_ops, marking both operations as mandatory. Assisted-by: Claude:fable-5 Signed-off-by: Benjamin Cabé --- include/zephyr/mgmt/ec_host_cmd/backend.h | 11 +++++++---- include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h | 9 ++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/zephyr/mgmt/ec_host_cmd/backend.h b/include/zephyr/mgmt/ec_host_cmd/backend.h index d917a56e0874..30aa6923d994 100644 --- a/include/zephyr/mgmt/ec_host_cmd/backend.h +++ b/include/zephyr/mgmt/ec_host_cmd/backend.h @@ -7,16 +7,14 @@ /** * @file * @brief Public APIs for Host Command backends that respond to host commands - * @ingroup ec_host_cmd_backend + * @ingroup ec_host_cmd_interface_backend */ #ifndef ZEPHYR_INCLUDE_MGMT_EC_HOST_CMD_BACKEND_H_ #define ZEPHYR_INCLUDE_MGMT_EC_HOST_CMD_BACKEND_H_ /** - * @brief Interface to EC Host Command backends - * @defgroup ec_host_cmd_backend Backends - * @ingroup ec_host_cmd_interface + * @def_driverbackendgroup{Host Command,ec_host_cmd_interface} * @{ */ @@ -107,8 +105,13 @@ typedef int (*ec_host_cmd_backend_api_init)(const struct ec_host_cmd_backend *ba */ typedef int (*ec_host_cmd_backend_api_send)(const struct ec_host_cmd_backend *backend); +/** + * @driver_ops{Host Command} + */ struct ec_host_cmd_backend_api { + /** @driver_ops_mandatory @copybrief ec_host_cmd_backend_api_init */ ec_host_cmd_backend_api_init init; + /** @driver_ops_mandatory @copybrief ec_host_cmd_backend_api_send */ ec_host_cmd_backend_api_send send; }; diff --git a/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h b/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h index 12e50521945d..0516e9a3b5f7 100644 --- a/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h +++ b/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h @@ -75,7 +75,9 @@ enum ec_host_cmd_status { /** Can't resend response. */ EC_HOST_CMD_DUP_UNAVAILABLE = 20, - EC_HOST_CMD_MAX = UINT16_MAX /* Force enum to be 16 bits. */ + /** @cond INTERNAL_HIDDEN */ + EC_HOST_CMD_MAX = UINT16_MAX /* Force enum to be 16 bits */ + /** @endcond */ } __packed; /** @@ -127,8 +129,11 @@ typedef enum ec_host_cmd_status (*ec_host_cmd_in_progress_cb_t)(void *user_data) * Host command context structure */ struct ec_host_cmd { + /** Context used to pass data received from the host. */ struct ec_host_cmd_rx_ctx rx_ctx; + /** Buffer used to send a response to the host. */ struct ec_host_cmd_tx_buf tx; + /** Backend used for communication with the host. */ struct ec_host_cmd_backend *backend; /** * The backend gives rx_ready (by calling the ec_host_cmd_send_receive function), @@ -142,7 +147,9 @@ struct ec_host_cmd { * function. */ ec_host_cmd_user_cb_t user_cb; + /** User data passed to @a user_cb. */ void *user_data; + /** Current state of the host command handler. */ enum ec_host_cmd_state state; #ifdef CONFIG_EC_HOST_CMD_DEDICATED_THREAD struct k_thread thread; From 747b57c5377a74ffc5d36ffcb862c53126fdc706 Mon Sep 17 00:00:00 2001 From: Zhaoxiang Jin Date: Mon, 10 Aug 2026 18:48:28 +0800 Subject: [PATCH 149/455] west.yml: update hal nxp to the SDK 26.09.00 pvw1 1. update hal nxp to the SDK 26.09.00 pvw1 2. MCXA SystemInit initializes SRAM ECC by clearing the first 8 KiB of SRAM. Zephyr calls SystemInit after moving MSP to the main stack in that range, so the clear corrupts the active return stack and locks up during boot. Move the ECC initialization to the no-stack early reset hook and prevent SystemInit from repeating it. This preserves cold-boot ECC initialization without clearing an active Zephyr stack. Signed-off-by: Zhaoxiang Jin --- modules/hal_nxp/mcux/CMakeLists.txt | 4 ++++ soc/nxp/mcx/mcxa/Kconfig | 8 ++++++++ soc/nxp/mcx/mcxa/soc.c | 22 ++++++++++++++++++++++ west.yml | 2 +- 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/modules/hal_nxp/mcux/CMakeLists.txt b/modules/hal_nxp/mcux/CMakeLists.txt index 580cfb232916..f155218c9434 100644 --- a/modules/hal_nxp/mcux/CMakeLists.txt +++ b/modules/hal_nxp/mcux/CMakeLists.txt @@ -137,6 +137,10 @@ zephyr_library_compile_definitions_ifdef(CONFIG_NOCACHE_MEMORY __STARTUP_INITIALIZE_NONCACHEDATA ) +zephyr_library_compile_definitions_ifdef(CONFIG_SOC_MCXA_EARLY_SRAM_ECC_INIT + BYPASS_ECC_RAM_INIT + ) + if(CONFIG_HAS_MCUX_CACHE OR CONFIG_HAS_MCUX_XCACHE) zephyr_library_compile_definitions(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) endif() diff --git a/soc/nxp/mcx/mcxa/Kconfig b/soc/nxp/mcx/mcxa/Kconfig index f2a953ad1bf7..e0be4356c22d 100644 --- a/soc/nxp/mcx/mcxa/Kconfig +++ b/soc/nxp/mcx/mcxa/Kconfig @@ -14,12 +14,14 @@ config SOC_FAMILY_MCXA config SOC_SERIES_MCXA1X3 select CPU_CORTEX_M33 + select SOC_MCXA_EARLY_SRAM_ECC_INIT select HAS_PM select WUC if PM select HAS_POWEROFF config SOC_SERIES_MCXA1X6 select CPU_CORTEX_M33 + select SOC_MCXA_EARLY_SRAM_ECC_INIT select CPU_HAS_FPU select ARMV8_M_DSP select HAS_PM @@ -28,6 +30,7 @@ config SOC_SERIES_MCXA1X6 config SOC_SERIES_MCXAXX6 select CPU_CORTEX_M33 + select SOC_MCXA_EARLY_SRAM_ECC_INIT select CPU_HAS_ARM_MPU select CPU_HAS_FPU select ARMV8_M_DSP @@ -37,6 +40,7 @@ config SOC_SERIES_MCXAXX6 config SOC_SERIES_MCXAXX4 select CPU_CORTEX_M33 + select SOC_MCXA_EARLY_SRAM_ECC_INIT select CPU_HAS_ARM_MPU select CPU_HAS_FPU select ARMV8_M_DSP @@ -54,3 +58,7 @@ config SOC_SERIES_MCXAXX7 select HAS_PM select WUC if PM select HAS_POWEROFF + +config SOC_MCXA_EARLY_SRAM_ECC_INIT + bool + select SOC_EARLY_RESET_HOOK diff --git a/soc/nxp/mcx/mcxa/soc.c b/soc/nxp/mcx/mcxa/soc.c index 5e8c6460d28f..bc7e741e25e3 100644 --- a/soc/nxp/mcx/mcxa/soc.c +++ b/soc/nxp/mcx/mcxa/soc.c @@ -23,6 +23,28 @@ #define MCXA_SPC ((SPC_Type *)DT_REG_ADDR(DT_INST(0, nxp_spc))) #endif +#ifdef CONFIG_SOC_MCXA_EARLY_SRAM_ECC_INIT +__attribute__((naked)) void soc_early_reset_hook(void) +{ + __asm__ volatile( + "mov r1, pc\n" + "tst r1, #0x24000000\n" + "bne 2f\n" + "ldr r0, =0x20000000\n" + "ldr r1, =0x20002000\n" + "movs r2, #0\n" + "movs r3, #0\n" + "movs r4, #0\n" + "movs r5, #0\n" + "1:\n" + "stmia r0!, {r2-r5}\n" + "cmp r0, r1\n" + "bcc 1b\n" + "2:\n" + "bx lr\n"); +} +#endif + #ifdef CONFIG_SOC_RESET_HOOK void soc_reset_hook(void) { diff --git a/west.yml b/west.yml index a5dece1843e2..60024bc99122 100644 --- a/west.yml +++ b/west.yml @@ -213,7 +213,7 @@ manifest: groups: - hal - name: hal_nxp - revision: 5218647eea21dee20def3cac103898101bf71bbf + revision: 3a2dfb46f98f0eafa314e05e4cc52ec6174f88bd path: modules/hal/nxp groups: - hal From ac9f351ba13c0116a539dd941002d1a746ef8f44 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Mon, 20 Jul 2026 22:16:24 +0900 Subject: [PATCH 150/455] include: driver: pinctrl: rcar: fix handling of pins without IPSR Use a zero IPSR value when the pin node does not define the property. Signed-off-by: TOKITA Hiroshi --- include/zephyr/drivers/pinctrl/pinctrl_rcar_common.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/zephyr/drivers/pinctrl/pinctrl_rcar_common.h b/include/zephyr/drivers/pinctrl/pinctrl_rcar_common.h index df5e06971e0a..08f3f11f7e4b 100644 --- a/include/zephyr/drivers/pinctrl/pinctrl_rcar_common.h +++ b/include/zephyr/drivers/pinctrl/pinctrl_rcar_common.h @@ -43,8 +43,9 @@ typedef struct pinctrl_soc_pin { uint8_t voltage; } pinctrl_soc_pin_t; -#define RCAR_IPSR(node_id) DT_PROP_BY_IDX(node_id, pin, 1) #define RCAR_HAS_IPSR(node_id) DT_PROP_HAS_IDX(node_id, pin, 1) +#define RCAR_IPSR(node_id) COND_CODE_1(RCAR_HAS_IPSR(node_id), (DT_PROP_BY_IDX(node_id, pin, 1)), \ + (0)) /* Offsets are defined in dt-bindings pinctrl-rcar-common.h */ #define RCAR_PIN_FUNC(node_id) \ @@ -70,7 +71,7 @@ typedef struct pinctrl_soc_pin { { \ .pin = DT_PROP_BY_IDX(node_id, pin, 0), \ .func = COND_CODE_1(RCAR_HAS_IPSR(node_id), \ - (RCAR_PIN_FUNC(node_id)), {0}), \ + (RCAR_PIN_FUNC(node_id)), ({0})), \ .flags = RCAR_PIN_FLAGS(node_id), \ .drive_strength = \ COND_CODE_1(DT_NODE_HAS_PROP(node_id, drive_strength), \ From 5a779d1b3b07b4bafdc2a9f1aeb7713fcd737771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 17:29:00 +0000 Subject: [PATCH 151/455] drivers: sensor: adxl362: populate accel_odr in FIFO header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adxl362_process_fifo_samples_cb() never set the accel_odr field of the FIFO header, leaving it with stale RTIO pool contents that the decoder uses to index accel_period_ns[], yielding wrong sample periods or an out-of-bounds table read. Set it from the cached output data rate. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/adi/adxl362/adxl362_stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/sensor/adi/adxl362/adxl362_stream.c b/drivers/sensor/adi/adxl362/adxl362_stream.c index 03042a65ce51..badf5aa366a3 100644 --- a/drivers/sensor/adi/adxl362/adxl362_stream.c +++ b/drivers/sensor/adi/adxl362/adxl362_stream.c @@ -199,6 +199,7 @@ static void adxl362_process_fifo_samples_cb(struct rtio *r, const struct rtio_sq hdr->timestamp = data->timestamp; hdr->int_status = data->status; hdr->selected_range = data->selected_range; + hdr->accel_odr = data->accel_odr; hdr->has_tmp = data->en_temp_read; uint32_t buf_avail = buf_len; From f35f474b81c325d46e10b40be99452a0be88247f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 17:31:04 +0000 Subject: [PATCH 152/455] drivers: sensor: adxl362: fix streamed die temperature resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The streaming decoder divided the raw temperature code by a rounded, decoder-local 15 LSB/degC before applying the q31 scale, quantising the result to whole degrees and disagreeing with adxl362_temp_convert(). Scale with the shared ADXL362_TEMP_MC_PER_LSB first and divide last, so both paths report the same 0.065 degC resolution. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/adi/adxl362/adxl362_decoder.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/sensor/adi/adxl362/adxl362_decoder.c b/drivers/sensor/adi/adxl362/adxl362_decoder.c index de93a49d214d..427b2770b4ad 100644 --- a/drivers/sensor/adi/adxl362/adxl362_decoder.c +++ b/drivers/sensor/adi/adxl362/adxl362_decoder.c @@ -11,7 +11,6 @@ /* (2^31 / 2^8(shift) */ #define ADXL362_TEMP_QSCALE 8388608 -#define ADXL362_TEMP_LSB_PER_C 15 #define ADXL362_COMPLEMENT 0xf000 @@ -49,8 +48,10 @@ static inline void adxl362_temp_convert_q31(q31_t *out, int16_t data_in) data_in |= ADXL362_COMPLEMENT; } - *out = ((data_in - ADXL362_TEMP_BIAS_LSB) / ADXL362_TEMP_LSB_PER_C - + ADXL362_TEMP_BIAS_TEST_CONDITION) * ADXL362_TEMP_QSCALE; + int32_t milli_c = (data_in - ADXL362_TEMP_BIAS_LSB) * ADXL362_TEMP_MC_PER_LSB + + (ADXL362_TEMP_BIAS_TEST_CONDITION * 1000); + + *out = (q31_t)(((int64_t)milli_c * ADXL362_TEMP_QSCALE) / 1000); } static inline void adxl362_accel_convert_q31(q31_t *out, int16_t data_in, int32_t range) From 6c2b92a4e04afd728621b3ec2a24b78e67d1cfbb Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Tue, 11 Aug 2026 15:25:49 +0700 Subject: [PATCH 153/455] xen: arm64: allow Xen support on ARMv9-A ARMv9-A CPUs such as Cortex-A720 select ARMV9_A instead of ARMV8_A. Allow Xen support to be enabled on these platforms. Signed-off-by: Nam Nguyen --- arch/arm64/core/xen/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/core/xen/Kconfig b/arch/arm64/core/xen/Kconfig index 26a86899922c..0b6412bcb56d 100644 --- a/arch/arm64/core/xen/Kconfig +++ b/arch/arm64/core/xen/Kconfig @@ -6,7 +6,7 @@ config XEN bool default y - depends on ARMV8_A + depends on ARMV8_A || ARMV9_A depends on DT_HAS_XEN_XEN_ENABLED select GNU_C_EXTENSIONS help From d75f773130d8c6fc55905d0f5bc390c2a54ebeb2 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Tue, 11 Aug 2026 17:29:31 +0700 Subject: [PATCH 154/455] snippets: xen-dom0: add R-Car X5H support Add the board configuration and devicetree overlay required to boot Zephyr as Xen Dom0 on the R-Car X5H Cortex-A720. Configure Xen memory regions, Dom0 RAM, and event-channel PPI. Disable the on-board HSCIF0, SCMI firmware node and MFIS mailbox. On R-Car X5H, the actual SCMI server runs on a separate SCP core, and Dom0 can only reach it through the MFIS mailbox. Signed-off-by: Nam Nguyen --- .../rcar_ironhide_x5h_r8a78000_a720.conf | 10 +++++ .../rcar_ironhide_x5h_r8a78000_a720.overlay | 39 +++++++++++++++++++ snippets/xen-dom0/snippet.yml | 4 ++ 3 files changed, 53 insertions(+) create mode 100644 snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.conf create mode 100644 snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.overlay diff --git a/snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.conf b/snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.conf new file mode 100644 index 000000000000..114354858175 --- /dev/null +++ b/snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.conf @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Renesas Electronics Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_CLOCK_CONTROL=n +CONFIG_MBOX=n +CONFIG_ARM_SCMI=n + +# Xen starts Dom0 at EL1 Non-secure; configure GIC interrupts as Group 1. +CONFIG_ARMV8_A_NS=y diff --git a/snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.overlay b/snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.overlay new file mode 100644 index 000000000000..c821edb2be23 --- /dev/null +++ b/snippets/xen-dom0/boards/rcar_ironhide_x5h_r8a78000_a720.overlay @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &ram; +/delete-node/ &hscif0; +/delete-node/ &scmi_res0; + +#include + +&mfis { + status = "disabled"; +}; + +/ { + firmware { + /delete-node/ scmi; + }; + + hypervisor: hypervisor@58200000 { + compatible = "xen,xen"; + reg = <0x0 0x58200000 0x0 0x40000 + 0x0 0x39a00000 0x0 0x6600000>; + interrupts = ; + interrupt-parent = <&gic>; + status = "okay"; + }; + + /* + * Xen Dom0 BANK[0] 0x40000000-0x48000000 (128M); mapped 96M only. + * DTB is at 0x47e00000, outside this node. + */ + ram: memory@40000000 { + device_type = "mmio-sram"; + reg = <0x0 0x40000000 0x0 DT_SIZE_M(96)>; + }; +}; diff --git a/snippets/xen-dom0/snippet.yml b/snippets/xen-dom0/snippet.yml index d61a35ccf595..f30448fa0dd2 100644 --- a/snippets/xen-dom0/snippet.yml +++ b/snippets/xen-dom0/snippet.yml @@ -21,6 +21,10 @@ boards: rcar_spider_s4/r8a779f0/a55: append: EXTRA_DTC_OVERLAY_FILE: boards/rcar_spider_s4_r8a779f0_a55.overlay + rcar_ironhide_x5h/r8a78000/a720: + append: + EXTRA_DTC_OVERLAY_FILE: boards/rcar_ironhide_x5h_r8a78000_a720.overlay + EXTRA_CONF_FILE: boards/rcar_ironhide_x5h_r8a78000_a720.conf rpi_5/bcm2712: append: EXTRA_DTC_OVERLAY_FILE: boards/rpi_5.overlay From 20937051d6a5c8da0c12c1b7204c1f45b433889d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 12 Aug 2026 16:50:57 +0300 Subject: [PATCH 155/455] Bluetooth: Host: Monitor: add interrupt-driven UART output The UART monitor backend sends every byte with uart_poll_out(). At common UART rates this blocks the Bluetooth host while HCI traffic and logs are serialized. Add an opt-in interrupt-driven mode that buffers complete monitor records and drains them from the UART interrupt handler. Record fragments are copied in with ring_buf_put_ptr() and published with a single ring_buf_commit(), so a record is either fully present in the buffer or absent. This matters on panic: the panic handler flushes only complete records before switching to polling, keeping the stream parseable. Only the header-inline zero-copy ring buffer API is used, so the option does not need to select RING_BUFFER. A record that does not fit is dropped whole and reported through the existing drop counters. Counts already consumed into a dropped record's header are restored, keeping the accounting exact across consecutive drops, and counts exceeding the 8-bit header field carry over to the next record. Both kinds of loss previously also affected the RTT backend, which shares this code. Polling output remains the default and the panic-mode fallback, since interrupts may no longer run during panic handling. It is also the runtime fallback when the monitor UART driver lacks interrupt support, since SERIAL_SUPPORT_INTERRUPT only guarantees that some driver has it. The option depends on !LOG_MODE_MINIMAL because minimal logging has no panic hook to flush buffered output. Measured on nrf52dk/nrf52832 and xg24_rb4186c with a peripheral flooding GATT notifications and the monitor sharing the 115200 baud console UART: polling caps throughput at 5.9-6.6 KB/s, slows a paced 25 notifications/s workload to 15/s and blocks the producing context ~6 ms per large record (10-line printk burst: 58-65 ms). The interrupt-driven mode restores radio-limited throughput (41 KB/s) and the nominal paced rate, with the same burst at 0.8-1.3 ms. The trade-off is load shedding: under sustained overload ~87% of full-size ACL records are dropped (and accounted), while small records such as Number of Completed Packets events keep flowing. Unit tests cover contiguous and wrapped records, failed reservations, zero-length fragments and exact-fit records; an integration test runs the real producer/ISR path against the emulated serial-test UART, covering drain, buffer-full drops with accounting, and wrap-around consistency. The Bluetooth init test builds the interrupt-driven path with the deferred log backend, since tests default to minimal logging. Builds also pass with polling UART and RTT. Assisted-by: Claude:claude-fable-5 Signed-off-by: Johan Hedberg --- .../bluetooth/bluetooth-tools.rst | 8 + subsys/bluetooth/common/Kconfig | 31 ++ subsys/bluetooth/host/monitor.c | 284 +++++++++++++--- subsys/bluetooth/host/monitor_buffer.h | 104 ++++++ .../host/monitor_buffer/CMakeLists.txt | 13 + tests/bluetooth/host/monitor_buffer/prj.conf | 3 + .../bluetooth/host/monitor_buffer/src/main.c | 178 ++++++++++ .../bluetooth/host/monitor_buffer/tests.yaml | 8 + tests/bluetooth/init/prj_20.conf | 4 + tests/bluetooth/monitor/CMakeLists.txt | 8 + tests/bluetooth/monitor/app.overlay | 26 ++ tests/bluetooth/monitor/prj.conf | 16 + tests/bluetooth/monitor/src/main.c | 312 ++++++++++++++++++ tests/bluetooth/monitor/tests.yaml | 11 + 14 files changed, 958 insertions(+), 48 deletions(-) create mode 100644 subsys/bluetooth/host/monitor_buffer.h create mode 100644 tests/bluetooth/host/monitor_buffer/CMakeLists.txt create mode 100644 tests/bluetooth/host/monitor_buffer/prj.conf create mode 100644 tests/bluetooth/host/monitor_buffer/src/main.c create mode 100644 tests/bluetooth/host/monitor_buffer/tests.yaml create mode 100644 tests/bluetooth/monitor/CMakeLists.txt create mode 100644 tests/bluetooth/monitor/app.overlay create mode 100644 tests/bluetooth/monitor/prj.conf create mode 100644 tests/bluetooth/monitor/src/main.c create mode 100644 tests/bluetooth/monitor/tests.yaml diff --git a/doc/services/connectivity/bluetooth/bluetooth-tools.rst b/doc/services/connectivity/bluetooth/bluetooth-tools.rst index 59112aff67cf..f6a7579eff64 100644 --- a/doc/services/connectivity/bluetooth/bluetooth-tools.rst +++ b/doc/services/connectivity/bluetooth/bluetooth-tools.rst @@ -225,6 +225,14 @@ application: the system console. E.g. for ``printk`` and the :kconfig:option:`boot banner ` +Optionally, on boards whose monitor UART driver supports the interrupt API, +setting :kconfig:option:`CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN` queues +complete monitor records and transmits them from the UART interrupt handler +instead of blocking while each byte is transmitted. Records that do not fit in +the buffer are dropped and reported in the next monitor record. The buffer +size can be adjusted with +:kconfig:option:`CONFIG_BT_DEBUG_MONITOR_UART_BUFFER_SIZE`. + To decode the binary protocol that will now be sent to the console UART you need to use the btmon tool from :ref:`BlueZ `: diff --git a/subsys/bluetooth/common/Kconfig b/subsys/bluetooth/common/Kconfig index f7182b21ee8e..a9c9eefa194f 100644 --- a/subsys/bluetooth/common/Kconfig +++ b/subsys/bluetooth/common/Kconfig @@ -2,6 +2,7 @@ # Copyright (c) 2017 Nordic Semiconductor ASA # Copyright (c) 2016 Intel Corporation +# Copyright (c) 2026 Silicon Laboratories Inc. # SPDX-License-Identifier: Apache-2.0 menu "Bluetooth buffer configuration" @@ -319,6 +320,36 @@ config BT_DEBUG_MONITOR_UART UART_CONSOLE needs to be disabled (in which case printk/printf will get encoded into the monitor protocol). +if BT_DEBUG_MONITOR_UART + +config BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN + bool "Interrupt-driven UART monitor output" + depends on SERIAL_SUPPORT_INTERRUPT + # Minimal logging has no panic hook, so buffered records could not be + # flushed during a fatal error and crash output would be lost. + depends on !LOG_MODE_MINIMAL + select UART_INTERRUPT_DRIVEN + help + Buffer complete monitor records and transmit them from the UART + interrupt handler. This avoids blocking the Bluetooth host while + waiting for each byte to be transmitted. Records which cannot fit + in the buffer are dropped and reported in the next monitor record. + +config BT_DEBUG_MONITOR_UART_BUFFER_SIZE + int "UART monitor transmit buffer size" + default 1024 + # The upper limit matches RING_BUFFER_MAX_SIZE without RING_BUFFER_LARGE + range 64 32767 + depends on BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN + help + Size in bytes of the ring buffer used for interrupt-driven monitor + output. A record larger than the buffer can never be sent and is + always dropped, so make sure the buffer can hold the largest + monitor record: the largest HCI packet plus up to 25 bytes of + monitor header. + +endif # BT_DEBUG_MONITOR_UART + config BT_DEBUG_MONITOR_RTT bool "Monitor protocol over RTT" depends on USE_SEGGER_RTT diff --git a/subsys/bluetooth/host/monitor.c b/subsys/bluetooth/host/monitor.c index 426bdf9c7283..fd4067dc3c5c 100644 --- a/subsys/bluetooth/host/monitor.c +++ b/subsys/bluetooth/host/monitor.c @@ -4,6 +4,7 @@ /* * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2026 Silicon Laboratories Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -36,6 +37,7 @@ #include #include "monitor.h" +#include "monitor_buffer.h" /* This is the same default priority as for other console handlers, * except that we're not exporting it as a Kconfig variable until a @@ -111,49 +113,36 @@ static bool panic_mode; #define RTT_BUFFER_NAME CONFIG_BT_DEBUG_MONITOR_RTT_BUFFER_NAME #define RTT_BUF_SIZE CONFIG_BT_DEBUG_MONITOR_RTT_BUFFER_SIZE -static void monitor_send(const void *data, size_t len) +static bool monitor_send(const struct bt_monitor_data *frags, size_t count) { static uint8_t rtt_buf[RTT_BUF_SIZE]; - static size_t rtt_buf_offset; - struct bt_monitor_hdr *hdr; + size_t total = 0; unsigned int cnt = 0; - bool drop; - /* Drop any packet which cannot fit the buffer */ - drop = rtt_buf_offset + len > sizeof(rtt_buf); - if (!drop) { - (void)memcpy(rtt_buf + rtt_buf_offset, data, len); - } - - rtt_buf_offset += len; - - /* Check if the packet is complete */ - hdr = (struct bt_monitor_hdr *)rtt_buf; - if (rtt_buf_offset < sizeof(hdr->data_len) + hdr->data_len) { - return; - } + for (size_t i = 0; i < count; i++) { + /* Zero-length fragments may carry a NULL data pointer */ + if (frags[i].len == 0) { + continue; + } - if (!drop) { - if (panic_mode) { - cnt = SEGGER_RTT_WriteNoLock(CONFIG_BT_DEBUG_MONITOR_RTT_BUFFER, - rtt_buf, rtt_buf_offset); - } else { - cnt = SEGGER_RTT_Write(CONFIG_BT_DEBUG_MONITOR_RTT_BUFFER, - rtt_buf, rtt_buf_offset); + /* total only grows after passing this check, so it never + * exceeds sizeof(rtt_buf) and the subtraction cannot underflow. + */ + if (frags[i].len > sizeof(rtt_buf) - total) { + return false; } + + (void)memcpy(rtt_buf + total, frags[i].data, frags[i].len); + total += frags[i].len; } - if (!cnt) { - drop_add(hdr->opcode); + if (panic_mode) { + cnt = SEGGER_RTT_WriteNoLock(CONFIG_BT_DEBUG_MONITOR_RTT_BUFFER, rtt_buf, total); + } else { + cnt = SEGGER_RTT_Write(CONFIG_BT_DEBUG_MONITOR_RTT_BUFFER, rtt_buf, total); } - /* Prepare for the next packet */ - rtt_buf_offset = 0; -} - -static void poll_out(char c) -{ - monitor_send(&c, sizeof(c)); + return cnt != 0; } #elif defined(CONFIG_BT_DEBUG_MONITOR_UART) static const struct device *const monitor_dev = @@ -167,19 +156,106 @@ static const struct device *const monitor_dev = #error "BT_DEBUG_MONITOR_UART enabled but no UART specified" #endif -static void poll_out(char c) +static void monitor_poll_send(const struct bt_monitor_data *frags, size_t count) { - uart_poll_out(monitor_dev, c); + for (size_t i = 0; i < count; i++) { + const uint8_t *buf = frags[i].data; + + for (size_t j = 0; j < frags[i].len; j++) { + uart_poll_out(monitor_dev, buf[j]); + } + } } -static void monitor_send(const void *data, size_t len) +#if defined(CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN) +/* Single producer (serialized by BT_LOG_BUSY) and single consumer (the UART + * ISR), matching the ring buffer's lock-free SPSC contract. The memory + * ordering that contract additionally requires on SMP systems is provided by + * the fences in the bt_monitor_ring_buf_* helpers. On panic the flush in + * monitor_log_panic() becomes a second consumer; monitor_tx_lock serializes + * it with the ISR, while producers stay lock-free. + */ +static uint8_t monitor_tx_data[CONFIG_BT_DEBUG_MONITOR_UART_BUFFER_SIZE]; +static struct ring_buf monitor_tx_buf = RING_BUF_INIT(monitor_tx_data, sizeof(monitor_tx_data)); +static atomic_t monitor_tx_busy; +static struct k_spinlock monitor_tx_lock; +/* Set on panic or when the UART driver lacks interrupt support */ +static bool poll_mode; + +static void monitor_uart_tx(void) { - const uint8_t *buf = data; + uint8_t *data; + uint32_t len; + int sent; + + len = bt_monitor_ring_buf_get_ptr(&monitor_tx_buf, &data); + if (len > 0) { + sent = uart_fifo_fill(monitor_dev, data, len); + if (sent > 0) { + bt_monitor_ring_buf_consume(&monitor_tx_buf, sent); + return; + } - while (len--) { - poll_out(*buf++); + /* TX ready but nothing accepted: disable TX so a persistent + * driver error cannot cause an interrupt storm. The next + * committed record re-enables TX and retries. Unlike the + * empty-buffer path below, deliberately no re-check here: + * the buffer is non-empty at this point, so re-enabling + * would defeat the backoff. + */ + uart_irq_tx_disable(monitor_dev); + atomic_set(&monitor_tx_busy, 0); + return; } + + uart_irq_tx_disable(monitor_dev); + atomic_set(&monitor_tx_busy, 0); + + /* Close the race with a producer that committed while TX was disabled. */ + if (!ring_buf_is_empty(&monitor_tx_buf) && atomic_cas(&monitor_tx_busy, 0, 1)) { + uart_irq_tx_enable(monitor_dev); + } +} + +static void monitor_uart_isr(const struct device *dev, void *user_data) +{ + ARG_UNUSED(user_data); + + k_spinlock_key_t key = k_spin_lock(&monitor_tx_lock); + + uart_irq_update(dev); + if (uart_irq_tx_ready(dev) > 0) { + monitor_uart_tx(); + } + + k_spin_unlock(&monitor_tx_lock, key); +} + +static bool monitor_send(const struct bt_monitor_data *frags, size_t count) +{ + if (poll_mode) { + monitor_poll_send(frags, count); + return true; + } + + if (!bt_monitor_ring_buf_put(&monitor_tx_buf, frags, count)) { + return false; + } + + if (atomic_cas(&monitor_tx_busy, 0, 1)) { + uart_irq_tx_enable(monitor_dev); + } + + return true; } +#else +static bool monitor_send(const struct bt_monitor_data *frags, size_t count) +{ + monitor_poll_send(frags, count); + + return true; +} +#endif /* CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN */ #endif /* CONFIG_BT_DEBUG_MONITOR_UART */ static void encode_drops(struct bt_monitor_hdr *hdr, uint8_t type, @@ -191,6 +267,51 @@ static void encode_drops(struct bt_monitor_hdr *hdr, uint8_t type, if (count) { hdr->ext[hdr->hdr_len++] = type; hdr->ext[hdr->hdr_len++] = MIN(count, 255); + if (count > 255) { + /* Keep the surplus for the next record */ + atomic_add(val, count - 255); + } + } +} + +static atomic_t *drop_counter(uint8_t type) +{ + switch (type) { + case BT_MONITOR_COMMAND_DROPS: + return &drops.cmd; + case BT_MONITOR_EVENT_DROPS: + return &drops.evt; + case BT_MONITOR_ACL_TX_DROPS: + return &drops.acl_tx; + case BT_MONITOR_ACL_RX_DROPS: + return &drops.acl_rx; +#if defined(CONFIG_BT_CLASSIC) + case BT_MONITOR_SCO_TX_DROPS: + return &drops.sco_tx; + case BT_MONITOR_SCO_RX_DROPS: + return &drops.sco_rx; +#endif + case BT_MONITOR_OTHER_DROPS: + return &drops.other; + default: + return NULL; + } +} + +/* Restore drop counts that encode_hdr() consumed into a record which then + * failed to be sent: without this, every record dropped back-to-back would + * destroy the counts accumulated by its predecessors, understating drops + * under sustained overload. + */ +static void restore_drops(const struct bt_monitor_hdr *hdr) +{ + /* The timestamp is always encoded first, drop pairs follow */ + for (uint8_t i = sizeof(struct bt_monitor_ts32); i + 1U < hdr->hdr_len; i += 2U) { + atomic_t *counter = drop_counter(hdr->ext[i]); + + if (counter != NULL) { + atomic_add(counter, hdr->ext[i + 1]); + } } } @@ -247,6 +368,10 @@ static inline void encode_hdr(struct bt_monitor_hdr *hdr, log_timestamp_t timest void bt_monitor_send(uint16_t opcode, const void *data, size_t len) { struct bt_monitor_hdr hdr; + struct bt_monitor_data frags[] = { + { &hdr, 0 }, /* Length known only after encode_hdr() */ + { data, len }, + }; if (atomic_test_and_set_bit(&flags, BT_LOG_BUSY)) { drop_add(opcode); @@ -254,9 +379,12 @@ void bt_monitor_send(uint16_t opcode, const void *data, size_t len) } encode_hdr(&hdr, monitor_ts_get(), opcode, len); + frags[0].len = BT_MONITOR_BASE_HDR_LEN + hdr.hdr_len; - monitor_send(&hdr, BT_MONITOR_BASE_HDR_LEN + hdr.hdr_len); - monitor_send(data, len); + if (!monitor_send(frags, ARRAY_SIZE(frags))) { + restore_drops(&hdr); + drop_add(opcode); + } atomic_clear_bit(&flags, BT_LOG_BUSY); } @@ -356,6 +484,13 @@ static void monitor_log_process(const struct log_backend *const backend, struct monitor_log_ctx ctx; struct bt_monitor_hdr hdr; static const char id[] = "bt"; + struct bt_monitor_data frags[] = { + { &hdr, 0 }, /* Length known only after encode_hdr() */ + { &user_log, sizeof(user_log) }, + { id, sizeof(id) }, + { ctx.msg, 0 }, /* Length known only after log processing */ + { "", 1 }, /* Terminating NUL for the message string */ + }; log_output_ctx_set(&monitor_log_output, &ctx); @@ -374,14 +509,13 @@ static void monitor_log_process(const struct log_backend *const backend, user_log.priority = monitor_priority_get(log_msg_get_level(&msg->log)); user_log.ident_len = sizeof(id); + frags[0].len = BT_MONITOR_BASE_HDR_LEN + hdr.hdr_len; + frags[3].len = ctx.total_len; - monitor_send(&hdr, BT_MONITOR_BASE_HDR_LEN + hdr.hdr_len); - monitor_send(&user_log, sizeof(user_log)); - monitor_send(id, sizeof(id)); - monitor_send(ctx.msg, ctx.total_len); - - /* Terminate the string with null */ - poll_out('\0'); + if (!monitor_send(frags, ARRAY_SIZE(frags))) { + restore_drops(&hdr); + drop_add(BT_MONITOR_USER_LOGGING); + } atomic_clear_bit(&flags, BT_LOG_BUSY); } @@ -390,6 +524,49 @@ static void monitor_log_panic(const struct log_backend *const backend) { #if defined(CONFIG_BT_DEBUG_MONITOR_RTT) panic_mode = true; +#elif defined(CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN) + k_spinlock_key_t key; + bool locked = false; + uint8_t *data; + uint32_t len; + + poll_mode = true; + + /* Setting monitor_tx_busy prevents a producer that read poll_mode + * before it was set above from re-enabling TX interrupts: from here + * on the buffer is drained only by the flush below. A record such a + * producer still commits stays buffered and untransmitted, which is + * accepted in panic context. + */ + atomic_set(&monitor_tx_busy, 1); + uart_irq_tx_disable(monitor_dev); + + /* Serialize with a UART ISR that may be consuming on another CPU. + * Bounded, because if the panic originated inside that ISR the lock + * would never be released: then flush unserialized rather than hang + * the panic path. + */ + for (int i = 0; i < 100; i++) { + if (k_spin_trylock(&monitor_tx_lock, &key) == 0) { + locked = true; + break; + } + + k_busy_wait(10); + } + + len = bt_monitor_ring_buf_get_ptr(&monitor_tx_buf, &data); + while (len > 0) { + struct bt_monitor_data frag = { data, len }; + + monitor_poll_send(&frag, 1); + bt_monitor_ring_buf_consume(&monitor_tx_buf, len); + len = bt_monitor_ring_buf_get_ptr(&monitor_tx_buf, &data); + } + + if (locked) { + k_spin_unlock(&monitor_tx_lock, key); + } #endif } @@ -419,6 +596,17 @@ static int bt_monitor_init(void) #elif defined(CONFIG_BT_DEBUG_MONITOR_UART) __ASSERT_NO_MSG(device_is_ready(monitor_dev)); +#if defined(CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN) + /* SERIAL_SUPPORT_INTERRUPT only guarantees that some UART driver + * supports the interrupt API, not necessarily the monitor UART's. + * Fall back to polling instead of failing, so that a misconfigured + * board still produces monitor output. + */ + if (uart_irq_callback_user_data_set(monitor_dev, monitor_uart_isr, NULL) != 0) { + poll_mode = true; + } +#endif /* CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN */ + #if defined(CONFIG_UART_INTERRUPT_DRIVEN) uart_irq_rx_disable(monitor_dev); uart_irq_tx_disable(monitor_dev); diff --git a/subsys/bluetooth/host/monitor_buffer.h b/subsys/bluetooth/host/monitor_buffer.h new file mode 100644 index 000000000000..1d4e2ce665bc --- /dev/null +++ b/subsys/bluetooth/host/monitor_buffer.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SUBSYS_BLUETOOTH_HOST_MONITOR_BUFFER_H_ +#define ZEPHYR_SUBSYS_BLUETOOTH_HOST_MONITOR_BUFFER_H_ + +#include +#include +#include +#include + +#include +#include +#include + +struct bt_monitor_data { + const void *data; + size_t len; +}; + +/* Header-only so that the unit test in tests/bluetooth/host/monitor_buffer + * can build this function without pulling in monitor.c and its dependencies. + * monitor.c is the only in-tree includer, so the generated code is the same + * as for a file-local static function. + */ +static inline bool bt_monitor_ring_buf_put(struct ring_buf *buf, + const struct bt_monitor_data *frags, size_t count) +{ + size_t space = ring_buf_space_get(buf); + size_t total = 0; + + /* total only grows after passing the check, so total <= space is + * invariant: the subtraction cannot underflow, the sum cannot + * overflow, and offset/remaining in the copy loop below stay bounded + * by the buffer size. + */ + for (size_t i = 0; i < count; i++) { + if (frags[i].len > space - total) { + return false; + } + + total += frags[i].len; + } + + /* Pairs with bt_monitor_ring_buf_consume(): space observed above may + * have been freed by a consumer on another CPU, whose data reads must + * complete before the writes below reuse it. + */ + barrier_dmem_fence_full(); + + /* The space check guarantees that the writes below always fit, and + * the single commit makes the complete record visible to the + * consumer at once. + */ + for (size_t i = 0, offset = 0; i < count; i++) { + const uint8_t *src = frags[i].data; + size_t remaining = frags[i].len; + + while (remaining > 0) { + uint8_t *dst; + size_t len; + + len = MIN(remaining, ring_buf_put_ptr(buf, &dst, offset)); + (void)memcpy(dst, src, len); + offset += len; + src += len; + remaining -= len; + } + } + + /* Make the record bytes visible before the commit publishes them + * (pairs with bt_monitor_ring_buf_get_ptr()). + */ + barrier_dmem_fence_full(); + ring_buf_commit(buf, total); + + return true; +} + +/* Consumer-side wrappers adding the memory ordering that the ring buffer's + * lock-free SPSC contract leaves to its users on SMP systems. The producer + * side is handled in bt_monitor_ring_buf_put(). + */ +static inline uint32_t bt_monitor_ring_buf_get_ptr(struct ring_buf *buf, uint8_t **data) +{ + uint32_t len = ring_buf_get_ptr(buf, data, 0); + + /* Order the index load against the data reads that follow */ + barrier_dmem_fence_full(); + + return len; +} + +static inline void bt_monitor_ring_buf_consume(struct ring_buf *buf, uint32_t size) +{ + /* Order the data reads against the space becoming reusable */ + barrier_dmem_fence_full(); + ring_buf_consume(buf, size); +} + +#endif /* ZEPHYR_SUBSYS_BLUETOOTH_HOST_MONITOR_BUFFER_H_ */ diff --git a/tests/bluetooth/host/monitor_buffer/CMakeLists.txt b/tests/bluetooth/host/monitor_buffer/CMakeLists.txt new file mode 100644 index 000000000000..45b141b4451a --- /dev/null +++ b/tests/bluetooth/host/monitor_buffer/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) + +find_package(Zephyr COMPONENTS unittest HINTS $ENV{ZEPHYR_BASE}) + +project(bt_monitor_buffer) + +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host host_mocks) + +target_include_directories(testbinary PRIVATE ${ZEPHYR_BASE}/subsys/bluetooth/host) +target_sources(testbinary PRIVATE src/main.c ${ZEPHYR_BASE}/lib/utils/ring_buffer.c) +target_link_libraries(testbinary PRIVATE host_mocks) diff --git a/tests/bluetooth/host/monitor_buffer/prj.conf b/tests/bluetooth/host/monitor_buffer/prj.conf new file mode 100644 index 000000000000..5e75373f01dd --- /dev/null +++ b/tests/bluetooth/host/monitor_buffer/prj.conf @@ -0,0 +1,3 @@ +CONFIG_ZTEST=y +CONFIG_ASSERT=y +CONFIG_ASSERT_LEVEL=2 diff --git a/tests/bluetooth/host/monitor_buffer/src/main.c b/tests/bluetooth/host/monitor_buffer/src/main.c new file mode 100644 index 000000000000..3fcf6d40eba1 --- /dev/null +++ b/tests/bluetooth/host/monitor_buffer/src/main.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +#include "monitor_buffer.h" + +DEFINE_FFF_GLOBALS; + +ZTEST_SUITE(bt_monitor_buffer, NULL, NULL, NULL, NULL, NULL); + +static void ring_put(struct ring_buf *buf, const uint8_t *data, size_t len) +{ + zassert_equal(ring_buf_put(buf, data, len), len); +} + +static void ring_get(struct ring_buf *buf, uint8_t *data, size_t len) +{ + zassert_equal(ring_buf_get(buf, data, len), len); +} + +ZTEST(bt_monitor_buffer, test_put_fragments) +{ + uint8_t storage[16]; + struct ring_buf buf; + static const uint8_t header[] = { 0x01, 0x02, 0x03 }; + static const uint8_t payload[] = { 0x04, 0x05, 0x06, 0x07 }; + static const uint8_t expected[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { payload, sizeof(payload) }, + }; + uint8_t output[sizeof(expected)]; + + ring_buf_init(&buf, sizeof(storage), storage); + + zassert_true(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + zassert_equal(ring_buf_size_get(&buf), sizeof(expected)); + ring_get(&buf, output, sizeof(output)); + zassert_mem_equal(output, expected, sizeof(expected)); +} + +ZTEST(bt_monitor_buffer, test_put_fragments_across_wrap) +{ + uint8_t storage[16]; + struct ring_buf buf; + static const uint8_t initial[] = { 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09 }; + static const uint8_t header[] = { 0x10, 0x11, 0x12, 0x13 }; + static const uint8_t payload[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25 }; + static const uint8_t expected[] = { 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25 }; + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { payload, sizeof(payload) }, + }; + uint8_t output[sizeof(expected)]; + uint8_t discarded[8]; + + ring_buf_init(&buf, sizeof(storage), storage); + ring_put(&buf, initial, sizeof(initial)); + ring_get(&buf, discarded, sizeof(discarded)); + + zassert_true(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + ring_get(&buf, output, sizeof(output)); + zassert_mem_equal(output, expected, sizeof(expected)); +} + +ZTEST(bt_monitor_buffer, test_insufficient_space_does_not_publish_partial_record) +{ + uint8_t storage[8]; + struct ring_buf buf; + static const uint8_t initial[] = { 0xaa, 0xbb, 0xcc }; + static const uint8_t header[] = { 0x01, 0x02, 0x03 }; + static const uint8_t payload[] = { 0x04, 0x05, 0x06 }; + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { payload, sizeof(payload) }, + }; + uint8_t output[sizeof(initial)]; + + ring_buf_init(&buf, sizeof(storage), storage); + ring_put(&buf, initial, sizeof(initial)); + + zassert_false(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + zassert_equal(ring_buf_size_get(&buf), sizeof(initial)); + ring_get(&buf, output, sizeof(output)); + zassert_mem_equal(output, initial, sizeof(initial)); +} + +ZTEST(bt_monitor_buffer, test_zero_length_fragment_with_null_data) +{ + uint8_t storage[8]; + struct ring_buf buf; + static const uint8_t header[] = { 0x01, 0x02 }; + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { NULL, 0 }, + }; + uint8_t output[sizeof(header)]; + + ring_buf_init(&buf, sizeof(storage), storage); + + zassert_true(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + zassert_equal(ring_buf_size_get(&buf), sizeof(header)); + ring_get(&buf, output, sizeof(output)); + zassert_mem_equal(output, header, sizeof(header)); +} + +ZTEST(bt_monitor_buffer, test_size_max_fragment_is_rejected) +{ + uint8_t storage[8]; + struct ring_buf buf; + static const uint8_t header[] = { 0x01, 0x02 }; + /* A SIZE_MAX length would wrap the fragment size sum if it were + * computed before being checked; the length must be rejected without + * the data pointer ever being dereferenced. + */ + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { header, SIZE_MAX }, + }; + + ring_buf_init(&buf, sizeof(storage), storage); + + zassert_false(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + zassert_true(ring_buf_is_empty(&buf)); +} + +ZTEST(bt_monitor_buffer, test_fragment_larger_than_remaining_space) +{ + uint8_t storage[8]; + struct ring_buf buf; + static const uint8_t header[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + static const uint8_t payload[] = { 0x06, 0x07, 0x08, 0x09 }; + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { payload, sizeof(payload) }, + }; + + ring_buf_init(&buf, sizeof(storage), storage); + + /* The first fragment fits on its own but the second exceeds what is + * left after it; the whole record must be rejected. + */ + zassert_false(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + zassert_true(ring_buf_is_empty(&buf)); +} + +ZTEST(bt_monitor_buffer, test_record_exactly_filling_buffer) +{ + uint8_t storage[8]; + struct ring_buf buf; + static const uint8_t header[] = { 0x01, 0x02, 0x03 }; + static const uint8_t payload[] = { 0x04, 0x05, 0x06, 0x07, 0x08 }; + static const uint8_t expected[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + const struct bt_monitor_data frags[] = { + { header, sizeof(header) }, + { payload, sizeof(payload) }, + }; + uint8_t output[sizeof(expected)]; + + ring_buf_init(&buf, sizeof(storage), storage); + + zassert_true(bt_monitor_ring_buf_put(&buf, frags, ARRAY_SIZE(frags))); + zassert_equal(ring_buf_size_get(&buf), sizeof(expected)); + zassert_equal(ring_buf_space_get(&buf), 0); + ring_get(&buf, output, sizeof(output)); + zassert_mem_equal(output, expected, sizeof(expected)); +} diff --git a/tests/bluetooth/host/monitor_buffer/tests.yaml b/tests/bluetooth/host/monitor_buffer/tests.yaml new file mode 100644 index 000000000000..e72811c2e5a3 --- /dev/null +++ b/tests/bluetooth/host/monitor_buffer/tests.yaml @@ -0,0 +1,8 @@ +common: + tags: + - bluetooth + - host + - ring_buffer +tests: + bluetooth.host.monitor_buffer: + type: unit diff --git a/tests/bluetooth/init/prj_20.conf b/tests/bluetooth/init/prj_20.conf index 80c0ad4657d4..68a22088b387 100644 --- a/tests/bluetooth/init/prj_20.conf +++ b/tests/bluetooth/init/prj_20.conf @@ -7,6 +7,10 @@ CONFIG_BT_USE_DEBUG_KEYS=y CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y CONFIG_BT_GATT_CLIENT=y CONFIG_BT_DEBUG_MONITOR_UART=y +CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN=y +# Tests default to minimal logging, which the interrupt-driven monitor +# does not support (no panic hook). Use the full log backend instead. +CONFIG_LOG_MODE_DEFERRED=y CONFIG_UART_CONSOLE=n CONFIG_BT_HCI_CORE_LOG_LEVEL_DBG=y CONFIG_BT_CONN_LOG_LEVEL_DBG=y diff --git a/tests/bluetooth/monitor/CMakeLists.txt b/tests/bluetooth/monitor/CMakeLists.txt new file mode 100644 index 000000000000..b35555ea68ed --- /dev/null +++ b/tests/bluetooth/monitor/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(bt_monitor_irq) + +target_sources(app PRIVATE src/main.c) +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/bluetooth/host) diff --git a/tests/bluetooth/monitor/app.overlay b/tests/bluetooth/monitor/app.overlay new file mode 100644 index 000000000000..153310eb76d8 --- /dev/null +++ b/tests/bluetooth/monitor/app.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,bt-hci = &bt_hci_test; + zephyr,bt-mon-uart = &bt_mon_uart; + }; + + bt_hci_test: bt_hci_test { + compatible = "zephyr,bt-hci-test"; + status = "okay"; + }; + + /* Capture buffer deliberately smaller than the monitor ring buffer + * so that overflow tests exercise the buffer-full handling. + */ + bt_mon_uart: bt_mon_uart { + compatible = "vnd,serial"; + status = "okay"; + buffer-size = <64>; + }; +}; diff --git a/tests/bluetooth/monitor/prj.conf b/tests/bluetooth/monitor/prj.conf new file mode 100644 index 000000000000..c9b89e959975 --- /dev/null +++ b/tests/bluetooth/monitor/prj.conf @@ -0,0 +1,16 @@ +CONFIG_ZTEST=y +CONFIG_BT=y +CONFIG_SERIAL=y +CONFIG_BT_DEBUG_MONITOR_UART=y +CONFIG_BT_DEBUG_MONITOR_UART_INTERRUPT_DRIVEN=y +CONFIG_BT_DEBUG_MONITOR_UART_BUFFER_SIZE=128 +# Tests default to minimal logging, which the interrupt-driven monitor +# does not support (no panic hook). Use the full log backend instead. +CONFIG_LOG_MODE_DEFERRED=y +CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_BOOT_BANNER=n +# The emulated vnd,serial device initializes at POST_KERNEL, after the +# monitor's PRE_KERNEL_1 init has already run its device_is_ready() assert. +# The emulated UART works regardless (it has no init function), so disable +# asserts rather than reorder initialization for a test. +CONFIG_ASSERT=n diff --git a/tests/bluetooth/monitor/src/main.c b/tests/bluetooth/monitor/src/main.c new file mode 100644 index 000000000000..41c829bc3c6c --- /dev/null +++ b/tests/bluetooth/monitor/src/main.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2026 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Integration test for the interrupt-driven Bluetooth monitor UART output. + * + * Drives bt_monitor_send() through the real producer path and the real UART + * interrupt handler against the emulated serial-test UART, and parses the + * emitted monitor byte stream. The emulated UART invokes the interrupt + * handler synchronously from uart_irq_tx_enable(), draining the monitor ring + * buffer until it is empty or the capture buffer (which is smaller than the + * monitor buffer) is full, so buffer-full handling, drop accounting and ring + * buffer wrap-around are all exercised. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "monitor.h" + +/* Minimal fake HCI driver to satisfy the zephyr,bt-hci chosen node; the + * monitor is initialized via SYS_INIT and does not need bt_enable(). + */ +#define DT_DRV_COMPAT zephyr_bt_hci_test + +static int fake_driver_open(const struct device *dev) +{ + ARG_UNUSED(dev); + + return -ENOSYS; +} + +static int fake_driver_send(const struct device *dev, struct net_buf *buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(buf); + + return -ENOSYS; +} + +static DEVICE_API(bt_hci, fake_driver_api) = { + .open = fake_driver_open, + .send = fake_driver_send, +}; + +#define TEST_DEVICE_INIT(inst) \ + static struct bt_hci_driver_data fake_driver_data_##inst; \ + static const struct bt_hci_driver_config fake_driver_config_##inst = \ + BT_DT_HCI_DRIVER_CONFIG_INST_GET(inst); \ + DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &fake_driver_data_##inst, \ + &fake_driver_config_##inst, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &fake_driver_api) + +DT_INST_FOREACH_STATUS_OKAY(TEST_DEVICE_INIT) + +static const struct device *const mon_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_mon_uart)); + +/* Large enough for every test's full capture */ +static uint8_t stream[4096]; +static size_t stream_len; + +/* Payload: 4-byte little-endian sequence number followed by a pattern */ +#define TEST_PAYLOAD_LEN 26 +#define TEST_OPCODE BT_MONITOR_ACL_TX_PKT + +/* Payload fill pattern after the sequence number: cycles through the + * uppercase alphabet. + */ +#define PATTERN_BYTE(idx) ('A' + ((idx) % 26)) + +static uint32_t tx_seq; + +static void send_record(void) +{ + uint8_t payload[TEST_PAYLOAD_LEN]; + + sys_put_le32(tx_seq, payload); + for (size_t i = sizeof(tx_seq); i < sizeof(payload); i++) { + payload[i] = PATTERN_BYTE(i); + } + + bt_monitor_send(TEST_OPCODE, payload, sizeof(payload)); + tx_seq++; +} + +/* serial_test only invokes the interrupt handler from within + * uart_irq_tx_enable(): once its capture buffer fills mid-drain, the pump + * stops and reading data out does not restart it. Re-assert TX enable to + * emulate the TX-ready interrupt a real UART raises as accepted bytes + * drain onto the wire; the interrupt handler is idempotent and disables + * TX again when the monitor ring buffer is empty. + */ +static void pump(void) +{ + uart_irq_tx_enable(mon_uart); +} + +static void capture(void) +{ + uint32_t got; + + do { + pump(); + got = serial_vnd_read_out_data(mon_uart, &stream[stream_len], + sizeof(stream) - stream_len); + stream_len += got; + } while (got > 0); + + zassert_true(stream_len < sizeof(stream), "stream buffer exhausted"); +} + +struct stream_stats { + uint32_t records; /* well-formed records of TEST_OPCODE */ + uint32_t last_seq; /* sequence number of the last such record */ + uint32_t seq_errors; /* out-of-order or corrupt payloads */ + uint32_t acl_tx_drops; /* accumulated from extended headers */ + uint32_t parse_errors; /* framing violations */ +}; + +static void parse_stream(struct stream_stats *st) +{ + size_t pos = 0; + uint32_t expect_seq = 0; + bool have_seq = false; + + (void)memset(st, 0, sizeof(*st)); + + while (pos + BT_MONITOR_BASE_HDR_LEN <= stream_len) { + /* data_len counts everything after the data_len field itself, + * i.e. the rest of the base header, the extended header and + * the payload. + */ + uint16_t data_len = sys_get_le16(&stream[pos]); + uint16_t opcode = + sys_get_le16(&stream[pos + offsetof(struct bt_monitor_hdr, opcode)]); + uint8_t hdr_len = stream[pos + offsetof(struct bt_monitor_hdr, hdr_len)]; + size_t payload_len; + const uint8_t *ext = &stream[pos + BT_MONITOR_BASE_HDR_LEN]; + const uint8_t *payload; + size_t i = 0; + + /* Opcodes above the highest defined one indicate lost framing */ + if (data_len < BT_MONITOR_BASE_HDR_LEN - sizeof(data_len) + hdr_len || + opcode > BT_MONITOR_ISO_RX_PKT) { + st->parse_errors++; + return; + } + + if (pos + sizeof(data_len) + data_len > stream_len) { + /* Truncated tail: not published yet, stop parsing */ + return; + } + + payload_len = data_len - (BT_MONITOR_BASE_HDR_LEN - sizeof(data_len)) - hdr_len; + payload = &stream[pos + BT_MONITOR_BASE_HDR_LEN + hdr_len]; + + while (i < hdr_len) { + switch (ext[i]) { + case BT_MONITOR_TS32: + /* Type byte plus 32-bit timestamp */ + i += 1 + sizeof(uint32_t); + break; + case BT_MONITOR_ACL_TX_DROPS: + st->acl_tx_drops += ext[i + 1]; + /* Type byte plus 8-bit drop count */ + i += 2; + break; + case BT_MONITOR_COMMAND_DROPS: + case BT_MONITOR_EVENT_DROPS: + case BT_MONITOR_ACL_RX_DROPS: + case BT_MONITOR_SCO_RX_DROPS: + case BT_MONITOR_SCO_TX_DROPS: + case BT_MONITOR_OTHER_DROPS: + /* Type byte plus 8-bit drop count */ + i += 2; + break; + default: + st->parse_errors++; + return; + } + } + + if (opcode == TEST_OPCODE) { + uint32_t seq; + + if (payload_len != TEST_PAYLOAD_LEN) { + st->seq_errors++; + } else { + seq = sys_get_le32(payload); + /* Sequence numbers must be strictly + * increasing; gaps are drops, not errors. + */ + if (have_seq && seq < expect_seq) { + st->seq_errors++; + } + for (size_t j = sizeof(seq); j < TEST_PAYLOAD_LEN; j++) { + if (payload[j] != PATTERN_BYTE(j)) { + st->seq_errors++; + break; + } + } + st->records++; + st->last_seq = seq; + expect_seq = seq + 1; + have_seq = true; + } + } + + pos += sizeof(data_len) + data_len; + } +} + +/* Flush boot-time records (log backend output, etc.) and any content stuck + * in the monitor ring buffer from a full capture buffer, so that each test + * starts from an empty pipeline. + */ +static void monitor_test_before(void *fixture) +{ + ARG_UNUSED(fixture); + + uint8_t discard[64]; + uint32_t got; + + do { + pump(); + got = serial_vnd_read_out_data(mon_uart, discard, sizeof(discard)); + } while (got > 0); + + stream_len = 0; + tx_seq = 0; +} + +ZTEST_SUITE(bt_monitor_irq, NULL, NULL, monitor_test_before, NULL, NULL); + +ZTEST(bt_monitor_irq, test_single_record) +{ + struct stream_stats st; + + send_record(); + capture(); + parse_stream(&st); + + zassert_equal(st.parse_errors, 0); + zassert_equal(st.seq_errors, 0); + zassert_equal(st.records, 1); + zassert_equal(st.last_seq, 0); + zassert_equal(st.acl_tx_drops, 0); +} + +ZTEST(bt_monitor_irq, test_stream_across_wrap) +{ + struct stream_stats st; + + /* Each record is ~40 bytes against a 128-byte monitor buffer, so + * this crosses the ring buffer wrap point many times. The capture + * is drained after every record, so nothing may be dropped. + */ + for (int i = 0; i < 64; i++) { + send_record(); + capture(); + } + parse_stream(&st); + + zassert_equal(st.parse_errors, 0); + zassert_equal(st.seq_errors, 0); + zassert_equal(st.records, 64); + zassert_equal(st.last_seq, 63); + zassert_equal(st.acl_tx_drops, 0); +} + +ZTEST(bt_monitor_irq, test_overflow_drops_whole_records) +{ + struct stream_stats st; + uint32_t sent; + + /* Without draining the capture buffer, the emulated UART fills up, + * the interrupt handler stops accepting data, and the monitor ring + * buffer overflows: records must be dropped whole and counted. + */ + for (int i = 0; i < 16; i++) { + send_record(); + } + + /* Drain in stages: each new record re-enables TX, which moves + * another capture buffer's worth of backlog out of the ring. + */ + for (int i = 0; i < 16; i++) { + capture(); + send_record(); + } + capture(); + sent = tx_seq; + parse_stream(&st); + + zassert_equal(st.parse_errors, 0, "corrupt stream after overflow"); + zassert_equal(st.seq_errors, 0, "partial or reordered records"); + zassert_true(st.acl_tx_drops > 0, "overflow did not drop records"); + zassert_equal(st.records + st.acl_tx_drops, sent, + "records (%u) + drops (%u) != sent (%u)", + st.records, st.acl_tx_drops, sent); +} diff --git a/tests/bluetooth/monitor/tests.yaml b/tests/bluetooth/monitor/tests.yaml new file mode 100644 index 000000000000..2709634caf8a --- /dev/null +++ b/tests/bluetooth/monitor/tests.yaml @@ -0,0 +1,11 @@ +common: + tags: + - bluetooth + - host +tests: + bluetooth.monitor.uart_irq: + platform_allow: + - native_sim + - native_sim/native/64 + integration_platforms: + - native_sim From 32e70973c43cdbc681db7f5718f849b964f4fcff Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Sun, 23 Aug 2026 15:14:29 -0400 Subject: [PATCH 156/455] arch: x86: absorb the system APIC arch_irq_* glue intc_system_apic.c is not an interrupt controller driver: it is the x86 platform glue that implements arch_irq_enable(), arch_irq_disable() and the vector programming hook by dispatching between the IOAPIC and LOAPIC drivers, which expose their own APIs. Interrupt controller drivers should only expose their own namespaced API, so move the file to arch/x86/core where the glue belongs, unchanged apart from a comment stating its role. Signed-off-by: Anas Nashif --- arch/x86/core/CMakeLists.txt | 1 + .../intc_system_apic.c => arch/x86/core/irq_system_apic.c | 6 ++++++ drivers/interrupt_controller/CMakeLists.txt | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) rename drivers/interrupt_controller/intc_system_apic.c => arch/x86/core/irq_system_apic.c (92%) diff --git a/arch/x86/core/CMakeLists.txt b/arch/x86/core/CMakeLists.txt index 80200f4015b5..9245dc963871 100644 --- a/arch/x86/core/CMakeLists.txt +++ b/arch/x86/core/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_library() zephyr_library_sources(cpuhalt.c) +zephyr_library_sources_ifdef(CONFIG_LOAPIC irq_system_apic.c) zephyr_library_sources(prep_c.c) zephyr_library_sources(fatal.c) zephyr_library_sources(cpuid.c) diff --git a/drivers/interrupt_controller/intc_system_apic.c b/arch/x86/core/irq_system_apic.c similarity index 92% rename from drivers/interrupt_controller/intc_system_apic.c rename to arch/x86/core/irq_system_apic.c index 614a21719cf2..899ee4abf94c 100644 --- a/drivers/interrupt_controller/intc_system_apic.c +++ b/arch/x86/core/irq_system_apic.c @@ -10,6 +10,12 @@ * */ +/* + * This file is x86 platform glue, not an interrupt controller driver: + * it implements the architecture interrupt control functions and vector + * programming by dispatching between the IOAPIC and LOAPIC drivers. + */ + #include #include #include diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index deec5ca1af9b..753e9d52cd68 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -52,7 +52,7 @@ zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_INTC intc_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_INTC_V2 intc_ite_it8xxx2_v2.c) zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_WUC wuc_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_LEON_IRQMP intc_irqmp.c) -zephyr_library_sources_ifdef(CONFIG_LOAPIC intc_loapic.c intc_system_apic.c) +zephyr_library_sources_ifdef(CONFIG_LOAPIC intc_loapic.c) zephyr_library_sources_ifdef(CONFIG_LOAPIC_SPURIOUS_VECTOR intc_loapic_spurious.S) zephyr_library_sources_ifdef(CONFIG_MAX32_RV32_INTC intc_max32_rv32.c) zephyr_library_sources_ifdef(CONFIG_MCHP_ECIA_XEC intc_mchp_ecia_xec.c) From 5908d858b19e48c1466c9862a53f1b53de574719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 14 Aug 2026 09:39:50 +0000 Subject: [PATCH 157/455] drivers: i2c: bitbang: use inclusive terminology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update comments, log messages, Kconfig prose, and driver-local identifiers to use the controller/target terminology ratified by coding guideline A.2 and already used by the Zephyr I2C API. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/i2c/Kconfig.gpio | 2 +- drivers/i2c/i2c_bitbang.c | 18 +++++++++--------- drivers/i2c/i2c_bitbang.h | 2 +- drivers/i2c/i2c_gpio.c | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/Kconfig.gpio b/drivers/i2c/Kconfig.gpio index c124142ea417..9d49149f9c27 100644 --- a/drivers/i2c/Kconfig.gpio +++ b/drivers/i2c/Kconfig.gpio @@ -15,7 +15,7 @@ config I2C_GPIO_CLOCK_STRETCHING bool "GPIO bit banging I2C clock stretching support" default y help - Enable Slave clock stretching support + Enable Target clock stretching support config I2C_GPIO_CLOCK_STRETCHING_TIMEOUT_US int "GPIO bit banging I2C clock stretching timeout (us)" diff --git a/drivers/i2c/i2c_bitbang.c b/drivers/i2c/i2c_bitbang.c index efe467098660..750154c60da6 100644 --- a/drivers/i2c/i2c_bitbang.c +++ b/drivers/i2c/i2c_bitbang.c @@ -8,7 +8,7 @@ * @file * @brief Software driven 'bit-banging' library for I2C * - * This code implements the I2C single master protocol in software by directly + * This code implements the I2C single controller protocol in software by directly * manipulating the levels of the SCL and SDA lines of an I2C bus. It supports * the Standard-mode and Fast-mode speeds and doesn't support optional * protocol feature like 10-bit addresses or clock stretching. @@ -82,7 +82,7 @@ static void i2c_set_scl(struct i2c_bitbang *context, int state) context->io->set_scl(context->io_context, state); #ifdef CONFIG_I2C_GPIO_CLOCK_STRETCHING if (state == 1) { - /* Wait for slave to release the clock */ + /* Wait for target to release the clock */ WAIT_FOR(context->io->get_scl(context->io_context) != 0, CONFIG_I2C_GPIO_CLOCK_STRETCHING_TIMEOUT_US, ;); @@ -114,7 +114,7 @@ static void i2c_start(struct i2c_bitbang *context) if (!i2c_get_sda(context)) { /* * SDA is already low, so we need to do something to make it - * high. Try pulsing clock low to get slave to release SDA. + * high. Try pulsing clock low to get target to release SDA. */ i2c_set_scl(context, 0); i2c_delay(context->delays[T_LOW]); @@ -166,7 +166,7 @@ static bool i2c_read_bit(struct i2c_bitbang *context) bool bit; /* SDA hold time is zero, so no need for a delay here */ - i2c_set_sda(context, 1); /* Stop driving low, so slave has control */ + i2c_set_sda(context, 1); /* Stop driving low, so target has control */ i2c_set_scl(context, 1); i2c_delay(context->delays[T_HIGH]); @@ -204,7 +204,7 @@ static uint8_t i2c_read_byte(struct i2c_bitbang *context) int i2c_bitbang_transfer(struct i2c_bitbang *context, struct i2c_msg *msgs, uint8_t num_msgs, - uint16_t slave_address) + uint16_t target_address) { uint8_t *buf, *buf_end; unsigned int flags; @@ -213,7 +213,7 @@ int i2c_bitbang_transfer(struct i2c_bitbang *context, /* We want an initial Start condition */ flags = I2C_MSG_RESTART; - /* Make sure we're in a good state so slave recognises the Start */ + /* Make sure we're in a good state so target recognises the Start */ i2c_set_scl(context, 1); flags |= I2C_MSG_STOP; @@ -238,7 +238,7 @@ int i2c_bitbang_transfer(struct i2c_bitbang *context, /* Send address after any Start condition */ if (flags & I2C_MSG_RESTART) { - unsigned int byte0 = slave_address << 1; + unsigned int byte0 = target_address << 1; byte0 |= (flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; if (!i2c_write_byte(context, byte0)) { @@ -285,13 +285,13 @@ int i2c_bitbang_recover_bus(struct i2c_bitbang *context) /* * The I2C-bus specification and user manual (NXP UM10204 - * rev. 6, section 3.1.16) suggests the master emit 9 SCL + * rev. 6, section 3.1.16) suggests the controller emit 9 SCL * clock pulses to recover the bus. * * The Linux kernel I2C bitbang recovery functionality issues * a START condition followed by 9 STOP conditions. * - * Other I2C slave devices (e.g. Microchip ATSHA204a) suggest + * Other I2C target devices (e.g. Microchip ATSHA204a) suggest * issuing a START condition followed by 9 SCL clock pulses * with SDA held high/floating, a REPEATED START condition, * and a STOP condition. diff --git a/drivers/i2c/i2c_bitbang.h b/drivers/i2c/i2c_bitbang.h index 39d25a696c70..5c228c633f63 100644 --- a/drivers/i2c/i2c_bitbang.h +++ b/drivers/i2c/i2c_bitbang.h @@ -76,6 +76,6 @@ int i2c_bitbang_recover_bus(struct i2c_bitbang *bitbang); */ int i2c_bitbang_transfer(struct i2c_bitbang *bitbang, struct i2c_msg *msgs, uint8_t num_msgs, - uint16_t slave_address); + uint16_t target_address); #endif /* ZEPHYR_DRIVERS_I2C_I2C_BITBANG_H */ diff --git a/drivers/i2c/i2c_gpio.c b/drivers/i2c/i2c_gpio.c index b827e36c6be4..0ce10be43a04 100644 --- a/drivers/i2c/i2c_gpio.c +++ b/drivers/i2c/i2c_gpio.c @@ -124,7 +124,7 @@ static int i2c_gpio_get_config(const struct device *dev, uint32_t *config) } static int i2c_gpio_transfer(const struct device *dev, struct i2c_msg *msgs, - uint8_t num_msgs, uint16_t slave_address) + uint8_t num_msgs, uint16_t target_address) { struct i2c_gpio_context *context = dev->data; int rc; @@ -132,7 +132,7 @@ static int i2c_gpio_transfer(const struct device *dev, struct i2c_msg *msgs, k_mutex_lock(&context->mutex, K_FOREVER); rc = i2c_bitbang_transfer(&context->bitbang, msgs, num_msgs, - slave_address); + target_address); k_mutex_unlock(&context->mutex); From e76f53a3d6b68b16eac880c39586bff7b9c4777f Mon Sep 17 00:00:00 2001 From: Arkadiusz Grzelka Date: Sun, 23 Aug 2026 14:29:34 +0200 Subject: [PATCH 158/455] net: http: fix a signed overflow decoding an HPACK integer hpack_integer_decode() accumulates the continuation octets of an HPACK variable-length integer with *value += (*buf & ~HPACK_INTEGER_CONTINUATION_FLAG) * (1 << m); Both operands are int, so the multiplication is done in int. The guard above it stops at m > 32, which lets m reach 28, and an octet with any value above 7 then overflows the multiplication: for the last octet of ff 80 80 80 80 7f the product is 127 * 268435456, which does not fit in an int. That is undefined behaviour on bytes a peer chooses: the integer is decoded from a HEADERS frame, which an HTTP/2 client sends before anything has authenticated it. It can be watched happening with the decoder's own test suite. Add CONFIG_UBSAN=y CONFIG_UBSAN_TRAP=y to tests/net/lib/http_server/hpack/prj.conf, and a case that calls the decoder on those bytes, which the table-driven cases there cannot express because the block is refused rather than decoded: ZTEST(http2_hpack, test_integer_overflow) { static const uint8_t buf[] = { 0xff, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x00 }; struct http_hpack_header_buf hdr = { 0 }; zassert_equal(http_hpack_decode_header(buf, sizeof(buf), &hdr), -EBADMSG); } Built for native_sim/native/64 with ZEPHYR_TOOLCHAIN_VARIANT=host/llvm, an unmodified tree dies in that case, START - test_integer_overflow Illegal instruction (core dumped) and with this commit applied the suite passes, the new case included. The toolchain matters: gcc does not diagnose this multiplication at -O2, and the sanitizer runtime that CONFIG_UBSAN selects without CONFIG_UBSAN_TRAP does not come up on native_sim here, so clang in trap mode is what makes it visible. Do the arithmetic in the type the result is stored in, and reject the exponent at the first value that no longer fits in it rather than one step past. The reachable values of m are 0, 7, 14, 21, 28 and 35, so the guard refuses the same octet as before; what changes is that the shift that is performed is now defined. The decoded value can still wrap, which is defined for an unsigned type, and none of the three places that consume it accepts a wrapped one: an index that is not in the table is rejected by http_hpack_table_get(), a string length larger than the data left returns -EAGAIN from hpack_string_decode(), so the server waits for more data instead of reading past the buffer, and the size a dynamic table size update carries is discarded. That leaves a conformance gap this commit does not close: RFC 7541 section 5.1 says integer encodings that exceed implementation limits, in value or in octet length, MUST be treated as decoding errors. The octet-length half is already refused by the guard above; the value half is not, and rejecting a wrapped value is a behaviour change worth making on its own rather than inside a fix for undefined behaviour. No test is added. The old and the new code compute the same value on every toolchain this was tried with, and the decoder returns -EBADMSG for the input above either way, so the difference is visible only to a sanitizer and a ztest asserting the return value passes before the fix as well. The regression anchor is a libFuzzer harness for this decoder, which reaches this input in seconds; it is a separate contribution and is not in the tree yet, so until it lands this fix ships without one. Signed-off-by: Arkadiusz Grzelka Assisted-by: Claude:claude-opus-5 Claude-Code --- subsys/net/lib/http/http_hpack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subsys/net/lib/http/http_hpack.c b/subsys/net/lib/http/http_hpack.c index 4df4de0f7499..8594659df6d8 100644 --- a/subsys/net/lib/http/http_hpack.c +++ b/subsys/net/lib/http/http_hpack.c @@ -184,12 +184,12 @@ static int hpack_integer_decode(const uint8_t *buf, size_t datalen, return -EAGAIN; } - if (m > sizeof(uint32_t) * 8) { + if (m >= sizeof(uint32_t) * 8) { /* Can't handle integer that large. */ return -EBADMSG; } - *value += (*buf & ~HPACK_INTEGER_CONTINUATION_FLAG) * (1 << m); + *value += (uint32_t)(*buf & ~HPACK_INTEGER_CONTINUATION_FLAG) << m; m += 7; } while (*buf & HPACK_INTEGER_CONTINUATION_FLAG); From 8893ebddc0b7d8a387702ed2a2bef033d9cb2339 Mon Sep 17 00:00:00 2001 From: Arkadiusz Grzelka Date: Sun, 23 Aug 2026 10:32:19 +0200 Subject: [PATCH 159/455] tests: bsim: host: security: cover unreachable security levels Nothing in the tree checks what happens when an application asks for a security level the link's pairing method cannot deliver. The host refuses it in smp_send_security_req() and smp_send_pairing_req(), through sec_level_reachable(), and that refusal is the only signal the caller gets: a host that instead paired anyway and reported a lower level as success would leave an application believing it has authentication that the link does not carry. Add a two-device scenario pinning both halves of that behaviour. Neither peer registers authentication callbacks, so both are NoInputNoOutput and Just Works is the only method available, which leaves L3 and L4 out of reach. Each role in turn asks for L4 and for L3 and asserts that the request is refused, that no pairing happened, and that the link stays at L1. Each then asks for L2 and asserts that one is granted, so the test cannot pass on a build that simply never pairs. The two rounds are not redundant: bt_smp_start_security() sends a security request as peripheral but a pairing request as central, so the guard sits on two code paths. The bond from the first round is dropped before the second, otherwise the stored key would answer in place of the guard. Verified by removing the sec_level_reachable() guard from smp_send_pairing_req(): the central-driven round fails on its first assertion, and passes again once the guard is restored. Signed-off-by: Arkadiusz Grzelka --- .../security/level_enforced/CMakeLists.txt | 22 ++ .../host/security/level_enforced/prj.conf | 15 ++ .../security/level_enforced/src/central.c | 51 ++++ .../host/security/level_enforced/src/common.c | 244 ++++++++++++++++++ .../host/security/level_enforced/src/common.h | 31 +++ .../host/security/level_enforced/src/main.c | 36 +++ .../security/level_enforced/src/peripheral.c | 51 ++++ .../host/security/level_enforced/tests.yaml | 14 + .../level_enforced/tests_scripts/run_test.sh | 24 ++ 9 files changed, 488 insertions(+) create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/CMakeLists.txt create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/prj.conf create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/src/central.c create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/src/common.c create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/src/common.h create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/src/main.c create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/src/peripheral.c create mode 100644 tests/bsim/bluetooth/host/security/level_enforced/tests.yaml create mode 100755 tests/bsim/bluetooth/host/security/level_enforced/tests_scripts/run_test.sh diff --git a/tests/bsim/bluetooth/host/security/level_enforced/CMakeLists.txt b/tests/bsim/bluetooth/host/security/level_enforced/CMakeLists.txt new file mode 100644 index 000000000000..cfc27b98911c --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/CMakeLists.txt @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) + +find_package(Zephyr REQUIRED HINTS "$ENV{ZEPHYR_BASE}") +project(bsim_test_security_level_enforced) + +add_subdirectory("${ZEPHYR_BASE}/tests/bsim/babblekit" babblekit) +target_link_libraries(app PRIVATE babblekit) + +target_sources(app PRIVATE + src/central.c + src/common.c + src/main.c + src/peripheral.c +) + +zephyr_include_directories( + "${BSIM_COMPONENTS_PATH}/libUtilv1/src/" + "${BSIM_COMPONENTS_PATH}/libPhyComv1/src/" +) diff --git a/tests/bsim/bluetooth/host/security/level_enforced/prj.conf b/tests/bsim/bluetooth/host/security/level_enforced/prj.conf new file mode 100644 index 000000000000..40c90abda559 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/prj.conf @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-FileCopyrightText: Copyright (c) 2026 Dev It Wise +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y + +CONFIG_BT_SMP=y + +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_MAX_PAIRED=1 + +CONFIG_ASSERT=y +CONFIG_LOG=y diff --git a/tests/bsim/bluetooth/host/security/level_enforced/src/central.c b/tests/bsim/bluetooth/host/security/level_enforced/src/central.c new file mode 100644 index 000000000000..1b357ffad184 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/src/central.c @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2026 Dev It Wise + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common.h" + +#include + +#include "babblekit/testcase.h" + +LOG_MODULE_REGISTER(central, LOG_LEVEL_DBG); + +/* No bt_conn_auth_cb is registered on either side, which is what leaves both + * peers with no input and no output and pins the pairing method to Just Works. + */ +void central(void) +{ + test_setup(); + scan_connect_to_first_result(); + wait_connected(); + + /* First round is the peripheral's. Confirming the link reached L2 here + * is what makes the peripheral's own control a two-sided fact. + */ + expect_observed_elevation(BT_SECURITY_L2); + + wait_disconnected(); + clear_g_conn(); + + /* Second round is the central's. The roles do not share a code path: + * `bt_smp_start_security()` sends a security request as peripheral but a + * pairing request as central, so the guard has to hold on both. Dropping + * the bond first keeps the stored key from answering in its place. + */ + forget_bonds(); + forget_security_result(); + + scan_connect_to_first_result(); + wait_connected(); + + expect_refused_elevation(BT_SECURITY_L4); + expect_refused_elevation(BT_SECURITY_L3); + expect_granted_elevation(BT_SECURITY_L2); + + disconnect_and_wait(); + + TEST_PASS("Central done"); +} diff --git a/tests/bsim/bluetooth/host/security/level_enforced/src/common.c b/tests/bsim/bluetooth/host/security/level_enforced/src/common.c new file mode 100644 index 000000000000..145c675e5ee7 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/src/common.c @@ -0,0 +1,244 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2026 Dev It Wise + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common.h" + +#include +#include +#include + +#include "babblekit/flags.h" +#include "babblekit/testcase.h" + +LOG_MODULE_REGISTER(common, LOG_LEVEL_DBG); + +/* Long enough for a security request, a pairing exchange and an encryption + * change to have travelled the link, so that a refusal that leaked onto the + * link is observed rather than merely outrun. + */ +#define SETTLE_TIME K_MSEC(500) + +/* All of the test's state lives here and is reached only through the calls + * common.h declares, so neither role can read a flag or a reported level + * without going through the check that is supposed to interpret it. + */ +DEFINE_FLAG_STATIC(flag_is_connected); +DEFINE_FLAG_STATIC(flag_security_result); +DEFINE_FLAG_STATIC(flag_pairing_completed); + +static struct bt_conn *g_conn; + +/* The outcome of the elevation attempt, as reported to the application. */ +static bt_security_t g_reported_level; +static enum bt_security_err g_reported_err; + +static void connected(struct bt_conn *conn, uint8_t err) +{ + TEST_ASSERT(g_conn == NULL || conn == g_conn, "Unexpected new connection."); + + if (g_conn == NULL) { + g_conn = bt_conn_ref(conn); + } + + if (err != 0) { + clear_g_conn(); + return; + } + + SET_FLAG(flag_is_connected); +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + UNSET_FLAG(flag_is_connected); +} + +/* Both outcomes of an elevation attempt arrive here: a level that was reached, + * and an error that says it was not. Recording the level alongside the error is + * the whole point of the test, since a host that reports success at a lower + * level than the one asked for is the failure being guarded against. + */ +static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) +{ + LOG_INF("Security changed: level %d, err %d", level, err); + + g_reported_level = level; + g_reported_err = err; + + SET_FLAG(flag_security_result); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, + .security_changed = security_changed, +}; + +static void pairing_complete(struct bt_conn *conn, bool bonded) +{ + LOG_INF("Pairing complete, bonded %d", bonded); + + SET_FLAG(flag_pairing_completed); +} + +static void pairing_failed(struct bt_conn *conn, enum bt_security_err err) +{ + LOG_INF("Pairing failed, err %d", err); + + g_reported_err = err; + + SET_FLAG(flag_security_result); +} + +static struct bt_conn_auth_info_cb auth_info_cb = { + .pairing_complete = pairing_complete, + .pairing_failed = pairing_failed, +}; + +void test_setup(void) +{ + int err; + + err = bt_enable(NULL); + TEST_ASSERT(err == 0, "bt_enable failed (err %d)", err); + + err = bt_conn_auth_info_cb_register(&auth_info_cb); + TEST_ASSERT(err == 0, "bt_conn_auth_info_cb_register failed (err %d)", err); +} + +void wait_connected(void) +{ + WAIT_FOR_FLAG(flag_is_connected); +} + +void wait_disconnected(void) +{ + WAIT_FOR_FLAG_UNSET(flag_is_connected); +} + +void clear_g_conn(void) +{ + struct bt_conn *conn = bt_conn_take(&g_conn); + + TEST_ASSERT(conn, "Test error: no g_conn"); + bt_conn_unref(conn); +} + +void disconnect_and_wait(void) +{ + int err; + + err = bt_conn_disconnect(g_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + TEST_ASSERT(err == 0, "bt_conn_disconnect failed (err %d)", err); + + wait_disconnected(); + clear_g_conn(); +} + +/* The second round has to start from an unpaired link. A stored key would let + * the host satisfy the request from `smp_keys_check()` and never reach the + * reachability guard the round is there to exercise. + */ +void forget_bonds(void) +{ + int err; + + err = bt_unpair(BT_ID_DEFAULT, NULL); + TEST_ASSERT(err == 0, "bt_unpair failed (err %d)", err); +} + +void forget_security_result(void) +{ + UNSET_FLAG(flag_security_result); + UNSET_FLAG(flag_pairing_completed); + + g_reported_level = BT_SECURITY_L1; + g_reported_err = BT_SECURITY_ERR_SUCCESS; +} + +/* An unreachable level must be refused locally: the call fails, nothing is put + * on the link, and the link stays where it was. Both roles owe this behaviour, + * and each reaches it through a different SMP path. + */ +void expect_refused_elevation(bt_security_t level) +{ + int err; + + err = bt_conn_set_security(g_conn, level); + TEST_ASSERT(err < 0, "Elevation to an unreachable L%d was accepted (err %d)", level, err); + + /* Nothing may have happened on the link as a result of the refusal. */ + k_sleep(SETTLE_TIME); + TEST_ASSERT(!IS_FLAG_SET(flag_security_result), + "Refused elevation to L%d still produced a security result", level); + TEST_ASSERT(!IS_FLAG_SET(flag_pairing_completed), + "Refused elevation to L%d still paired", level); + TEST_ASSERT(bt_conn_get_security(g_conn) == BT_SECURITY_L1, + "Link reports level %d after a refused elevation to L%d", + bt_conn_get_security(g_conn), level); +} + +/* The control for the refusals above: the same link does pair when asked for a + * level the pairing method can actually deliver. Without it the refusals would + * also hold on a build that cannot pair at all, which would make the whole test + * a confident lie. + */ +void expect_granted_elevation(bt_security_t level) +{ + int err; + + err = bt_conn_set_security(g_conn, level); + TEST_ASSERT(err == 0, "Elevation to a reachable L%d was refused (err %d)", level, err); + + expect_observed_elevation(level); +} + +/* The same outcome seen from the peer that did not ask for it. The granted + * round is only a two-sided fact if both ends observe the level being reached, + * and this is the half a role cannot get from its own return value. + */ +void expect_observed_elevation(bt_security_t level) +{ + WAIT_FOR_FLAG(flag_security_result); + TEST_ASSERT(g_reported_err == BT_SECURITY_ERR_SUCCESS, "Elevation to L%d failed (err %d)", + level, g_reported_err); + TEST_ASSERT(g_reported_level >= level, + "Link settled at level %d after a successful elevation to L%d", + g_reported_level, level); +} + +static void stop_scan_and_connect(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) +{ + int err; + + if (g_conn != NULL) { + return; + } + + err = bt_le_scan_stop(); + TEST_ASSERT(err == 0, "bt_le_scan_stop failed (err %d)", err); + + err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn); + TEST_ASSERT(err == 0, "bt_conn_le_create failed (err %d)", err); +} + +void scan_connect_to_first_result(void) +{ + int err; + + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, stop_scan_and_connect); + TEST_ASSERT(err == 0, "bt_le_scan_start failed (err %d)", err); +} + +void advertise_connectable(void) +{ + int err; + + err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, NULL, 0, NULL, 0); + TEST_ASSERT(err == 0, "bt_le_adv_start failed (err %d)", err); +} diff --git a/tests/bsim/bluetooth/host/security/level_enforced/src/common.h b/tests/bsim/bluetooth/host/security/level_enforced/src/common.h new file mode 100644 index 000000000000..7276d4b8a93f --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/src/common.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2026 Dev It Wise + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TESTS_BSIM_BLUETOOTH_HOST_SECURITY_LEVEL_ENFORCED_COMMON_H_ +#define TESTS_BSIM_BLUETOOTH_HOST_SECURITY_LEVEL_ENFORCED_COMMON_H_ + +#include + +/* The connection, the flags and the reported outcome are private to common.c. + * Both roles reach them only through the calls below, so there is one place + * that decides what an elevation attempt is supposed to look like. + */ + +void test_setup(void); +void wait_connected(void); +void wait_disconnected(void); +void clear_g_conn(void); +void scan_connect_to_first_result(void); +void advertise_connectable(void); +void disconnect_and_wait(void); +void forget_bonds(void); +void forget_security_result(void); +void expect_refused_elevation(bt_security_t level); +void expect_granted_elevation(bt_security_t level); +void expect_observed_elevation(bt_security_t level); + +#endif /* TESTS_BSIM_BLUETOOTH_HOST_SECURITY_LEVEL_ENFORCED_COMMON_H_ */ diff --git a/tests/bsim/bluetooth/host/security/level_enforced/src/main.c b/tests/bsim/bluetooth/host/security/level_enforced/src/main.c new file mode 100644 index 000000000000..0814ffabb755 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/src/main.c @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2026 Dev It Wise + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bstests.h" + +void central(void); +void peripheral(void); + +static const struct bst_test_instance test_to_add[] = { + { + .test_id = "central", + .test_main_f = central, + }, + { + .test_id = "peripheral", + .test_main_f = peripheral, + }, + BSTEST_END_MARKER, +}; + +static struct bst_test_list *install(struct bst_test_list *tests) +{ + return bst_add_tests(tests, test_to_add); +}; + +bst_test_install_t test_installers[] = {install, NULL}; + +int main(void) +{ + bst_main(); + return 0; +} diff --git a/tests/bsim/bluetooth/host/security/level_enforced/src/peripheral.c b/tests/bsim/bluetooth/host/security/level_enforced/src/peripheral.c new file mode 100644 index 000000000000..225cbaac7e17 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/src/peripheral.c @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2026 Dev It Wise + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common.h" + +#include + +#include "babblekit/testcase.h" + +LOG_MODULE_REGISTER(peripheral, LOG_LEVEL_DBG); + +void peripheral(void) +{ + test_setup(); + advertise_connectable(); + wait_connected(); + + /* Neither peer registers authentication callbacks, so both have no + * input and no output and the only available pairing method is Just + * Works, which produces an unauthenticated key. L3 and L4 are both + * unreachable on this link by construction. + * + * The host must say so instead of pairing anyway and reporting a lower + * level as success: an application that asks for L4 and is told the + * request was accepted has no other signal to act on. + * + * A peripheral request travels `smp_send_security_req()`. + */ + expect_refused_elevation(BT_SECURITY_L4); + expect_refused_elevation(BT_SECURITY_L3); + expect_granted_elevation(BT_SECURITY_L2); + + disconnect_and_wait(); + + /* Second round: the central drives the same three attempts, over a link + * that carries no key from the first round. + */ + forget_bonds(); + forget_security_result(); + + advertise_connectable(); + wait_connected(); + wait_disconnected(); + clear_g_conn(); + + TEST_PASS("Unreachable security levels refused, reachable one granted"); +} diff --git a/tests/bsim/bluetooth/host/security/level_enforced/tests.yaml b/tests/bsim/bluetooth/host/security/level_enforced/tests.yaml new file mode 100644 index 000000000000..51eb84e74082 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/tests.yaml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +tests: + bluetooth.host.security.level_enforced: + tags: + - bluetooth + depends_on: bsim + platform_allow: + - nrf52_bsim/native + harness: bsim + harness_config: + bsim_exe_name: tests_bsim_bluetooth_host_security_level_enforced_prj_conf + fixture: bsim_multi_test diff --git a/tests/bsim/bluetooth/host/security/level_enforced/tests_scripts/run_test.sh b/tests/bsim/bluetooth/host/security/level_enforced/tests_scripts/run_test.sh new file mode 100755 index 000000000000..b0c6dfd759c6 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/level_enforced/tests_scripts/run_test.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +set -eu +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +simulation_id="${BOARD_TS}_security_level_enforced" +verbosity_level=2 + +test_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_$(guess_test_long_name)_prj_conf" + +cd ${BSIM_OUT_PATH}/bin + +Execute "${test_exe}" \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central -RealEncryption=1 + +Execute "${test_exe}" \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral -RealEncryption=1 + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=60e6 $@ + +wait_for_background_jobs From 26e8e1fdcc8da4c3d7fa913a2deca62346606d9f Mon Sep 17 00:00:00 2001 From: Dimitri Varpusvuori Date: Sat, 22 Aug 2026 15:18:40 +0300 Subject: [PATCH 160/455] tests: interrupt: honor NO_TRIGGER_FROM_SW in offload test NO_TRIGGER_FROM_SW identifies architectures that cannot raise an arbitrary hardware interrupt from software. This is independent of IRQ_OFFLOAD: an architecture may support irq_offload() without providing trigger_irq(). interrupt_offload.c references trigger_irq() unconditionally, so the suite fails to compile when that function is intentionally unavailable. Honor the existing capability marker and skip only the real-IRQ variant. Keep the irq_offload-based tests enabled and leave architectures with trigger_irq() unchanged. This is required by the initial m68k port discussed in RFC #114672. Refs #114672 Assisted-by: ChatGPT:gpt-5.5 Assisted-by: ChatGPT:gpt-5.6 Signed-off-by: Dimitri Varpusvuori --- tests/arch/common/interrupt/src/interrupt_offload.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/arch/common/interrupt/src/interrupt_offload.c b/tests/arch/common/interrupt/src/interrupt_offload.c index f26c20f84c90..f8fa8b70bc88 100644 --- a/tests/arch/common/interrupt/src/interrupt_offload.c +++ b/tests/arch/common/interrupt/src/interrupt_offload.c @@ -131,7 +131,11 @@ static void trigger_offload_interrupt(const bool real_irq, void *work) irq_param.work = work; if (real_irq) { +#ifdef NO_TRIGGER_FROM_SW + ztest_test_skip(); +#else trigger_irq(vector_num); +#endif } else { irq_offload((irq_offload_routine_t)&isr_handler, &irq_param); } From 40a02468605c60dbc7c7844b08d68afb743c7a1d Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 22 Aug 2026 10:29:31 +1000 Subject: [PATCH 161/455] pm: device_runtime: fix `put_async` with power domains Fix a race condition that leads to a device being in `STATE_ACTIVE` while its power domain is not enabled. The race occurs when an asynchronous put is running and a `get` request arrives. Assisted-by: GPT-5.5 Signed-off-by: Jordan Yates --- subsys/pm/device_runtime.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/subsys/pm/device_runtime.c b/subsys/pm/device_runtime.c index 39ebf48477a3..2136e785adf7 100644 --- a/subsys/pm/device_runtime.c +++ b/subsys/pm/device_runtime.c @@ -212,6 +212,7 @@ static void runtime_suspend_work(struct k_work *work) int ret; struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct pm_device *pm = CONTAINER_OF(dwork, struct pm_device, work); + bool release_domain = false; ret = pm->base.action_cb(pm->dev, PM_DEVICE_ACTION_SUSPEND); @@ -221,19 +222,21 @@ static void runtime_suspend_work(struct k_work *work) pm->base.state = PM_DEVICE_STATE_ACTIVE; } else { pm->base.state = PM_DEVICE_STATE_SUSPENDED; + release_domain = (pm->base.usage == 0U) && + atomic_test_bit(&pm->base.flags, PM_DEVICE_FLAG_PD_CLAIMED); } k_event_set(&pm->event, BIT(pm->base.state)); - k_sem_give(&pm->lock); /* * On async put, we have to suspend the domain when the device - * finishes its operation + * finishes its operation, unless a get arrived while the suspend was + * running and restored the device usage. */ - if ((ret == 0) && - atomic_test_bit(&pm->base.flags, PM_DEVICE_FLAG_PD_CLAIMED)) { + if (release_domain) { (void)pm_device_runtime_put(PM_DOMAIN(&pm->base)); atomic_clear_bit(&pm->base.flags, PM_DEVICE_FLAG_PD_CLAIMED); } + k_sem_give(&pm->lock); __ASSERT(ret == 0, "Could not suspend device (%d)", ret); } From c758d51ef1321c4f23dce06f8ff952f9650fdee4 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 21:22:36 +1000 Subject: [PATCH 162/455] tests: pm: power_domain: regression test for domain failure Add a regression test that exercises the previously failing path, a `pm_device_runtime_get` that arrives while a device is asynchronously suspending and the device is on a power domain. Also ensures the power domain does not cycle unnecessarily. Assisted-by: GPT-5.5 Signed-off-by: Jordan Yates --- tests/subsys/pm/power_domain/app.overlay | 12 +++ tests/subsys/pm/power_domain/src/main.c | 129 +++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/tests/subsys/pm/power_domain/app.overlay b/tests/subsys/pm/power_domain/app.overlay index 2b8acd67b502..d4e17146494b 100644 --- a/tests/subsys/pm/power_domain/app.overlay +++ b/tests/subsys/pm/power_domain/app.overlay @@ -34,4 +34,16 @@ status = "okay"; power-domains = <&test_domain_balanced>; }; + + test_domain_async: test_domain_async { + compatible = "power-domain"; + status = "okay"; + #power-domain-cells = <0>; + }; + + test_dev_async: test_dev_async { + compatible = "test-device-pm"; + status = "okay"; + power-domains = <&test_domain_async>; + }; }; diff --git a/tests/subsys/pm/power_domain/src/main.c b/tests/subsys/pm/power_domain/src/main.c index 029f24a6c1f2..f2b15f9fe90e 100644 --- a/tests/subsys/pm/power_domain/src/main.c +++ b/tests/subsys/pm/power_domain/src/main.c @@ -288,6 +288,135 @@ ZTEST(power_domain_1cpu, test_power_domain_device_balanced) zassert_equal(state, PM_DEVICE_STATE_ACTIVE); } + +/* Devices and synchronization used to force get-vs-async-suspend ordering. */ +#define TEST_DOMAIN_ASYNC DT_NODELABEL(test_domain_async) +#define TEST_DEV_ASYNC DT_NODELABEL(test_dev_async) + +static bool block_async_suspend; +static K_SEM_DEFINE(async_suspend_started, 0, 1); +static K_SEM_DEFINE(async_suspend_continue, 0, 1); +static K_SEM_DEFINE(async_get_started, 0, 1); +static struct k_thread async_get_thread; +K_THREAD_STACK_DEFINE(async_get_stack, 1024); +static int domain_async_resume_count; +static int domain_async_suspend_count; + +static const struct device *const domain_async = DEVICE_DT_GET(TEST_DOMAIN_ASYNC); +static const struct device *const dev_async = DEVICE_DT_GET(TEST_DEV_ASYNC); + +static int domain_async_pm_action(const struct device *dev, enum pm_device_action pm_action) +{ + if (pm_action == PM_DEVICE_ACTION_RESUME) { + domain_async_resume_count++; + } else if (pm_action == PM_DEVICE_ACTION_SUSPEND) { + domain_async_suspend_count++; + } + + return domain_pm_action(dev, pm_action); +} + +static int async_pm_action(const struct device *dev, enum pm_device_action pm_action) +{ + /* Hold the device suspend action open after async suspend has started. */ + if ((pm_action == PM_DEVICE_ACTION_SUSPEND) && block_async_suspend) { + k_sem_give(&async_suspend_started); + k_sem_take(&async_suspend_continue, K_FOREVER); + block_async_suspend = false; + } + + return deva_pm_action(dev, pm_action); +} + +PM_DEVICE_DT_DEFINE(TEST_DOMAIN_ASYNC, domain_async_pm_action); +DEVICE_DT_DEFINE(TEST_DOMAIN_ASYNC, NULL, PM_DEVICE_DT_GET(TEST_DOMAIN_ASYNC), NULL, NULL, + POST_KERNEL, 10, NULL); + +PM_DEVICE_DT_DEFINE(TEST_DEV_ASYNC, async_pm_action); +DEVICE_DT_DEFINE(TEST_DEV_ASYNC, NULL, PM_DEVICE_DT_GET(TEST_DEV_ASYNC), NULL, NULL, POST_KERNEL, + 20, NULL); + +static void get_async_device(void *arg1, void *arg2, void *arg3) +{ + int ret; + + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + k_sem_give(&async_get_started); + ret = pm_device_runtime_get(dev_async); + zassert_equal(ret, 0); +} + +ZTEST(power_domain_1cpu, test_power_domain_get_while_async_suspend) +{ + enum pm_device_state state; + int ret; + + pm_device_init_suspended(domain_async); + pm_device_init_suspended(dev_async); + domain_async_resume_count = 0; + domain_async_suspend_count = 0; + + ret = pm_device_runtime_enable(domain_async); + zassert_equal(ret, 0); + ret = pm_device_runtime_enable(dev_async); + zassert_equal(ret, 0); + + /* Start from an active child that has claimed its power domain. */ + ret = pm_device_runtime_get(dev_async); + zassert_equal(ret, 0); + zassert_true(pm_device_is_powered(dev_async)); + zassert_equal(domain_async_resume_count, 1); + zassert_equal(domain_async_suspend_count, 0); + + k_sem_reset(&async_suspend_started); + k_sem_reset(&async_suspend_continue); + k_sem_reset(&async_get_started); + block_async_suspend = true; + + /* Queue async suspend and stop it inside the child suspend callback. */ + ret = pm_device_runtime_put_async(dev_async, K_NO_WAIT); + zassert_equal(ret, 0); + zassert_equal(k_sem_take(&async_suspend_started, K_SECONDS(1)), 0); + + pm_device_state_get(dev_async, &state); + zassert_equal(state, PM_DEVICE_STATE_SUSPENDING); + + /* Force a resume request to arrive while the async suspend is running. */ + k_thread_create(&async_get_thread, async_get_stack, K_THREAD_STACK_SIZEOF(async_get_stack), + get_async_device, NULL, NULL, NULL, + K_PRIO_PREEMPT(CONFIG_SYSTEM_WORKQUEUE_PRIORITY), 0, K_NO_WAIT); + + zassert_equal(k_sem_take(&async_get_started, K_SECONDS(1)), 0); + + /* Keep the suspend blocked for a short duration while get() is running. */ + k_sleep(K_MSEC(100)); + + /* Let suspend finish */ + k_sem_give(&async_suspend_continue); + zassert_equal(k_thread_join(&async_get_thread, K_SECONDS(1)), 0); + k_sleep(K_MSEC(10)); + + /* The child must be active and must keep the domain powered/refcounted. */ + pm_device_state_get(dev_async, &state); + zassert_equal(state, PM_DEVICE_STATE_ACTIVE); + pm_device_state_get(domain_async, &state); + zassert_equal(state, PM_DEVICE_STATE_ACTIVE); + zassert_true(pm_device_is_powered(dev_async)); + zassert_true(atomic_test_bit(&dev_async->pm_base->flags, PM_DEVICE_FLAG_PD_CLAIMED)); + zassert_equal(pm_device_runtime_usage(dev_async), 1); + zassert_equal(pm_device_runtime_usage(domain_async), 1); + + /* The domain should not have cycled */ + zassert_equal(domain_async_resume_count, 1); + zassert_equal(domain_async_suspend_count, 0); + + ret = pm_device_runtime_put(dev_async); + zassert_equal(ret, 0); +} + ZTEST(power_domain_1cpu, test_on_power_domain) { zassert_true(device_is_ready(domain), "Device is not ready!"); From 0ef7d23a80911569557b83455b8a3244543bb0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 21 Aug 2026 22:15:48 +0000 Subject: [PATCH 163/455] doc: doxygen: drop dead output and fix mis-set options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit of doc/zephyr.doxyfile.in against the Doxygen 1.17.0 release that CI pins, correcting settings that produce output nothing consumes or that do not apply to a C codebase. The figures below are from full runs over the Doxygen INPUT set with that version. CPP_CLI_SUPPORT is turned off. It enables parsing of Microsoft C++/CLI, which makes '^' a handle declarator and reserves the managed keywords. That was not merely inapplicable: 'internal' is a C++/CLI access specifier, so the 'internal' bitfield of struct dac_channel_cfg, in include/zephyr/drivers/dac.h, was dropped from the generated output without a warning and is missing from the published API documentation. Restoring it is the only change this commit makes to the documented symbols; across the whole tree no other entity gains or loses documentation. XML_PROGRAMLISTING is turned off. The XML tree is consumed only by the Sphinx extensions (doxybridge, api_overview), by coverxygen and by scripts/ci/doxygen_toplevel_groups.py, and none of them read the dump of the scanned sources. Dropping it takes the XML from 221M to 138M, and the time doxmlparser needs to walk it from 72s to 32s, which the Sphinx build pays on every run. The @code blocks written in documentation comments are part of and are kept. The Doxygen Checks workflow runs Doxygen twice per pull request, so it benefits twice over. GENERATE_DOCSET is turned off. Nothing builds or consumes an Apple Xcode docset, but it added Info.plist, Makefile, Nodes.xml and Tokens.xml to the HTML output, all of which are published, and every DOCSET_* value was left at its placeholder ("org.doxygen.Project", "Publisher", "FeedUrl"). That is 51M of the 422M HTML tree. DOT_MULTI_TARGETS is removed. It was dropped in Doxygen 1.16 and made every run print an "has become obsolete" warning. CHM_FILE is cleared. It names the CHM file to generate, so "NO" was never meaningful, and it is inert while GENERATE_HTMLHELP is off. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- doc/zephyr.doxyfile.in | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/doc/zephyr.doxyfile.in b/doc/zephyr.doxyfile.in index 59189242216b..2f9e7181d5fc 100644 --- a/doc/zephyr.doxyfile.in +++ b/doc/zephyr.doxyfile.in @@ -478,7 +478,7 @@ BUILTIN_STL_SUPPORT = NO # enable parsing support. # The default value is: NO. -CPP_CLI_SUPPORT = YES +CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software) sources only. Doxygen will parse @@ -1603,7 +1603,7 @@ HTML_INDEX_NUM_ENTRIES = 100 # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_DOCSET = YES +GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider @@ -1669,7 +1669,7 @@ GENERATE_HTMLHELP = NO # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = NO +CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, @@ -2406,7 +2406,7 @@ XML_OUTPUT = xml # The default value is: YES. # This tag requires that the tag GENERATE_XML is set to YES. -XML_PROGRAMLISTING = YES +XML_PROGRAMLISTING = NO # If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include # namespace members in file scope as well, matching the HTML output. @@ -3040,15 +3040,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) support -# this, this feature is disabled by default. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_MULTI_TARGETS = NO - # If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. From 308f7066d1bb1c7e05cabc60d509527804436518 Mon Sep 17 00:00:00 2001 From: Richard Mc Sweeney Date: Fri, 26 Jun 2026 10:20:52 -0700 Subject: [PATCH 164/455] drivers: counter: add Infineon Counter pm_action Added pm_action suspend/resume/turn_on routine for Counter Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Richard Mc Sweeney --- drivers/counter/counter_infineon_tcpwm.c | 72 ++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/drivers/counter/counter_infineon_tcpwm.c b/drivers/counter/counter_infineon_tcpwm.c index 6ad09d86116f..a81f076a4284 100644 --- a/drivers/counter/counter_infineon_tcpwm.c +++ b/drivers/counter/counter_infineon_tcpwm.c @@ -1,6 +1,6 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2026 Infineon Technologies AG, - * or an affiliate of Infineon Technologies AG. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2026 Infineon Technologies AG, + * SPDX-FileCopyrightText: or an affiliate of Infineon Technologies AG. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,12 @@ struct ifx_tcpwm_counter_data { struct ifx_cat1_clock clock; /* Counter input frequency, cached at init (see ifx_tcpwm_counter_get_freq) */ uint32_t freq; +#ifdef CONFIG_PM_DEVICE + /* Whether the counter was running when suspend was entered, so + * resume only restarts a counter that was previously active. + */ + bool was_running; +#endif /* CONFIG_PM_DEVICE */ }; static const cy_stc_tcpwm_counter_config_t counter_default_config = { @@ -213,6 +220,14 @@ static int ifx_tcpwm_counter_start(const struct device *dev) Cy_TCPWM_TriggerStart_Single(config->reg_base, config->index); #endif +#ifdef CONFIG_PM_DEVICE + { + struct ifx_tcpwm_counter_data *const data = dev->data; + + data->was_running = true; + } +#endif /* CONFIG_PM_DEVICE */ + return 0; } @@ -224,6 +239,14 @@ static int ifx_tcpwm_counter_stop(const struct device *dev) Cy_TCPWM_Counter_Disable(config->reg_base, config->index); +#ifdef CONFIG_PM_DEVICE + { + struct ifx_tcpwm_counter_data *const data = dev->data; + + data->was_running = false; + } +#endif /* CONFIG_PM_DEVICE */ + return 0; } @@ -480,6 +503,43 @@ static int ifx_tcpwm_counter_set_guard_period(const struct device *dev, uint32_t return 0; } +#ifdef CONFIG_PM_DEVICE +static int ifx_tcpwm_counter_pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct ifx_tcpwm_counter_config *config = dev->config; + struct ifx_tcpwm_counter_data *const data = dev->data; + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + /* Clock gate the block; clock tree left untouched. */ + Cy_TCPWM_Counter_Disable(config->reg_base, config->index); + break; + case PM_DEVICE_ACTION_RESUME: + Cy_TCPWM_Counter_Enable(config->reg_base, config->index); + /* Restart the counter if it was running before suspend. */ + if (data->was_running) { + (void)ifx_tcpwm_counter_start(dev); + } + break; +#if defined(CONFIG_PM_S2RAM) || defined(CONFIG_PM_DEVICE_POWER_DOMAIN) + case PM_DEVICE_ACTION_TURN_ON: { + /* Power was removed so re-initialize the peripheral. */ + int ret = ifx_tcpwm_counter_init(dev); + + if (ret < 0) { + return ret; + } + break; + } +#endif /* CONFIG_PM_S2RAM || CONFIG_PM_DEVICE_POWER_DOMAIN */ + default: + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_PM_DEVICE */ + static DEVICE_API(counter, counter_api) = { .start = ifx_tcpwm_counter_start, .stop = ifx_tcpwm_counter_stop, @@ -545,6 +605,8 @@ static DEVICE_API(counter, counter_api) = { static struct ifx_tcpwm_counter_data ifx_tcpwm_counter##n##_data = { \ COUNTER_PERI_CLOCK_INIT(n)}; \ \ + PM_DEVICE_DT_INST_DEFINE(n, ifx_tcpwm_counter_pm_action); \ + \ static const struct ifx_tcpwm_counter_config ifx_tcpwm_counter##n##_config = { \ .counter_info = {.max_top_value = (DT_PROP(DT_INST_PARENT(n), resolution) == 32) \ ? UINT32_MAX \ @@ -560,8 +622,8 @@ static DEVICE_API(counter, counter_api) = { .irq_enable_func = ifx_counter_irq_enable_func_##n, \ }; \ \ - DEVICE_DT_INST_DEFINE(n, ifx_tcpwm_counter_init, NULL, &ifx_tcpwm_counter##n##_data, \ - &ifx_tcpwm_counter##n##_config, POST_KERNEL, \ - CONFIG_COUNTER_INIT_PRIORITY, &counter_api); + DEVICE_DT_INST_DEFINE(n, ifx_tcpwm_counter_init, PM_DEVICE_DT_INST_GET(n), \ + &ifx_tcpwm_counter##n##_data, &ifx_tcpwm_counter##n##_config, \ + POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, &counter_api); DT_INST_FOREACH_STATUS_OKAY(INFINEON_TCPWM_COUNTER_INIT); From f6c9d4224bfed33b21ee2465215d2ae28d89de26 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Fri, 21 Aug 2026 15:24:39 -0500 Subject: [PATCH 165/455] drivers: sensor: adxl345: clear is_fifo in one-shot sample reads adxl345_decoder_decode() routes on is_fifo at byte offset 0 to decide whether to call adxl345_decode_stream(). adxl345_submit_fetch() passes adxl345_read_sample() a raw RTIO mempool buffer, which is not zeroed, and adxl345_read_sample() never sets is_fifo, so the flag is whatever the previous user of that block left behind. With CONFIG_ADXL345_STREAM=y, a one-shot sensor_read() can therefore be misrouted into adxl345_decode_stream(), which reads a 12-byte struct adxl345_fifo_data header out of a buffer sized for the 10-byte struct adxl345_sample and then iterates on an out-of-bounds fifo_byte_count. Clear is_fifo explicitly, as adxl362, adxl367 and adxl372 already do. Assisted-by: Claude:claude-opus-5 Signed-off-by: Maureen Helm --- drivers/sensor/adi/adxl345/adxl345.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/sensor/adi/adxl345/adxl345.c b/drivers/sensor/adi/adxl345/adxl345.c index f6e2c0a9c33c..901b126565ab 100644 --- a/drivers/sensor/adi/adxl345/adxl345.c +++ b/drivers/sensor/adi/adxl345/adxl345.c @@ -370,6 +370,9 @@ int adxl345_read_sample(const struct device *dev, sample->selected_range = data->selected_range; sample->is_full_res = data->is_full_res; +#ifdef CONFIG_ADXL345_STREAM + sample->is_fifo = 0; +#endif /* CONFIG_ADXL345_STREAM */ return 0; } From b3d6c3611a59ee5f968aab407b82bd0590d90c10 Mon Sep 17 00:00:00 2001 From: Corey Wharton Date: Fri, 21 Aug 2026 14:52:54 -0700 Subject: [PATCH 166/455] drivers: i3c: dw: fix PM device action callback name PM_DEVICE_DT_INST_DEFINE() references dw_i3c_pm_action, but the callback is named dw_i3c_pm_ctrl. The symbol does not exist anywhere in the tree, so building with CONFIG_PM_DEVICE=y fails with 'dw_i3c_pm_action' undeclared. Reference the actual name at both instantiation sites, snps,designware-i3c and microchip,xec-i3c. Signed-off-by: Corey Wharton --- drivers/i3c/i3c_dw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/i3c_dw.c b/drivers/i3c/i3c_dw.c index 600ee25b75fc..eb2eb326ff4d 100644 --- a/drivers/i3c/i3c_dw.c +++ b/drivers/i3c/i3c_dw.c @@ -2987,7 +2987,7 @@ static DEVICE_API(i3c, dw_i3c_api) = { DT_INST_PROP_OR(n, primary_controller_da, 0x00), \ .common.flags = I3C_CONTROLLER_CONFIG_FLAGS_DT_INST(n),)) \ I3C_DW_PINCTRL_INIT(n)}; \ - PM_DEVICE_DT_INST_DEFINE(n, dw_i3c_pm_action); \ + PM_DEVICE_DT_INST_DEFINE(n, dw_i3c_pm_ctrl); \ DEVICE_DT_INST_DEFINE(n, dw_i3c_init, PM_DEVICE_DT_INST_GET(n), &dw_i3c_data_##n, \ &dw_i3c_cfg_##n, POST_KERNEL, CONFIG_I3C_CONTROLLER_INIT_PRIORITY, \ &dw_i3c_api); @@ -3070,7 +3070,7 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_HAS_MCHP_MEC_I3C), DT_INST_PROP_OR(n, primary_controller_da, 0x00), \ .common.flags = I3C_CONTROLLER_CONFIG_FLAGS_DT_INST(n),)) \ I3C_DW_PINCTRL_INIT(n)}; \ - PM_DEVICE_DT_INST_DEFINE(n, dw_i3c_pm_action); \ + PM_DEVICE_DT_INST_DEFINE(n, dw_i3c_pm_ctrl); \ DEVICE_DT_INST_DEFINE(n, dw_i3c_init, PM_DEVICE_DT_INST_GET(n), &xec_i3c_data_##n, \ &xec_i3c_cfg_##n, POST_KERNEL, CONFIG_I3C_CONTROLLER_INIT_PRIORITY, \ &dw_i3c_api); From 3c6834da952f4396a6e11baa4969aabe0f81814b Mon Sep 17 00:00:00 2001 From: Corey Wharton Date: Fri, 21 Aug 2026 14:54:51 -0700 Subject: [PATCH 167/455] tests: drivers: build_all: i3c: add build for pm device No scenario enabled CONFIG_PM_DEVICE, so the i3c drivers' PM hooks were never compiled in CI. Add a dual role scenario with CONFIG_PM_DEVICE=y, covering both cdns,i3c and snps,designware-i3c through the existing qemu_cortex_m3 overlay. Signed-off-by: Corey Wharton --- tests/drivers/build_all/i3c/tests.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/drivers/build_all/i3c/tests.yaml b/tests/drivers/build_all/i3c/tests.yaml index e1f954e25088..59a696ea8ac4 100644 --- a/tests/drivers/build_all/i3c/tests.yaml +++ b/tests/drivers/build_all/i3c/tests.yaml @@ -46,6 +46,13 @@ tests: - nucleo_u385rg_q tags: i3c_cdns i3c_dw extra_args: "CONFIG_I3C_TARGET_ROLE_ONLY=y CONFIG_I3C_USE_IBI=n" + drivers.i3c.build.dual_role_pm: + # will cover drivers without in-tree boards + platform_allow: + - qemu_cortex_m3 + - nucleo_u385rg_q + tags: i3c_cdns i3c_dw + extra_args: "CONFIG_I3C_DUAL_ROLE=y CONFIG_I3C_RTIO=y CONFIG_PM_DEVICE=y" drivers.i3c.build.stm32: platform_allow: - nucleo_c5a3zg From 6f278808b7704eb6ae32f9756beb6b7738d894f5 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Fri, 21 Aug 2026 10:38:54 -0700 Subject: [PATCH 168/455] tests: mcumgr/os_mgmt_info: depends_on bluetooth Replace the dependency on ble with most widely used bluetooth tag. Signed-off-by: Flavio Ceolin --- tests/subsys/mgmt/mcumgr/os_mgmt_info/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/subsys/mgmt/mcumgr/os_mgmt_info/tests.yaml b/tests/subsys/mgmt/mcumgr/os_mgmt_info/tests.yaml index 917ae46a0852..9c3fe6f79ee2 100644 --- a/tests/subsys/mgmt/mcumgr/os_mgmt_info/tests.yaml +++ b/tests/subsys/mgmt/mcumgr/os_mgmt_info/tests.yaml @@ -22,7 +22,7 @@ tests: - CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS=n - CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=n mgmt.mcumgr.os.info.bt: - depends_on: ble + depends_on: bluetooth extra_configs: - CONFIG_BT=y - CONFIG_BT_DEVICE_NAME="a_bt_name" From 682f2e0430d19ebcfd52dc7ff3f056520d3fb5ed Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Fri, 21 Aug 2026 10:50:03 -0700 Subject: [PATCH 169/455] boards: replace "ble" supported tag with "bluetooth" The "ble" board supported feature tag is replaced in favor of "bluetooth". Update all remaining board YAML files so tests using depends_on: bluetooth are correctly filtered in. Only one test was using this tag and it was previsouly changed. Signed-off-by: Flavio Ceolin --- boards/96boards/carbon/96b_carbon_nrf51822.yaml | 2 +- boards/96boards/carbon/96b_carbon_stm32f401xe.yaml | 2 +- boards/96boards/nitrogen/96b_nitrogen.yaml | 2 +- boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840.yaml | 2 +- .../adafruit_feather_nrf52840_nrf52840_sense.yaml | 2 +- .../adafruit_feather_nrf52840_nrf52840_sense_uf2.yaml | 2 +- .../adafruit_feather_nrf52840_nrf52840_uf2.yaml | 2 +- boards/adafruit/itsybitsy/adafruit_itsybitsy_nrf52840.yaml | 2 +- boards/ambiq/apollo4p_blue_kxr_evb/apollo4p_blue_kxr_evb.yaml | 2 +- .../nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev1.yaml | 2 +- .../nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev2.yaml | 2 +- boards/arduino/nano_33_ble/arduino_nano_33_ble_rev1.yaml | 2 +- boards/arduino/nano_33_ble/arduino_nano_33_ble_rev2.yaml | 2 +- boards/bbc/microbit/bbc_microbit.yaml | 2 +- boards/bbc/microbit_v2/bbc_microbit_v2.yaml | 2 +- .../bcdevices/plt_demo_v2/blueclover_plt_demo_v2_nrf52832.yaml | 2 +- boards/bytesatwork/bytesensi_l/bytesensi_l.yaml | 2 +- boards/coredevices/p2d/p2d.yaml | 2 +- boards/croxel/croxel_cx1825/croxel_cx1825_nrf52840.yaml | 2 +- boards/ct/ctcc/ctcc_nrf52840.yaml | 2 +- boards/electronut/nrf52840_blip/nrf52840_blip.yaml | 2 +- boards/electronut/nrf52840_papyr/nrf52840_papyr.yaml | 2 +- boards/ezurio/bl653_dvk/bl653_dvk.yaml | 2 +- boards/ezurio/bl654_dvk/bl654_dvk.yaml | 2 +- boards/ezurio/bl654_dvk/bl654_dvk_nrf52840_pa.yaml | 2 +- boards/ezurio/bl654_sensor_board/bl654_sensor_board.yaml | 2 +- boards/ezurio/bl654_usb/bl654_usb.yaml | 2 +- boards/ezurio/bl654_usb/bl654_usb_nrf52840_bare.yaml | 2 +- boards/ezurio/bt510/bt510.yaml | 2 +- boards/ezurio/bt610/bt610.yaml | 2 +- boards/ezurio/mg100/mg100.yaml | 2 +- boards/ezurio/pinnacle_100_dvk/pinnacle_100_dvk.yaml | 2 +- boards/ezurio/rm1xx_dvk/rm1xx_dvk.yaml | 2 +- boards/fobe/quill_nrf52840_mesh/quill_nrf52840_mesh.yaml | 2 +- boards/heltec/heltec_t114_v2/heltec_t114_v2.yaml | 2 +- boards/heltec/heltec_t114_v2/heltec_t114_v2_nrf52840_uf2.yaml | 2 +- boards/holyiot/yj16019/holyiot_yj16019.yaml | 2 +- boards/holyiot/yj17095/holyiot_yj17095.yaml | 2 +- boards/makerdiary/nrf52840_mdk/nrf52840_mdk.yaml | 2 +- .../nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.yaml | 2 +- boards/mikroe/hexiwear/hexiwear_mk64f12.yaml | 2 +- boards/nordic/nrf21540dk/nrf21540dk_nrf52840.yaml | 2 +- boards/nordic/nrf51dk/nrf51dk_nrf51822.yaml | 2 +- boards/nordic/nrf51dongle/nrf51dongle_nrf51822.yaml | 2 +- boards/nordic/nrf52833dk/nrf52833dk_nrf52820.yaml | 2 +- boards/nordic/nrf52833dk/nrf52833dk_nrf52833.yaml | 2 +- boards/nordic/nrf52840dk/nrf52840dk_nrf52840.yaml | 2 +- boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840.yaml | 2 +- boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840_bare.yaml | 2 +- boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_14_0.yaml | 2 +- boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_7_0.yaml | 2 +- boards/nordic/thingy53/thingy53_nrf5340_cpuapp.yaml | 2 +- boards/nucode/nucode_nu32/nucode_nu32_nrf52832.yaml | 2 +- boards/nucode/nucode_nu40/nucode_nu40_nrf52840.yaml | 2 +- boards/nucode/nucode_nu40/nucode_nu40_nrf52840_bare.yaml | 2 +- boards/others/promicro_nrf52840/promicro_nrf52840.yaml | 2 +- .../promicro_nrf52840/promicro_nrf52840_nrf52840_uf2.yaml | 2 +- boards/panasonic/pan1770_evb/pan1770_evb.yaml | 2 +- boards/panasonic/pan1780_evb/pan1780_evb.yaml | 2 +- boards/panasonic/pan1781_evb/pan1781_evb.yaml | 2 +- boards/panasonic/pan1782_evb/pan1782_evb.yaml | 2 +- boards/particle/argon/particle_argon.yaml | 2 +- boards/particle/boron/particle_boron.yaml | 2 +- boards/particle/nrf51_blenano/nrf51_blenano.yaml | 2 +- boards/particle/xenon/particle_xenon.yaml | 2 +- boards/phytec/reel_board/reel_board_1.yaml | 2 +- boards/phytec/reel_board/reel_board_nrf52840_2.yaml | 2 +- boards/qorvo/decawave_dwm3001cdk/decawave_dwm3001cdk.yaml | 2 +- boards/rakwireless/rak4631/rak4631_nrf52840.yaml | 2 +- boards/rakwireless/rak5010/rak5010_nrf52840.yaml | 2 +- .../raytac_mdbt50q_cx_40_dongle_nrf52840.yaml | 2 +- boards/raytac/mdbt50q_db_33/raytac_mdbt50q_db_33_nrf52833.yaml | 2 +- boards/raytac/mdbt50q_db_40/raytac_mdbt50q_db_40_nrf52840.yaml | 2 +- boards/ruuvi/ruuvitag/ruuvi_ruuvitag.yaml | 2 +- boards/seeed/wio_tracker_l1/wio_tracker_l1.yaml | 2 +- boards/seeed/xiao_ble/xiao_ble.yaml | 2 +- boards/seeed/xiao_ble/xiao_ble_nrf52840_sense.yaml | 2 +- boards/sparkfun/micromod/micromod_nrf52840.yaml | 2 +- boards/st/b_l4s5i_iot01a/b_l4s5i_iot01a.yaml | 2 +- boards/st/disco_l475_iot1/disco_l475_iot1.yaml | 2 +- boards/st/sensortile_box/sensortile_box.yaml | 2 +- boards/st/sensortile_box_pro/sensortile_box_pro.yaml | 2 +- boards/st/steval_stwinbx1/steval_stwinbx1.yaml | 2 +- boards/st/stm32l562e_dk/stm32l562e_dk.yaml | 2 +- boards/u-blox/ubx_bmd340eval/ubx_bmd340eval_nrf52840.yaml | 2 +- boards/u-blox/ubx_bmd345eval/ubx_bmd345eval_nrf52840.yaml | 2 +- boards/u-blox/ubx_bmd380eval/ubx_bmd380eval_nrf52840.yaml | 2 +- boards/u-blox/ubx_evkninab3/ubx_evkninab3_nrf52840.yaml | 2 +- boards/u-blox/ubx_evkninab4/ubx_evkninab4_nrf52833.yaml | 2 +- boards/waveshare/nrf51_ble400/nrf51_ble400.yaml | 2 +- boards/we/proteus3ev/we_proteus3ev_nrf52840.yaml | 2 +- 91 files changed, 91 insertions(+), 91 deletions(-) diff --git a/boards/96boards/carbon/96b_carbon_nrf51822.yaml b/boards/96boards/carbon/96b_carbon_nrf51822.yaml index daa4501a13f5..fe54913de7b6 100644 --- a/boards/96boards/carbon/96b_carbon_nrf51822.yaml +++ b/boards/96boards/carbon/96b_carbon_nrf51822.yaml @@ -8,5 +8,5 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth vendor: seeed diff --git a/boards/96boards/carbon/96b_carbon_stm32f401xe.yaml b/boards/96boards/carbon/96b_carbon_stm32f401xe.yaml index a0254053ca31..4641d6eff7f6 100644 --- a/boards/96boards/carbon/96b_carbon_stm32f401xe.yaml +++ b/boards/96boards/carbon/96b_carbon_stm32f401xe.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - gpio - - ble + - bluetooth - i2c - counter - spi diff --git a/boards/96boards/nitrogen/96b_nitrogen.yaml b/boards/96boards/nitrogen/96b_nitrogen.yaml index 43875e92ed92..6031ca936fdb 100644 --- a/boards/96boards/nitrogen/96b_nitrogen.yaml +++ b/boards/96boards/nitrogen/96b_nitrogen.yaml @@ -6,7 +6,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - gpio - i2c - spi diff --git a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840.yaml b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840.yaml index 2f9551d9ae00..3bf3e5227c99 100644 --- a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840.yaml +++ b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - watchdog - counter - feather_serial diff --git a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense.yaml b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense.yaml index b1e358d8ce6d..386f42709de5 100644 --- a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense.yaml +++ b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - watchdog - counter - feather_serial diff --git a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense_uf2.yaml b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense_uf2.yaml index 8033b1a7c270..2e68eff06b73 100644 --- a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense_uf2.yaml +++ b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_sense_uf2.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - watchdog - counter - feather_serial diff --git a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_uf2.yaml b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_uf2.yaml index 968b30bd6d4f..17a1a653dcf7 100644 --- a/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_uf2.yaml +++ b/boards/adafruit/feather_nrf52840/adafruit_feather_nrf52840_nrf52840_uf2.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - watchdog - counter - feather_serial diff --git a/boards/adafruit/itsybitsy/adafruit_itsybitsy_nrf52840.yaml b/boards/adafruit/itsybitsy/adafruit_itsybitsy_nrf52840.yaml index f954fc16b41c..a05eb561a751 100644 --- a/boards/adafruit/itsybitsy/adafruit_itsybitsy_nrf52840.yaml +++ b/boards/adafruit/itsybitsy/adafruit_itsybitsy_nrf52840.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/ambiq/apollo4p_blue_kxr_evb/apollo4p_blue_kxr_evb.yaml b/boards/ambiq/apollo4p_blue_kxr_evb/apollo4p_blue_kxr_evb.yaml index 0f938a51b569..e89a19af3587 100644 --- a/boards/ambiq/apollo4p_blue_kxr_evb/apollo4p_blue_kxr_evb.yaml +++ b/boards/ambiq/apollo4p_blue_kxr_evb/apollo4p_blue_kxr_evb.yaml @@ -17,7 +17,7 @@ supported: - i2c - i2c_target - clock_control - - ble + - bluetooth - usbd testing: ignore_tags: diff --git a/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev1.yaml b/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev1.yaml index 2169c8e5d234..db9940ab4307 100644 --- a/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev1.yaml +++ b/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev1.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - i2c - pwm - serial diff --git a/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev2.yaml b/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev2.yaml index d17ec02467c3..4a632cfb9550 100644 --- a/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev2.yaml +++ b/boards/arduino/nano_33_ble/arduino_nano_33_ble_nrf52840_sense_rev2.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - i2c - pwm - serial diff --git a/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev1.yaml b/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev1.yaml index 996932f0e4a4..71fb4c1474ec 100644 --- a/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev1.yaml +++ b/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev1.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - i2c - pwm - serial diff --git a/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev2.yaml b/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev2.yaml index 63d461c1a845..51c55792e86a 100644 --- a/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev2.yaml +++ b/boards/arduino/nano_33_ble/arduino_nano_33_ble_rev2.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - i2c - pwm - serial diff --git a/boards/bbc/microbit/bbc_microbit.yaml b/boards/bbc/microbit/bbc_microbit.yaml index 521c111f2b89..4df72153beb5 100644 --- a/boards/bbc/microbit/bbc_microbit.yaml +++ b/boards/bbc/microbit/bbc_microbit.yaml @@ -10,7 +10,7 @@ testing: ignore_tags: - net supported: - - ble + - bluetooth - i2c - gpio - pwm diff --git a/boards/bbc/microbit_v2/bbc_microbit_v2.yaml b/boards/bbc/microbit_v2/bbc_microbit_v2.yaml index 81928e2c1eda..2fd71788caaa 100644 --- a/boards/bbc/microbit_v2/bbc_microbit_v2.yaml +++ b/boards/bbc/microbit_v2/bbc_microbit_v2.yaml @@ -10,6 +10,6 @@ testing: ignore_tags: - net supported: - - ble + - bluetooth - i2c - gpio diff --git a/boards/bcdevices/plt_demo_v2/blueclover_plt_demo_v2_nrf52832.yaml b/boards/bcdevices/plt_demo_v2/blueclover_plt_demo_v2_nrf52832.yaml index 74fa69a1cba0..ec730d947462 100644 --- a/boards/bcdevices/plt_demo_v2/blueclover_plt_demo_v2_nrf52832.yaml +++ b/boards/bcdevices/plt_demo_v2/blueclover_plt_demo_v2_nrf52832.yaml @@ -9,7 +9,7 @@ toolchain: ram: 64 flash: 512 supported: - - ble + - bluetooth - counter - nvs - i2c diff --git a/boards/bytesatwork/bytesensi_l/bytesensi_l.yaml b/boards/bytesatwork/bytesensi_l/bytesensi_l.yaml index 362508e67a2f..37fe2ace4791 100644 --- a/boards/bytesatwork/bytesensi_l/bytesensi_l.yaml +++ b/boards/bytesatwork/bytesensi_l/bytesensi_l.yaml @@ -10,7 +10,7 @@ toolchain: ram: 64 flash: 512 supported: - - ble + - bluetooth - gpio - i2c - lora diff --git a/boards/coredevices/p2d/p2d.yaml b/boards/coredevices/p2d/p2d.yaml index 3f530c506e67..f08e553265e9 100644 --- a/boards/coredevices/p2d/p2d.yaml +++ b/boards/coredevices/p2d/p2d.yaml @@ -11,7 +11,7 @@ toolchain: ram: 256 flash: 1024 supported: - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/croxel/croxel_cx1825/croxel_cx1825_nrf52840.yaml b/boards/croxel/croxel_cx1825/croxel_cx1825_nrf52840.yaml index 3eef0741d5af..57fae08e086e 100644 --- a/boards/croxel/croxel_cx1825/croxel_cx1825_nrf52840.yaml +++ b/boards/croxel/croxel_cx1825/croxel_cx1825_nrf52840.yaml @@ -8,7 +8,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/ct/ctcc/ctcc_nrf52840.yaml b/boards/ct/ctcc/ctcc_nrf52840.yaml index 6ca082d8d276..087b968ac7c4 100644 --- a/boards/ct/ctcc/ctcc_nrf52840.yaml +++ b/boards/ct/ctcc/ctcc_nrf52840.yaml @@ -6,7 +6,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - gpio - usbd - watchdog diff --git a/boards/electronut/nrf52840_blip/nrf52840_blip.yaml b/boards/electronut/nrf52840_blip/nrf52840_blip.yaml index 975f1b831edf..17f2013cc3e7 100644 --- a/boards/electronut/nrf52840_blip/nrf52840_blip.yaml +++ b/boards/electronut/nrf52840_blip/nrf52840_blip.yaml @@ -9,5 +9,5 @@ supported: - adc - i2c - usb_device - - ble + - bluetooth vendor: electronutlabs diff --git a/boards/electronut/nrf52840_papyr/nrf52840_papyr.yaml b/boards/electronut/nrf52840_papyr/nrf52840_papyr.yaml index 1c743016802e..87d2707df7ec 100644 --- a/boards/electronut/nrf52840_papyr/nrf52840_papyr.yaml +++ b/boards/electronut/nrf52840_papyr/nrf52840_papyr.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usb_device - - ble + - bluetooth - pwm - watchdog vendor: electronutlabs diff --git a/boards/ezurio/bl653_dvk/bl653_dvk.yaml b/boards/ezurio/bl653_dvk/bl653_dvk.yaml index 24a17bc8c777..a4f6faca0047 100644 --- a/boards/ezurio/bl653_dvk/bl653_dvk.yaml +++ b/boards/ezurio/bl653_dvk/bl653_dvk.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usb_device - - ble + - bluetooth - pwm - watchdog - gpio diff --git a/boards/ezurio/bl654_dvk/bl654_dvk.yaml b/boards/ezurio/bl654_dvk/bl654_dvk.yaml index c43d8f4328aa..7df5cc38245f 100644 --- a/boards/ezurio/bl654_dvk/bl654_dvk.yaml +++ b/boards/ezurio/bl654_dvk/bl654_dvk.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usb_device - - ble + - bluetooth - pwm - watchdog vendor: ezurio diff --git a/boards/ezurio/bl654_dvk/bl654_dvk_nrf52840_pa.yaml b/boards/ezurio/bl654_dvk/bl654_dvk_nrf52840_pa.yaml index d38960a6eedb..5f203cb4329c 100644 --- a/boards/ezurio/bl654_dvk/bl654_dvk_nrf52840_pa.yaml +++ b/boards/ezurio/bl654_dvk/bl654_dvk_nrf52840_pa.yaml @@ -8,7 +8,7 @@ toolchain: supported: - adc - usb_device - - ble + - bluetooth - pwm - watchdog vendor: ezurio diff --git a/boards/ezurio/bl654_sensor_board/bl654_sensor_board.yaml b/boards/ezurio/bl654_sensor_board/bl654_sensor_board.yaml index 0dce5bbbe46c..19c82b0bfe49 100644 --- a/boards/ezurio/bl654_sensor_board/bl654_sensor_board.yaml +++ b/boards/ezurio/bl654_sensor_board/bl654_sensor_board.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/ezurio/bl654_usb/bl654_usb.yaml b/boards/ezurio/bl654_usb/bl654_usb.yaml index 4551f47dd61b..406b5036ca7b 100644 --- a/boards/ezurio/bl654_usb/bl654_usb.yaml +++ b/boards/ezurio/bl654_usb/bl654_usb.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - usbd - - ble + - bluetooth - pwm - watchdog - counter diff --git a/boards/ezurio/bl654_usb/bl654_usb_nrf52840_bare.yaml b/boards/ezurio/bl654_usb/bl654_usb_nrf52840_bare.yaml index e3bd3adcc9f6..74d61888d9c8 100644 --- a/boards/ezurio/bl654_usb/bl654_usb_nrf52840_bare.yaml +++ b/boards/ezurio/bl654_usb/bl654_usb_nrf52840_bare.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - usbd - - ble + - bluetooth - pwm - watchdog - counter diff --git a/boards/ezurio/bt510/bt510.yaml b/boards/ezurio/bt510/bt510.yaml index f13cd759f427..d3b260643814 100644 --- a/boards/ezurio/bt510/bt510.yaml +++ b/boards/ezurio/bt510/bt510.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - pwm - watchdog - i2c diff --git a/boards/ezurio/bt610/bt610.yaml b/boards/ezurio/bt610/bt610.yaml index c56ec7587361..44a7d4545923 100644 --- a/boards/ezurio/bt610/bt610.yaml +++ b/boards/ezurio/bt610/bt610.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - gpio - i2c - pwm diff --git a/boards/ezurio/mg100/mg100.yaml b/boards/ezurio/mg100/mg100.yaml index bbfd5f81bc71..6462d9a4c7fe 100644 --- a/boards/ezurio/mg100/mg100.yaml +++ b/boards/ezurio/mg100/mg100.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/ezurio/pinnacle_100_dvk/pinnacle_100_dvk.yaml b/boards/ezurio/pinnacle_100_dvk/pinnacle_100_dvk.yaml index 5888bc4febc6..ca4b3baecac8 100644 --- a/boards/ezurio/pinnacle_100_dvk/pinnacle_100_dvk.yaml +++ b/boards/ezurio/pinnacle_100_dvk/pinnacle_100_dvk.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/ezurio/rm1xx_dvk/rm1xx_dvk.yaml b/boards/ezurio/rm1xx_dvk/rm1xx_dvk.yaml index 2dd24375a5b7..1b32d0289df4 100644 --- a/boards/ezurio/rm1xx_dvk/rm1xx_dvk.yaml +++ b/boards/ezurio/rm1xx_dvk/rm1xx_dvk.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb ram: 32 supported: - - ble + - bluetooth - i2c - spi - lora diff --git a/boards/fobe/quill_nrf52840_mesh/quill_nrf52840_mesh.yaml b/boards/fobe/quill_nrf52840_mesh/quill_nrf52840_mesh.yaml index 14823a53fcbd..f7418d1a2f3e 100644 --- a/boards/fobe/quill_nrf52840_mesh/quill_nrf52840_mesh.yaml +++ b/boards/fobe/quill_nrf52840_mesh/quill_nrf52840_mesh.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - lora diff --git a/boards/heltec/heltec_t114_v2/heltec_t114_v2.yaml b/boards/heltec/heltec_t114_v2/heltec_t114_v2.yaml index 598971f7da4e..6094ed340600 100644 --- a/boards/heltec/heltec_t114_v2/heltec_t114_v2.yaml +++ b/boards/heltec/heltec_t114_v2/heltec_t114_v2.yaml @@ -10,7 +10,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - pwm - spi - watchdog diff --git a/boards/heltec/heltec_t114_v2/heltec_t114_v2_nrf52840_uf2.yaml b/boards/heltec/heltec_t114_v2/heltec_t114_v2_nrf52840_uf2.yaml index 2f4da84a19ed..abc716162aee 100644 --- a/boards/heltec/heltec_t114_v2/heltec_t114_v2_nrf52840_uf2.yaml +++ b/boards/heltec/heltec_t114_v2/heltec_t114_v2_nrf52840_uf2.yaml @@ -10,7 +10,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - pwm - spi - watchdog diff --git a/boards/holyiot/yj16019/holyiot_yj16019.yaml b/boards/holyiot/yj16019/holyiot_yj16019.yaml index f60d7b319a3e..79017a6b4617 100644 --- a/boards/holyiot/yj16019/holyiot_yj16019.yaml +++ b/boards/holyiot/yj16019/holyiot_yj16019.yaml @@ -6,7 +6,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - pwm - watchdog ram: 64 diff --git a/boards/holyiot/yj17095/holyiot_yj17095.yaml b/boards/holyiot/yj17095/holyiot_yj17095.yaml index 6bd95752d5a1..18e97701ed6c 100644 --- a/boards/holyiot/yj17095/holyiot_yj17095.yaml +++ b/boards/holyiot/yj17095/holyiot_yj17095.yaml @@ -6,7 +6,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - pwm - watchdog ram: 64 diff --git a/boards/makerdiary/nrf52840_mdk/nrf52840_mdk.yaml b/boards/makerdiary/nrf52840_mdk/nrf52840_mdk.yaml index 79d62b2bf329..c637f7476154 100644 --- a/boards/makerdiary/nrf52840_mdk/nrf52840_mdk.yaml +++ b/boards/makerdiary/nrf52840_mdk/nrf52840_mdk.yaml @@ -7,6 +7,6 @@ toolchain: - gnuarmemb supported: - usb_device - - ble + - bluetooth - pwm vendor: makerdiary diff --git a/boards/makerdiary/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.yaml b/boards/makerdiary/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.yaml index a26119160414..ff2eed97c7b6 100644 --- a/boards/makerdiary/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.yaml +++ b/boards/makerdiary/nrf52840_mdk_usb_dongle/nrf52840_mdk_usb_dongle.yaml @@ -9,6 +9,6 @@ toolchain: - gnuarmemb supported: - usbd - - ble + - bluetooth - watchdog - counter diff --git a/boards/mikroe/hexiwear/hexiwear_mk64f12.yaml b/boards/mikroe/hexiwear/hexiwear_mk64f12.yaml index 171032e915a7..5c34cdf5183f 100644 --- a/boards/mikroe/hexiwear/hexiwear_mk64f12.yaml +++ b/boards/mikroe/hexiwear/hexiwear_mk64f12.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - flash - gpio - i2c diff --git a/boards/nordic/nrf21540dk/nrf21540dk_nrf52840.yaml b/boards/nordic/nrf21540dk/nrf21540dk_nrf52840.yaml index d097ead5541a..40ab137669aa 100644 --- a/boards/nordic/nrf21540dk/nrf21540dk_nrf52840.yaml +++ b/boards/nordic/nrf21540dk/nrf21540dk_nrf52840.yaml @@ -11,7 +11,7 @@ supported: - adc - arduino_gpio - arduino_i2c - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/nordic/nrf51dk/nrf51dk_nrf51822.yaml b/boards/nordic/nrf51dk/nrf51dk_nrf51822.yaml index 801aa256adcc..79613715e803 100644 --- a/boards/nordic/nrf51dk/nrf51dk_nrf51822.yaml +++ b/boards/nordic/nrf51dk/nrf51dk_nrf51822.yaml @@ -9,7 +9,7 @@ ram: 32 flash: 256 supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/nordic/nrf51dongle/nrf51dongle_nrf51822.yaml b/boards/nordic/nrf51dongle/nrf51dongle_nrf51822.yaml index 9f87b6282c7f..5c20078cb3a1 100644 --- a/boards/nordic/nrf51dongle/nrf51dongle_nrf51822.yaml +++ b/boards/nordic/nrf51dongle/nrf51dongle_nrf51822.yaml @@ -8,6 +8,6 @@ toolchain: ram: 32 flash: 256 supported: - - ble + - bluetooth - nvs vendor: nordic diff --git a/boards/nordic/nrf52833dk/nrf52833dk_nrf52820.yaml b/boards/nordic/nrf52833dk/nrf52833dk_nrf52820.yaml index 682b6a0c67c6..e0ba9192cf32 100644 --- a/boards/nordic/nrf52833dk/nrf52833dk_nrf52820.yaml +++ b/boards/nordic/nrf52833dk/nrf52833dk_nrf52820.yaml @@ -10,7 +10,7 @@ flash: 256 supported: - usb_device - usbd - - ble + - bluetooth - gpio - watchdog - counter diff --git a/boards/nordic/nrf52833dk/nrf52833dk_nrf52833.yaml b/boards/nordic/nrf52833dk/nrf52833dk_nrf52833.yaml index 93d3744e2fe1..faf9f3e97292 100644 --- a/boards/nordic/nrf52833dk/nrf52833dk_nrf52833.yaml +++ b/boards/nordic/nrf52833dk/nrf52833dk_nrf52833.yaml @@ -14,7 +14,7 @@ supported: - arduino_spi - usb_device - usbd - - ble + - bluetooth - gpio - pwm - watchdog diff --git a/boards/nordic/nrf52840dk/nrf52840dk_nrf52840.yaml b/boards/nordic/nrf52840dk/nrf52840dk_nrf52840.yaml index d257234e7396..e1346f5806f3 100644 --- a/boards/nordic/nrf52840dk/nrf52840dk_nrf52840.yaml +++ b/boards/nordic/nrf52840dk/nrf52840dk_nrf52840.yaml @@ -13,7 +13,7 @@ supported: - arduino_i2c - arduino_serial - arduino_spi - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840.yaml b/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840.yaml index d12aca5fc52f..dd0bf2b8cbde 100644 --- a/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840.yaml +++ b/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840.yaml @@ -10,7 +10,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - pwm - spi - watchdog diff --git a/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840_bare.yaml b/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840_bare.yaml index 7ab56a99e46b..6a5dd58b310e 100644 --- a/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840_bare.yaml +++ b/boards/nordic/nrf52840dongle/nrf52840dongle_nrf52840_bare.yaml @@ -10,7 +10,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - pwm - spi - watchdog diff --git a/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_14_0.yaml b/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_14_0.yaml index 2219fa3b3a4c..a59bf326de0f 100644 --- a/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_14_0.yaml +++ b/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_14_0.yaml @@ -8,7 +8,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - netif:openthread - gpio vendor: nordic diff --git a/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_7_0.yaml b/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_7_0.yaml index 7613fabaed57..39026d227d03 100644 --- a/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_7_0.yaml +++ b/boards/nordic/nrf9160dk/nrf9160dk_nrf52840_0_7_0.yaml @@ -8,7 +8,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - netif:openthread - gpio vendor: nordic diff --git a/boards/nordic/thingy53/thingy53_nrf5340_cpuapp.yaml b/boards/nordic/thingy53/thingy53_nrf5340_cpuapp.yaml index ea3c467ee0de..3c30e875e8a2 100644 --- a/boards/nordic/thingy53/thingy53_nrf5340_cpuapp.yaml +++ b/boards/nordic/thingy53/thingy53_nrf5340_cpuapp.yaml @@ -8,7 +8,7 @@ toolchain: ram: 448 flash: 1024 supported: - - ble + - bluetooth - gpio - i2c - pwm diff --git a/boards/nucode/nucode_nu32/nucode_nu32_nrf52832.yaml b/boards/nucode/nucode_nu32/nucode_nu32_nrf52832.yaml index 6ab6b4b110c6..c9d1888808c5 100644 --- a/boards/nucode/nucode_nu32/nucode_nu32_nrf52832.yaml +++ b/boards/nucode/nucode_nu32/nucode_nu32_nrf52832.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - nfc diff --git a/boards/nucode/nucode_nu40/nucode_nu40_nrf52840.yaml b/boards/nucode/nucode_nu40/nucode_nu40_nrf52840.yaml index ed7171572822..38108593a28c 100644 --- a/boards/nucode/nucode_nu40/nucode_nu40_nrf52840.yaml +++ b/boards/nucode/nucode_nu40/nucode_nu40_nrf52840.yaml @@ -13,7 +13,7 @@ supported: - arduino_serial - arduino_spi - usbd - - ble + - bluetooth - i2c - pwm - spi diff --git a/boards/nucode/nucode_nu40/nucode_nu40_nrf52840_bare.yaml b/boards/nucode/nucode_nu40/nucode_nu40_nrf52840_bare.yaml index 535fc517f911..4abb9f225b82 100644 --- a/boards/nucode/nucode_nu40/nucode_nu40_nrf52840_bare.yaml +++ b/boards/nucode/nucode_nu40/nucode_nu40_nrf52840_bare.yaml @@ -13,7 +13,7 @@ supported: - arduino_serial - arduino_spi - usbd - - ble + - bluetooth - i2c - pwm - spi diff --git a/boards/others/promicro_nrf52840/promicro_nrf52840.yaml b/boards/others/promicro_nrf52840/promicro_nrf52840.yaml index ef54516b3876..e30143979b53 100644 --- a/boards/others/promicro_nrf52840/promicro_nrf52840.yaml +++ b/boards/others/promicro_nrf52840/promicro_nrf52840.yaml @@ -10,7 +10,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - pwm - spi - watchdog diff --git a/boards/others/promicro_nrf52840/promicro_nrf52840_nrf52840_uf2.yaml b/boards/others/promicro_nrf52840/promicro_nrf52840_nrf52840_uf2.yaml index f5228a763eda..7f5e80ec35fb 100644 --- a/boards/others/promicro_nrf52840/promicro_nrf52840_nrf52840_uf2.yaml +++ b/boards/others/promicro_nrf52840/promicro_nrf52840_nrf52840_uf2.yaml @@ -10,7 +10,7 @@ toolchain: supported: - adc - usbd - - ble + - bluetooth - pwm - spi - watchdog diff --git a/boards/panasonic/pan1770_evb/pan1770_evb.yaml b/boards/panasonic/pan1770_evb/pan1770_evb.yaml index a10c15ea3e10..462a1dfaed40 100644 --- a/boards/panasonic/pan1770_evb/pan1770_evb.yaml +++ b/boards/panasonic/pan1770_evb/pan1770_evb.yaml @@ -17,7 +17,7 @@ supported: - arduino_gpio - arduino_i2c - arduino_spi - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/panasonic/pan1780_evb/pan1780_evb.yaml b/boards/panasonic/pan1780_evb/pan1780_evb.yaml index 65a29e5c75fc..38faa8badc56 100644 --- a/boards/panasonic/pan1780_evb/pan1780_evb.yaml +++ b/boards/panasonic/pan1780_evb/pan1780_evb.yaml @@ -17,7 +17,7 @@ supported: - arduino_gpio - arduino_i2c - arduino_spi - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/panasonic/pan1781_evb/pan1781_evb.yaml b/boards/panasonic/pan1781_evb/pan1781_evb.yaml index 5ba7fae607fa..9222eab60ee2 100644 --- a/boards/panasonic/pan1781_evb/pan1781_evb.yaml +++ b/boards/panasonic/pan1781_evb/pan1781_evb.yaml @@ -15,7 +15,7 @@ toolchain: supported: - arduino_i2c - arduino_spi - - ble + - bluetooth - gpio - i2c - spi diff --git a/boards/panasonic/pan1782_evb/pan1782_evb.yaml b/boards/panasonic/pan1782_evb/pan1782_evb.yaml index 0574926ea72e..bc65b7fcc99e 100644 --- a/boards/panasonic/pan1782_evb/pan1782_evb.yaml +++ b/boards/panasonic/pan1782_evb/pan1782_evb.yaml @@ -17,7 +17,7 @@ supported: - arduino_i2c - arduino_spi - usb_device - - ble + - bluetooth - gpio - i2c - spi diff --git a/boards/particle/argon/particle_argon.yaml b/boards/particle/argon/particle_argon.yaml index 8ec6c888b921..04a39b2b5f0f 100644 --- a/boards/particle/argon/particle_argon.yaml +++ b/boards/particle/argon/particle_argon.yaml @@ -12,7 +12,7 @@ supported: - spi - gpio - usb_device - - ble + - bluetooth - feather_serial - feather_i2c - feather_spi diff --git a/boards/particle/boron/particle_boron.yaml b/boards/particle/boron/particle_boron.yaml index 1e2835ac09d2..1f0a84e0d262 100644 --- a/boards/particle/boron/particle_boron.yaml +++ b/boards/particle/boron/particle_boron.yaml @@ -12,7 +12,7 @@ supported: - spi - gpio - usb_device - - ble + - bluetooth - feather_serial - feather_i2c - feather_spi diff --git a/boards/particle/nrf51_blenano/nrf51_blenano.yaml b/boards/particle/nrf51_blenano/nrf51_blenano.yaml index 4d4c9c96a224..fc082001ff10 100644 --- a/boards/particle/nrf51_blenano/nrf51_blenano.yaml +++ b/boards/particle/nrf51_blenano/nrf51_blenano.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb ram: 16 supported: - - ble + - bluetooth testing: ignore_tags: - net diff --git a/boards/particle/xenon/particle_xenon.yaml b/boards/particle/xenon/particle_xenon.yaml index 0b2176965477..7420e96d2e18 100644 --- a/boards/particle/xenon/particle_xenon.yaml +++ b/boards/particle/xenon/particle_xenon.yaml @@ -17,7 +17,7 @@ supported: - spi - gpio - usb_device - - ble + - bluetooth - feather_serial - feather_i2c - feather_spi diff --git a/boards/phytec/reel_board/reel_board_1.yaml b/boards/phytec/reel_board/reel_board_1.yaml index c973f47cc79e..d13ea6b1eb1d 100644 --- a/boards/phytec/reel_board/reel_board_1.yaml +++ b/boards/phytec/reel_board/reel_board_1.yaml @@ -12,7 +12,7 @@ supported: - spi - gpio - usb_device - - ble + - bluetooth - pwm - arduino_i2c - arduino_spi diff --git a/boards/phytec/reel_board/reel_board_nrf52840_2.yaml b/boards/phytec/reel_board/reel_board_nrf52840_2.yaml index 5852cf72161b..847336e3823c 100644 --- a/boards/phytec/reel_board/reel_board_nrf52840_2.yaml +++ b/boards/phytec/reel_board/reel_board_nrf52840_2.yaml @@ -12,7 +12,7 @@ supported: - spi - gpio - usb_device - - ble + - bluetooth - pwm - arduino_i2c - arduino_spi diff --git a/boards/qorvo/decawave_dwm3001cdk/decawave_dwm3001cdk.yaml b/boards/qorvo/decawave_dwm3001cdk/decawave_dwm3001cdk.yaml index da6ecebeea31..f1a690acc40d 100644 --- a/boards/qorvo/decawave_dwm3001cdk/decawave_dwm3001cdk.yaml +++ b/boards/qorvo/decawave_dwm3001cdk/decawave_dwm3001cdk.yaml @@ -11,7 +11,7 @@ toolchain: supported: - adc - usb_device - - ble + - bluetooth - gpio - pwm - watchdog diff --git a/boards/rakwireless/rak4631/rak4631_nrf52840.yaml b/boards/rakwireless/rak4631/rak4631_nrf52840.yaml index 342b30b8f7fe..d5d94f9673ff 100644 --- a/boards/rakwireless/rak4631/rak4631_nrf52840.yaml +++ b/boards/rakwireless/rak4631/rak4631_nrf52840.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/rakwireless/rak5010/rak5010_nrf52840.yaml b/boards/rakwireless/rak5010/rak5010_nrf52840.yaml index 41c9a3e37893..7e51df217a70 100644 --- a/boards/rakwireless/rak5010/rak5010_nrf52840.yaml +++ b/boards/rakwireless/rak5010/rak5010_nrf52840.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/raytac/mdbt50q_cx_40_dongle/raytac_mdbt50q_cx_40_dongle_nrf52840.yaml b/boards/raytac/mdbt50q_cx_40_dongle/raytac_mdbt50q_cx_40_dongle_nrf52840.yaml index d5ecd05ff73e..13148c5fe5e9 100644 --- a/boards/raytac/mdbt50q_cx_40_dongle/raytac_mdbt50q_cx_40_dongle_nrf52840.yaml +++ b/boards/raytac/mdbt50q_cx_40_dongle/raytac_mdbt50q_cx_40_dongle_nrf52840.yaml @@ -13,7 +13,7 @@ toolchain: - gnuarmemb supported: - usbd - - ble + - bluetooth - pwm - watchdog - counter diff --git a/boards/raytac/mdbt50q_db_33/raytac_mdbt50q_db_33_nrf52833.yaml b/boards/raytac/mdbt50q_db_33/raytac_mdbt50q_db_33_nrf52833.yaml index fa8c20d01633..4ea1e13c1322 100644 --- a/boards/raytac/mdbt50q_db_33/raytac_mdbt50q_db_33_nrf52833.yaml +++ b/boards/raytac/mdbt50q_db_33/raytac_mdbt50q_db_33_nrf52833.yaml @@ -12,7 +12,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/raytac/mdbt50q_db_40/raytac_mdbt50q_db_40_nrf52840.yaml b/boards/raytac/mdbt50q_db_40/raytac_mdbt50q_db_40_nrf52840.yaml index 4a1e5fd20de7..519db4859109 100644 --- a/boards/raytac/mdbt50q_db_40/raytac_mdbt50q_db_40_nrf52840.yaml +++ b/boards/raytac/mdbt50q_db_40/raytac_mdbt50q_db_40_nrf52840.yaml @@ -14,7 +14,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/ruuvi/ruuvitag/ruuvi_ruuvitag.yaml b/boards/ruuvi/ruuvitag/ruuvi_ruuvitag.yaml index 3331bd179052..27152f5be473 100644 --- a/boards/ruuvi/ruuvitag/ruuvi_ruuvitag.yaml +++ b/boards/ruuvi/ruuvitag/ruuvi_ruuvitag.yaml @@ -8,7 +8,7 @@ toolchain: ram: 64 flash: 512 supported: - - ble + - bluetooth - adc - gpio - spi diff --git a/boards/seeed/wio_tracker_l1/wio_tracker_l1.yaml b/boards/seeed/wio_tracker_l1/wio_tracker_l1.yaml index c7520086261a..39f1065f59c9 100644 --- a/boards/seeed/wio_tracker_l1/wio_tracker_l1.yaml +++ b/boards/seeed/wio_tracker_l1/wio_tracker_l1.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - display - flash diff --git a/boards/seeed/xiao_ble/xiao_ble.yaml b/boards/seeed/xiao_ble/xiao_ble.yaml index fea7a4fa769f..c781431b987d 100644 --- a/boards/seeed/xiao_ble/xiao_ble.yaml +++ b/boards/seeed/xiao_ble/xiao_ble.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/seeed/xiao_ble/xiao_ble_nrf52840_sense.yaml b/boards/seeed/xiao_ble/xiao_ble_nrf52840_sense.yaml index 7edb8000612d..ecd93dc62085 100644 --- a/boards/seeed/xiao_ble/xiao_ble_nrf52840_sense.yaml +++ b/boards/seeed/xiao_ble/xiao_ble_nrf52840_sense.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/sparkfun/micromod/micromod_nrf52840.yaml b/boards/sparkfun/micromod/micromod_nrf52840.yaml index d1cc5c241b5b..8f05662246f9 100644 --- a/boards/sparkfun/micromod/micromod_nrf52840.yaml +++ b/boards/sparkfun/micromod/micromod_nrf52840.yaml @@ -8,7 +8,7 @@ toolchain: - zephyr - gnuarmemb supported: - - ble + - bluetooth - gpio - spi - qspi diff --git a/boards/st/b_l4s5i_iot01a/b_l4s5i_iot01a.yaml b/boards/st/b_l4s5i_iot01a/b_l4s5i_iot01a.yaml index daaa2bd2f40a..4c9efb591bd9 100644 --- a/boards/st/b_l4s5i_iot01a/b_l4s5i_iot01a.yaml +++ b/boards/st/b_l4s5i_iot01a/b_l4s5i_iot01a.yaml @@ -16,7 +16,7 @@ supported: - lsm6dsl - pwm - gpio - - ble + - bluetooth - spi - vl53l0x - watchdog diff --git a/boards/st/disco_l475_iot1/disco_l475_iot1.yaml b/boards/st/disco_l475_iot1/disco_l475_iot1.yaml index c40ceae3c547..83392db4a68c 100644 --- a/boards/st/disco_l475_iot1/disco_l475_iot1.yaml +++ b/boards/st/disco_l475_iot1/disco_l475_iot1.yaml @@ -9,7 +9,7 @@ supported: - adc - arduino_gpio - arduino_i2c - - ble + - bluetooth - counter - crc - dac diff --git a/boards/st/sensortile_box/sensortile_box.yaml b/boards/st/sensortile_box/sensortile_box.yaml index 6519278fba05..44fce36d3b65 100644 --- a/boards/st/sensortile_box/sensortile_box.yaml +++ b/boards/st/sensortile_box/sensortile_box.yaml @@ -8,7 +8,7 @@ toolchain: supported: - pwm - spi - - ble + - bluetooth - i2c - gpio - usbd diff --git a/boards/st/sensortile_box_pro/sensortile_box_pro.yaml b/boards/st/sensortile_box_pro/sensortile_box_pro.yaml index 6778e52c71e5..ca7769a485ac 100644 --- a/boards/st/sensortile_box_pro/sensortile_box_pro.yaml +++ b/boards/st/sensortile_box_pro/sensortile_box_pro.yaml @@ -8,7 +8,7 @@ toolchain: supported: - pwm - spi - - ble + - bluetooth - i2c - gpio - usbd diff --git a/boards/st/steval_stwinbx1/steval_stwinbx1.yaml b/boards/st/steval_stwinbx1/steval_stwinbx1.yaml index 26577289f538..6d68af09ab21 100644 --- a/boards/st/steval_stwinbx1/steval_stwinbx1.yaml +++ b/boards/st/steval_stwinbx1/steval_stwinbx1.yaml @@ -12,5 +12,5 @@ supported: - gpio - pwm - watchdog - - ble + - bluetooth vendor: st diff --git a/boards/st/stm32l562e_dk/stm32l562e_dk.yaml b/boards/st/stm32l562e_dk/stm32l562e_dk.yaml index c4ca40922283..49fea92534b4 100644 --- a/boards/st/stm32l562e_dk/stm32l562e_dk.yaml +++ b/boards/st/stm32l562e_dk/stm32l562e_dk.yaml @@ -9,7 +9,7 @@ supported: - adc - arduino_gpio - arduino_spi - - ble + - bluetooth - counter - crc - dac diff --git a/boards/u-blox/ubx_bmd340eval/ubx_bmd340eval_nrf52840.yaml b/boards/u-blox/ubx_bmd340eval/ubx_bmd340eval_nrf52840.yaml index cacac6dc2b33..64b395f97112 100644 --- a/boards/u-blox/ubx_bmd340eval/ubx_bmd340eval_nrf52840.yaml +++ b/boards/u-blox/ubx_bmd340eval/ubx_bmd340eval_nrf52840.yaml @@ -12,7 +12,7 @@ supported: - arduino_gpio - arduino_i2c - arduino_spi - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/u-blox/ubx_bmd345eval/ubx_bmd345eval_nrf52840.yaml b/boards/u-blox/ubx_bmd345eval/ubx_bmd345eval_nrf52840.yaml index ca5ec5224640..b1eb30f835b5 100644 --- a/boards/u-blox/ubx_bmd345eval/ubx_bmd345eval_nrf52840.yaml +++ b/boards/u-blox/ubx_bmd345eval/ubx_bmd345eval_nrf52840.yaml @@ -11,7 +11,7 @@ supported: - adc - arduino_i2c - arduino_spi - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/u-blox/ubx_bmd380eval/ubx_bmd380eval_nrf52840.yaml b/boards/u-blox/ubx_bmd380eval/ubx_bmd380eval_nrf52840.yaml index 31e23654cef5..7b6ad2e72bf8 100644 --- a/boards/u-blox/ubx_bmd380eval/ubx_bmd380eval_nrf52840.yaml +++ b/boards/u-blox/ubx_bmd380eval/ubx_bmd380eval_nrf52840.yaml @@ -9,7 +9,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/u-blox/ubx_evkninab3/ubx_evkninab3_nrf52840.yaml b/boards/u-blox/ubx_evkninab3/ubx_evkninab3_nrf52840.yaml index 8aad52f8b229..8c7cf7c9e96c 100644 --- a/boards/u-blox/ubx_evkninab3/ubx_evkninab3_nrf52840.yaml +++ b/boards/u-blox/ubx_evkninab3/ubx_evkninab3_nrf52840.yaml @@ -11,7 +11,7 @@ supported: - adc - arduino_gpio - arduino_i2c - - ble + - bluetooth - counter - gpio - i2c diff --git a/boards/u-blox/ubx_evkninab4/ubx_evkninab4_nrf52833.yaml b/boards/u-blox/ubx_evkninab4/ubx_evkninab4_nrf52833.yaml index a89676bfefa3..418ec276ae07 100644 --- a/boards/u-blox/ubx_evkninab4/ubx_evkninab4_nrf52833.yaml +++ b/boards/u-blox/ubx_evkninab4/ubx_evkninab4_nrf52833.yaml @@ -11,7 +11,7 @@ supported: - arduino_i2c - arduino_spi - usb_device - - ble + - bluetooth - gpio - pwm - watchdog diff --git a/boards/waveshare/nrf51_ble400/nrf51_ble400.yaml b/boards/waveshare/nrf51_ble400/nrf51_ble400.yaml index 681d32ac5325..20fbfe569fe7 100644 --- a/boards/waveshare/nrf51_ble400/nrf51_ble400.yaml +++ b/boards/waveshare/nrf51_ble400/nrf51_ble400.yaml @@ -7,7 +7,7 @@ toolchain: - gnuarmemb ram: 32 supported: - - ble + - bluetooth - gpio - i2c testing: diff --git a/boards/we/proteus3ev/we_proteus3ev_nrf52840.yaml b/boards/we/proteus3ev/we_proteus3ev_nrf52840.yaml index 2467fb427959..a6b73e8da913 100644 --- a/boards/we/proteus3ev/we_proteus3ev_nrf52840.yaml +++ b/boards/we/proteus3ev/we_proteus3ev_nrf52840.yaml @@ -12,7 +12,7 @@ toolchain: - gnuarmemb supported: - adc - - ble + - bluetooth - gpio - i2c - spi From e291aa1b94fd981f95d1e915574ec71bf7c00117 Mon Sep 17 00:00:00 2001 From: Al Semjonovs Date: Fri, 21 Aug 2026 11:06:23 -0600 Subject: [PATCH 170/455] ec_host_cmd: initialize thread field in context Update struct ec_host_cmd to unconditionally store the k_tid_t of the thread executing host commands. When running with a dedicated thread, store the k_tid_t returned by k_thread_create. When running via ec_host_cmd_task, record k_current_get() upon entry. This allows consumers to consistently query the host command thread via the ec_host_cmd context regardless of the threading configuration. Signed-off-by: Al Semjonovs --- include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h | 5 ++--- subsys/mgmt/ec_host_cmd/ec_host_cmd_handler.c | 11 +++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h b/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h index 0516e9a3b5f7..e373bebb8188 100644 --- a/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h +++ b/include/zephyr/mgmt/ec_host_cmd/ec_host_cmd.h @@ -151,9 +151,8 @@ struct ec_host_cmd { void *user_data; /** Current state of the host command handler. */ enum ec_host_cmd_state state; -#ifdef CONFIG_EC_HOST_CMD_DEDICATED_THREAD - struct k_thread thread; -#endif /* CONFIG_EC_HOST_CMD_DEDICATED_THREAD */ + /** Thread running the host command handler loop. */ + k_tid_t thread; }; /** diff --git a/subsys/mgmt/ec_host_cmd/ec_host_cmd_handler.c b/subsys/mgmt/ec_host_cmd/ec_host_cmd_handler.c index 7357d418a267..453c919553dd 100644 --- a/subsys/mgmt/ec_host_cmd/ec_host_cmd_handler.c +++ b/subsys/mgmt/ec_host_cmd/ec_host_cmd_handler.c @@ -50,6 +50,7 @@ COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF, #ifdef CONFIG_EC_HOST_CMD_DEDICATED_THREAD static K_KERNEL_STACK_DEFINE(hc_stack, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE); +static struct k_thread hc_thread; #endif /* CONFIG_EC_HOST_CMD_DEDICATED_THREAD */ static struct ec_host_cmd ec_host_cmd = { @@ -464,6 +465,7 @@ FUNC_NORETURN static void ec_host_cmd_thread(void *hc_handle, void *arg2, void * #ifndef CONFIG_EC_HOST_CMD_DEDICATED_THREAD FUNC_NORETURN void ec_host_cmd_task(void) { + ec_host_cmd.thread = k_current_get(); ec_host_cmd_thread(&ec_host_cmd, NULL, NULL); } #endif @@ -518,10 +520,11 @@ int ec_host_cmd_init(struct ec_host_cmd_backend *backend) } #ifdef CONFIG_EC_HOST_CMD_DEDICATED_THREAD - k_thread_create(&hc->thread, hc_stack, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE, - ec_host_cmd_thread, (void *)hc, NULL, NULL, CONFIG_EC_HOST_CMD_HANDLER_PRIO, - 0, K_NO_WAIT); - k_thread_name_set(&hc->thread, "ec_host_cmd"); + hc->thread = k_thread_create(&hc_thread, hc_stack, + CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE, + ec_host_cmd_thread, (void *)hc, NULL, NULL, + CONFIG_EC_HOST_CMD_HANDLER_PRIO, 0, K_NO_WAIT); + k_thread_name_set(hc->thread, "ec_host_cmd"); #endif /* CONFIG_EC_HOST_CMD_DEDICATED_THREAD */ return 0; From 1c64203a61a4098b2449322fc8b9d1d09d75938c Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 11:14:35 +1000 Subject: [PATCH 171/455] modem: cellular: handle modem busy in periodic script If the periodic script failed to start, reschedule the timer so it doesn't permanently stop. Signed-off-by: Jordan Yates --- drivers/modem/modem_cellular.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/modem/modem_cellular.c b/drivers/modem/modem_cellular.c index 3bcb606b1314..44b6bdc86a41 100644 --- a/drivers/modem/modem_cellular.c +++ b/drivers/modem/modem_cellular.c @@ -1801,6 +1801,7 @@ static void modem_cellular_registered_event_handler(struct modem_cellular_data * { const struct modem_cellular_config *config = data->dev->config; struct cellular_evt_modem_comms_check_result result; + int ret; switch (evt) { case MODEM_CELLULAR_EVENT_SCRIPT_SUCCESS: @@ -1832,7 +1833,12 @@ static void modem_cellular_registered_event_handler(struct modem_cellular_data * data->periodic_timeout_skipped = true; break; } - modem_chat_run_script_async(&data->chat, config->vendor->scripts.periodic); + ret = modem_chat_run_script_async(&data->chat, config->vendor->scripts.periodic); + if (ret < 0) { + LOG_WRN("periodic %s %s, rearming timer", "timer", + ret == -EBUSY ? "busy" : "failed"); + modem_cellular_start_timer(data, MODEM_CELLULAR_PERIODIC_SCRIPT_TIMEOUT); + } break; case MODEM_CELLULAR_EVENT_PERIODIC_KICK: @@ -1844,9 +1850,10 @@ static void modem_cellular_registered_event_handler(struct modem_cellular_data * break; } data->periodic_timeout_skipped = false; - if (modem_chat_run_script_async(&data->chat, config->vendor->scripts.periodic) < - 0) { - LOG_WRN("periodic kick busy, rearming timer"); + ret = modem_chat_run_script_async(&data->chat, config->vendor->scripts.periodic); + if (ret < 0) { + LOG_WRN("periodic %s %s, rearming timer", "kick", + ret == -EBUSY ? "busy" : "failed"); modem_cellular_start_timer(data, MODEM_CELLULAR_PERIODIC_SCRIPT_TIMEOUT); } break; From 23332d44a508d95c6394ceda685252d4db400951 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 10 Aug 2026 16:03:58 +0200 Subject: [PATCH 172/455] drivers: i2c: stm32: Factorize i2c_stm32_runtime_configure() Factorize i2c_stm32_runtime_configure() between STM32 I2C non-RTIO and RTIO drivers that implements the same sequence but regarding SMBus support that is not yet available in the RTIO driver. No functional changes. Signed-off-by: Etienne Carriere --- drivers/i2c/i2c_stm32.c | 59 ------------------------------- drivers/i2c/i2c_stm32_common.c | 64 ++++++++++++++++++++++++++++++++++ drivers/i2c/i2c_stm32_rtio.c | 52 --------------------------- 3 files changed, 64 insertions(+), 111 deletions(-) diff --git a/drivers/i2c/i2c_stm32.c b/drivers/i2c/i2c_stm32.c index f799de68cbaa..0338e9ba54fa 100644 --- a/drivers/i2c/i2c_stm32.c +++ b/drivers/i2c/i2c_stm32.c @@ -61,65 +61,6 @@ int i2c_stm32_get_config(const struct device *dev, uint32_t *config) return 0; } -int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config) -{ - const struct i2c_stm32_config *cfg = dev->config; - struct i2c_stm32_data *data = dev->data; - const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); - I2C_TypeDef *i2c = cfg->i2c; - uint32_t i2c_clock = 0U; - int ret; - - __ASSERT(k_sem_count_get(&data->bus_mutex) == 0, "Bus is not locked"); - - if (cfg->pclk_len > 1) { - if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[1], - &i2c_clock) < 0) { - LOG_ERR("Failed call clock_control_get_rate(pclken[1])"); - return -EIO; - } - } else { - if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[0], - &i2c_clock) < 0) { - LOG_ERR("Failed call clock_control_get_rate(pclken[0])"); - return -EIO; - } - } - - data->dev_config = config; - -#ifdef CONFIG_PM_DEVICE_RUNTIME - ret = clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken[0]); - if (ret < 0) { - LOG_ERR("failure Enabling I2C clock"); - return ret; - } -#endif - - LL_I2C_Disable(i2c); -#if defined(I2C_CR1_SMBUS) || defined(I2C_CR1_SMBDEN) || defined(I2C_CR1_SMBHEN) - i2c_stm32_set_smbus_mode(dev, data->mode); -#endif - ret = i2c_stm32_configure_timing(dev, i2c_clock); - if (ret < 0) { - return ret; - } - - if (data->smbalert_active) { - LL_I2C_Enable(i2c); - } - -#ifdef CONFIG_PM_DEVICE_RUNTIME - ret = clock_control_off(clk, (clock_control_subsys_t)&cfg->pclken[0]); - if (ret < 0) { - LOG_ERR("failure disabling I2C clock"); - return ret; - } -#endif - - return 0; -} - #define OPERATION(msg) (((struct i2c_msg *) msg)->flags & I2C_MSG_RW_MASK) static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msg, diff --git a/drivers/i2c/i2c_stm32_common.c b/drivers/i2c/i2c_stm32_common.c index 771f2e6ac321..601754623c90 100644 --- a/drivers/i2c/i2c_stm32_common.c +++ b/drivers/i2c/i2c_stm32_common.c @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef CONFIG_I2C_STM32_BUS_RECOVERY #include "i2c_bitbang.h" @@ -189,6 +190,69 @@ void i2c_stm32_pm_put(const struct device *dev) (void)pm_device_runtime_put(dev); } +int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config) +{ + const struct i2c_stm32_config *cfg = dev->config; + struct i2c_stm32_data *data = dev->data; + const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + I2C_TypeDef *i2c = cfg->i2c; + uint32_t i2c_clock = 0U; + int ret; + + if (cfg->pclk_len > 1) { + if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[1], + &i2c_clock) < 0) { + LOG_ERR("Failed call clock_control_get_rate(pclken[1])"); + return -EIO; + } + } else { + if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[0], + &i2c_clock) < 0) { + LOG_ERR("Failed call clock_control_get_rate(pclken[0])"); + return -EIO; + } + } + + data->dev_config = config; + +#ifdef CONFIG_PM_DEVICE_RUNTIME + ret = clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken[0]); + if (ret < 0) { + LOG_ERR("failure Enabling I2C clock"); + return ret; + } +#endif + + LL_I2C_Disable(i2c); + +#ifndef CONFIG_I2C_RTIO +#if defined(I2C_CR1_SMBUS) || defined(I2C_CR1_SMBDEN) || defined(I2C_CR1_SMBHEN) + i2c_stm32_set_smbus_mode(dev, data->mode); +#endif +#endif /* CONFIG_I2C_RTIO */ + + ret = i2c_stm32_configure_timing(dev, i2c_clock); + if (ret < 0) { + return ret; + } + +#ifndef CONFIG_I2C_RTIO + if (data->smbalert_active) { + LL_I2C_Enable(i2c); + } +#endif /* CONFIG_I2C_RTIO */ + +#ifdef CONFIG_PM_DEVICE_RUNTIME + ret = clock_control_off(clk, (clock_control_subsys_t)&cfg->pclken[0]); + if (ret < 0) { + LOG_ERR("failure disabling I2C clock"); + return ret; + } +#endif + + return 0; +} + #ifdef CONFIG_I2C_STM32_BUS_RECOVERY static void i2c_stm32_bitbang_set_scl(void *io_context, int state) { diff --git a/drivers/i2c/i2c_stm32_rtio.c b/drivers/i2c/i2c_stm32_rtio.c index 18be3003fc9c..83050d3aeffb 100644 --- a/drivers/i2c/i2c_stm32_rtio.c +++ b/drivers/i2c/i2c_stm32_rtio.c @@ -27,58 +27,6 @@ LOG_MODULE_REGISTER(i2c_ll_stm32_rtio); #include "i2c_stm32.h" #include "i2c-priv.h" - -int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config) -{ - const struct i2c_stm32_config *cfg = dev->config; - struct i2c_stm32_data *data = dev->data; - const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); - I2C_TypeDef *i2c = cfg->i2c; - uint32_t i2c_clock = 0U; - int ret; - - if (cfg->pclk_len > 1) { - if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[1], - &i2c_clock) < 0) { - LOG_ERR("Failed call clock_control_get_rate(pclken[1])"); - return -EIO; - } - } else { - if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[0], - &i2c_clock) < 0) { - LOG_ERR("Failed call clock_control_get_rate(pclken[0])"); - return -EIO; - } - } - - data->dev_config = config; - -#ifdef CONFIG_PM_DEVICE_RUNTIME - ret = clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken[0]); - if (ret < 0) { - LOG_ERR("Failed enabling I2C clock"); - return ret; - } -#endif - - LL_I2C_Disable(i2c); - ret = i2c_stm32_configure_timing(dev, i2c_clock); - if (ret < 0) { - LOG_ERR("Failed configuring I2C timing"); - return ret; - } - -#ifdef CONFIG_PM_DEVICE_RUNTIME - ret = clock_control_off(clk, (clock_control_subsys_t)&cfg->pclken[0]); - if (ret < 0) { - LOG_ERR("Failed disabling I2C clock"); - return ret; - } -#endif - - return ret; -} - static bool i2c_stm32_start(const struct device *dev, int *status) { struct i2c_stm32_data *data = dev->data; From 2e4a03ff907e2c2dde9e994c3d88233c0f78b8e0 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 10 Aug 2026 16:56:35 +0200 Subject: [PATCH 173/455] drivers: i2c: stm32: Consistency in header file inclusions Reorder header files in STM32 I2C drivers to group Zephyr generic header files together. For that purpose, remove definition of LOG_LEVEL macro and provide the log level config as LOG_MODULE_REGISTER() argument which use is moved after header files inclusions. Remove #ifdef directives that are not really useful around #include directives. Remove several #include directives from local i2c_stm32.h file. No functional changes. Signed-off-by: Etienne Carriere --- drivers/i2c/i2c_stm32.c | 15 +++++++-------- drivers/i2c/i2c_stm32.h | 12 +++--------- drivers/i2c/i2c_stm32_common.c | 8 +++----- drivers/i2c/i2c_stm32_rtio.c | 14 +++++++------- drivers/i2c/i2c_stm32_v1.c | 14 ++++++++------ drivers/i2c/i2c_stm32_v1_rtio.c | 17 +++++++++-------- drivers/i2c/i2c_stm32_v2.c | 25 +++++++++++++------------ drivers/i2c/i2c_stm32_v2_rtio.c | 19 ++++++++++--------- 8 files changed, 60 insertions(+), 64 deletions(-) diff --git a/drivers/i2c/i2c_stm32.c b/drivers/i2c/i2c_stm32.c index 0338e9ba54fa..6beda60d856e 100644 --- a/drivers/i2c/i2c_stm32.c +++ b/drivers/i2c/i2c_stm32.c @@ -7,25 +7,24 @@ #include #include +#include +#include +#include +#include +#include #include #include #include -#include + #include #include #include #include -#include -#include -#include - -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_stm32); #include "i2c_stm32.h" #include "i2c-priv.h" +LOG_MODULE_REGISTER(i2c_stm32, CONFIG_I2C_LOG_LEVEL); int i2c_stm32_get_config(const struct device *dev, uint32_t *config) { diff --git a/drivers/i2c/i2c_stm32.h b/drivers/i2c/i2c_stm32.h index 2ae7c59d9bb0..4d22e1d35f7c 100644 --- a/drivers/i2c/i2c_stm32.h +++ b/drivers/i2c/i2c_stm32.h @@ -9,18 +9,12 @@ #ifndef ZEPHYR_DRIVERS_I2C_I2C_STM32_H_ #define ZEPHYR_DRIVERS_I2C_I2C_STM32_H_ +#include +#include +#include #include #include -#include #include -#include -#include - -#ifdef CONFIG_I2C_STM32_BUS_RECOVERY -#include -#endif /* CONFIG_I2C_STM32_BUS_RECOVERY */ - -#include typedef void (*irq_config_func_t)(const struct device *port); diff --git a/drivers/i2c/i2c_stm32_common.c b/drivers/i2c/i2c_stm32_common.c index 601754623c90..e54090688b96 100644 --- a/drivers/i2c/i2c_stm32_common.c +++ b/drivers/i2c/i2c_stm32_common.c @@ -14,19 +14,17 @@ #include #include #include +#include #include #include + #include #include -#ifdef CONFIG_I2C_STM32_BUS_RECOVERY #include "i2c_bitbang.h" #include "i2c-priv.h" -#endif /* CONFIG_I2C_STM32_BUS_RECOVERY */ -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_ll_stm32_common); +LOG_MODULE_REGISTER(i2c_ll_stm32_common, CONFIG_I2C_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_i2c_v2) #define DT_DRV_COMPAT st_stm32_i2c_v2 diff --git a/drivers/i2c/i2c_stm32_rtio.c b/drivers/i2c/i2c_stm32_rtio.c index 83050d3aeffb..79da95ef4f8e 100644 --- a/drivers/i2c/i2c_stm32_rtio.c +++ b/drivers/i2c/i2c_stm32_rtio.c @@ -4,29 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include -#include #include #include #include #include #include #include +#include #include #include #include #include #include -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_ll_stm32_rtio); +#include +#include +#include +#include #include "i2c_stm32.h" #include "i2c-priv.h" +LOG_MODULE_REGISTER(i2c_ll_stm32_rtio, CONFIG_I2C_LOG_LEVEL); + static bool i2c_stm32_start(const struct device *dev, int *status) { struct i2c_stm32_data *data = dev->data; diff --git a/drivers/i2c/i2c_stm32_v1.c b/drivers/i2c/i2c_stm32_v1.c index 5d389b72ef70..5ac9438e77ad 100644 --- a/drivers/i2c/i2c_stm32_v1.c +++ b/drivers/i2c/i2c_stm32_v1.c @@ -10,21 +10,23 @@ #include #include -#include +#include #include +#include +#include +#include + #include #include #include -#include -#include -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_ll_stm32_v1); +#include #include "i2c_stm32.h" #include "i2c-priv.h" +LOG_MODULE_REGISTER(i2c_ll_stm32_v1, CONFIG_I2C_LOG_LEVEL); + #define I2C_STM32_TIMEOUT_USEC 1000 #define I2C_REQUEST_WRITE 0x00 #define I2C_REQUEST_READ 0x01 diff --git a/drivers/i2c/i2c_stm32_v1_rtio.c b/drivers/i2c/i2c_stm32_v1_rtio.c index 50008c6be621..183da9e4c163 100644 --- a/drivers/i2c/i2c_stm32_v1_rtio.c +++ b/drivers/i2c/i2c_stm32_v1_rtio.c @@ -4,28 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include -#include -#include #include #include #include #include #include #include +#include #include #include #include -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_ll_stm32_v1_rtio); +#include +#include +#include +#include + +#include #include "i2c_stm32.h" #include "i2c-priv.h" +LOG_MODULE_REGISTER(i2c_ll_stm32_v1_rtio, CONFIG_I2C_LOG_LEVEL); + #define I2C_REQUEST_WRITE 0x00 #define I2C_REQUEST_READ 0x01 #define HEADER 0xF0 diff --git a/drivers/i2c/i2c_stm32_v2.c b/drivers/i2c/i2c_stm32_v2.c index 0264390d3f9d..99196778fe62 100644 --- a/drivers/i2c/i2c_stm32_v2.c +++ b/drivers/i2c/i2c_stm32_v2.c @@ -9,30 +9,31 @@ * */ +#include #include #include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include + #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_ll_stm32_v2); +#include #include "i2c_stm32.h" #include "i2c-priv.h" +LOG_MODULE_REGISTER(i2c_ll_stm32_v2, CONFIG_I2C_LOG_LEVEL); + #if CONFIG_STM32_HAL2 #define STM32_I2C_CONVERT_TIMINGS(prescaler, setup_time, hold_time, sclh_period, scll_period) \ LL_I2C_CONVERT_TIMINGS(prescaler, setup_time, hold_time, sclh_period, scll_period) diff --git a/drivers/i2c/i2c_stm32_v2_rtio.c b/drivers/i2c/i2c_stm32_v2_rtio.c index 2e28782d8bf5..adfc86d8b0e1 100644 --- a/drivers/i2c/i2c_stm32_v2_rtio.c +++ b/drivers/i2c/i2c_stm32_v2_rtio.c @@ -6,29 +6,30 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include -#include -#include +#include #include #include #include #include #include -#include #include +#include #include #include #include -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include -LOG_MODULE_REGISTER(i2c_ll_stm32_v2_rtio); +#include +#include +#include +#include + +#include #include "i2c_stm32.h" #include "i2c-priv.h" +LOG_MODULE_REGISTER(i2c_ll_stm32_v2_rtio, CONFIG_I2C_LOG_LEVEL); + #if CONFIG_STM32_HAL2 #define STM32_I2C_CONVERT_TIMINGS(prescaler, setup_time, hold_time, sclh_period, scll_period) \ LL_I2C_CONVERT_TIMINGS(prescaler, setup_time, hold_time, sclh_period, scll_period) From fc1d74c2d1dcccc3c264c85850756522a487e80b Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 10 Aug 2026 16:58:38 +0200 Subject: [PATCH 174/455] drivers: i2c: stm32: Consistency helper macros definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Indent value of I2C_STM32_IRQ_HANDLER_DECL() and I2C_STM32_IRQ_HANDLER() macros for consistency inside the Zepĥyr source tree. Add __unused attribute to argument of the generated i2c_stm32_irq_config_func_##index() functions. While at it, replace tabulation with a space char after #endif closing guard in i2c_stm32.h and split some helper macros definitions with empty lines to ease readability. No functional changes. Signed-off-by: Etienne Carriere --- drivers/i2c/i2c_stm32.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/i2c_stm32.h b/drivers/i2c/i2c_stm32.h index 4d22e1d35f7c..4184f3c1b01f 100644 --- a/drivers/i2c/i2c_stm32.h +++ b/drivers/i2c/i2c_stm32.h @@ -223,14 +223,16 @@ void i2c_stm32_error_isr(void *arg); #endif /* CONFIG_I2C_STM32_COMBINED_INTERRUPT */ #define I2C_STM32_IRQ_HANDLER_DECL(index) \ -static void i2c_stm32_irq_config_func_##index(const struct device *dev) + static void i2c_stm32_irq_config_func_##index(const struct device *dev) + #define I2C_STM32_IRQ_HANDLER_FUNCTION(index) \ .irq_config_func = i2c_stm32_irq_config_func_##index, + #define I2C_STM32_IRQ_HANDLER(index) \ -static void i2c_stm32_irq_config_func_##index(const struct device *dev) \ -{ \ - I2C_STM32_IRQ_CONNECT_AND_ENABLE(index); \ -} + static void i2c_stm32_irq_config_func_##index(const struct device *dev __unused) \ + { \ + I2C_STM32_IRQ_CONNECT_AND_ENABLE(index); \ + } #else /* CONFIG_I2C_STM32_INTERRUPT */ #define I2C_STM32_IRQ_HANDLER_DECL(index) @@ -238,4 +240,4 @@ static void i2c_stm32_irq_config_func_##index(const struct device *dev) \ #define I2C_STM32_IRQ_HANDLER(index) #endif /* CONFIG_I2C_STM32_INTERRUPT */ -#endif /* ZEPHYR_DRIVERS_I2C_I2C_STM32_H_ */ +#endif /* ZEPHYR_DRIVERS_I2C_I2C_STM32_H_ */ From b90926e219108ae4280960b3988fb51025ec1b3f Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 10 Aug 2026 16:05:24 +0200 Subject: [PATCH 175/455] drivers: i2c: stm32: Remove local OPERATION() local macro Remove local OPERATION() in STM32 I2C non-RTIO and RTIO drivers. Testing I2C message flags explicitly is enough. No functional changes. Signed-off-by: Etienne Carriere --- drivers/i2c/i2c_stm32.c | 4 +--- drivers/i2c/i2c_stm32_rtio.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/i2c_stm32.c b/drivers/i2c/i2c_stm32.c index 6beda60d856e..b702178a78a8 100644 --- a/drivers/i2c/i2c_stm32.c +++ b/drivers/i2c/i2c_stm32.c @@ -60,8 +60,6 @@ int i2c_stm32_get_config(const struct device *dev, uint32_t *config) return 0; } -#define OPERATION(msg) (((struct i2c_msg *) msg)->flags & I2C_MSG_RW_MASK) - static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msg, uint8_t num_msgs, uint16_t target) { @@ -90,7 +88,7 @@ static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msg, * Restart condition between messages * of different directions is required */ - if (OPERATION(current) != OPERATION(next)) { + if ((current->flags & I2C_MSG_RW_MASK) != (next->flags & I2C_MSG_RW_MASK)) { if (!(next->flags & I2C_MSG_RESTART)) { ret = -EINVAL; break; diff --git a/drivers/i2c/i2c_stm32_rtio.c b/drivers/i2c/i2c_stm32_rtio.c index 79da95ef4f8e..a447603990d8 100644 --- a/drivers/i2c/i2c_stm32_rtio.c +++ b/drivers/i2c/i2c_stm32_rtio.c @@ -108,8 +108,6 @@ static int i2c_stm32_configure(const struct device *dev, return i2c_rtio_configure(ctx, dev_config_raw); } -#define OPERATION(msg) ((msg)->flags & I2C_MSG_RW_MASK) - static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { @@ -142,7 +140,7 @@ static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msgs, } #endif - if ((OPERATION(msgs + n - 1) != OPERATION(msgs + n)) && + if (((msgs[n].flags & I2C_MSG_RW_MASK) != (msgs[n - 1].flags & I2C_MSG_RW_MASK)) && ((msgs[n].flags & I2C_MSG_RESTART) == 0U)) { LOG_ERR("Missing restart flag between message of different directions"); return -EINVAL; From 4b887f9886d6fceb881942424db1f3fdace829d3 Mon Sep 17 00:00:00 2001 From: Laura Carlesso Date: Fri, 21 Aug 2026 16:39:56 -0700 Subject: [PATCH 176/455] boards: infineon: kit_pse84_ai: Disable crypto and entropy The default configuration for boards should not enable everything and anything that is supported for that board but only the base features to maintain a smaller footprint. The drivers are enabled where they are required (e.g. crypto and entropy tests) Signed-off-by: Laura Carlesso --- boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig | 8 -------- boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig | 8 -------- 2 files changed, 16 deletions(-) diff --git a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig index f5183f8c6167..8f18ffb7bdf4 100644 --- a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig +++ b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig @@ -35,11 +35,3 @@ CONFIG_TRUSTED_EXECUTION_SECURE=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y - -# Enable MXCRYPTO TRNG entropy driver -CONFIG_ENTROPY_GENERATOR=y -CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y - -# Enable MXCRYPTO hardware crypto driver -CONFIG_CRYPTO=y -CONFIG_CRYPTO_INFINEON_MXCRYPTO=y diff --git a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig index d49fda7c806c..f0eed71536f6 100644 --- a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig +++ b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig @@ -25,11 +25,3 @@ CONFIG_SERIAL=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y - -# Enable MXCRYPTO TRNG entropy driver -CONFIG_ENTROPY_GENERATOR=y -CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y - -# Enable MXCRYPTO hardware crypto driver -CONFIG_CRYPTO=y -CONFIG_CRYPTO_INFINEON_MXCRYPTO=y From 54cdacb16600a90a4e4b29f4538bd090666f5662 Mon Sep 17 00:00:00 2001 From: Laura Carlesso Date: Fri, 21 Aug 2026 16:40:13 -0700 Subject: [PATCH 177/455] boards: infineon: kit_pse84_eval: Disable crypto and entropy The default configuration for boards should not enable everything and anything that is supported for that board but only the base features to maintain a smaller footprint. The drivers are enabled where they are required (e.g. crypto and entropy tests) Signed-off-by: Laura Carlesso --- .../infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig | 8 -------- .../infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig | 8 -------- 2 files changed, 16 deletions(-) diff --git a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig index 2a5ea357e724..a76e77b62bd5 100644 --- a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig +++ b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig @@ -34,11 +34,3 @@ CONFIG_TRUSTED_EXECUTION_SECURE=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y - -# Enable MXCRYPTO TRNG entropy driver -CONFIG_ENTROPY_GENERATOR=y -CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y - -# Enable MXCRYPTO hardware crypto driver -CONFIG_CRYPTO=y -CONFIG_CRYPTO_INFINEON_MXCRYPTO=y diff --git a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig index d49fda7c806c..f0eed71536f6 100644 --- a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig +++ b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig @@ -25,11 +25,3 @@ CONFIG_SERIAL=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y - -# Enable MXCRYPTO TRNG entropy driver -CONFIG_ENTROPY_GENERATOR=y -CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y - -# Enable MXCRYPTO hardware crypto driver -CONFIG_CRYPTO=y -CONFIG_CRYPTO_INFINEON_MXCRYPTO=y From 1e0593cbab898701a567b8e8efae2118aa0faa0e Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 26 Aug 2026 06:26:19 -0400 Subject: [PATCH 178/455] Revert "boards: infineon: kit_pse84_eval: Disable crypto and entropy" This reverts commit 54cdacb16600a90a4e4b29f4538bd090666f5662. Signed-off-by: Anas Nashif --- .../infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig | 8 ++++++++ .../infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig index a76e77b62bd5..2a5ea357e724 100644 --- a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig +++ b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m33_defconfig @@ -34,3 +34,11 @@ CONFIG_TRUSTED_EXECUTION_SECURE=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y + +# Enable MXCRYPTO TRNG entropy driver +CONFIG_ENTROPY_GENERATOR=y +CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y + +# Enable MXCRYPTO hardware crypto driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_INFINEON_MXCRYPTO=y diff --git a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig index f0eed71536f6..d49fda7c806c 100644 --- a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig +++ b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55_defconfig @@ -25,3 +25,11 @@ CONFIG_SERIAL=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y + +# Enable MXCRYPTO TRNG entropy driver +CONFIG_ENTROPY_GENERATOR=y +CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y + +# Enable MXCRYPTO hardware crypto driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_INFINEON_MXCRYPTO=y From 18d73449d61d225898e8d8a707149bbd4a27b0f3 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 26 Aug 2026 06:26:21 -0400 Subject: [PATCH 179/455] Revert "boards: infineon: kit_pse84_ai: Disable crypto and entropy" This reverts commit 4b887f9886d6fceb881942424db1f3fdace829d3. Signed-off-by: Anas Nashif --- boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig | 8 ++++++++ boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig index 8f18ffb7bdf4..f5183f8c6167 100644 --- a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig +++ b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m33_defconfig @@ -35,3 +35,11 @@ CONFIG_TRUSTED_EXECUTION_SECURE=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y + +# Enable MXCRYPTO TRNG entropy driver +CONFIG_ENTROPY_GENERATOR=y +CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y + +# Enable MXCRYPTO hardware crypto driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_INFINEON_MXCRYPTO=y diff --git a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig index f0eed71536f6..d49fda7c806c 100644 --- a/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig +++ b/boards/infineon/kit_pse84_ai/kit_pse84_ai_m55_defconfig @@ -25,3 +25,11 @@ CONFIG_SERIAL=y CONFIG_CODE_DATA_RELOCATION=y CONFIG_USE_DT_CODE_PARTITION=y + +# Enable MXCRYPTO TRNG entropy driver +CONFIG_ENTROPY_GENERATOR=y +CONFIG_ENTROPY_INFINEON_MXCRYPTO_TRNG=y + +# Enable MXCRYPTO hardware crypto driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_INFINEON_MXCRYPTO=y From 8f68a310ef44e9da45945ab3083f0052b293ef3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 26 Aug 2026 08:25:36 +0000 Subject: [PATCH 180/455] modules: hal_nxp: keep the i.MX952 device drivers on the include path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCUX common header (fsl_common_arm.h) includes "fsl_clock.h" unconditionally, and on i.MX952 that header lives in the device drivers/ folder. The SDK only adds that folder to the include path when one of its components is selected. On the Cortex-A55 the clocks are managed over SCMI, so driver.clock is skipped and nothing else pulls the folder in, which broke every A55 build: error: fsl_clock.h: No such file or directory (the imx952_evk A55 and A55 SMP builds of sample.kernel.synchronization in CI). Enable the header-only memory component for the device, the same workaround already used for the i.MX943 fsl_elec_spec.h header. driver.clock is not an option here: its fsl_clock.c drives the system manager directly and is not meant for SCMI configurations. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- modules/hal_nxp/mcux/mcux-sdk-ng/device/device.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/device/device.cmake b/modules/hal_nxp/mcux/mcux-sdk-ng/device/device.cmake index 052496ccc395..1cb03d2ea660 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/device/device.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/device/device.cmake @@ -87,6 +87,17 @@ if(CONFIG_SOC_MIMX94398) set(CONFIG_MCUX_COMPONENT_driver.elec_spec ON) endif() +# Same story on i.MX952: fsl_common_arm.h unconditionally includes +# "fsl_clock.h" from the device drivers/ folder, and on the Cortex-A55 the +# clocks are driven over SCMI so driver.clock is not selected above. Enable the +# header-only memory component, which is what puts that folder on the include +# path, for the whole device. driver.clock itself cannot be used here: its +# fsl_clock.c talks to the system manager directly and is not built for SCMI +# configurations. +if(CONFIG_SOC_MIMX9529) + set(CONFIG_MCUX_COMPONENT_driver.memory ON) +endif() + # load device variables include(${mcux_device_folder}/variable.cmake) From 64100f65d1fc27aabba5265ddd0a56d3ce9b6f31 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 25 Aug 2026 21:03:58 -0400 Subject: [PATCH 181/455] tests: gpio: it8xxx2_v2: include arch_interface.h in cpu.h shim The test replaces zephyr/arch/cpu.h with a shim that includes posix/arch.h directly. Since irq.h gained the inline k_irq_* wrappers that call arch_irq_lock() and friends, the shim breaks the build: the posix arch header pulls in irq.h before it defines those functions, and the real cpu.h only avoids this by including arch_interface.h, which declares the arch_irq_* prototypes, ahead of the arch header. Mirror the real cpu.h in the shim: include arch_interface.h first and add the matching include guard, which is needed because arch_interface.h includes cpu.h back. The hand-written dynamic interrupt prototypes are provided by arch_interface.h, so drop them. Signed-off-by: Anas Nashif --- .../gpio_ite_it8xxx2_v2/include/zephyr/arch/cpu.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/drivers/gpio/gpio_ite_it8xxx2_v2/include/zephyr/arch/cpu.h b/tests/drivers/gpio/gpio_ite_it8xxx2_v2/include/zephyr/arch/cpu.h index ab921224676c..cd1e5bf76b86 100644 --- a/tests/drivers/gpio/gpio_ite_it8xxx2_v2/include/zephyr/arch/cpu.h +++ b/tests/drivers/gpio/gpio_ite_it8xxx2_v2/include/zephyr/arch/cpu.h @@ -4,13 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#ifndef ZEPHYR_INCLUDE_ARCH_CPU_H_ +#define ZEPHYR_INCLUDE_ARCH_CPU_H_ + +#include + #include #include -int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority, - void (*routine)(const void *parameter), - const void *parameter, uint32_t flags); -int arch_irq_disconnect_dynamic(unsigned int irq, unsigned int priority, - void (*routine)(const void *parameter), - const void *parameter, uint32_t flags); typedef struct z_thread_stack_element k_thread_stack_t; + +#endif /* ZEPHYR_INCLUDE_ARCH_CPU_H_ */ From 6909e8d69192976b4a782afebf94332d3ba7e665 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 26 Aug 2026 06:11:40 -0400 Subject: [PATCH 182/455] Revert "boards/qemu/x86: Add qemu_x86_64_kvm" This reverts commit c0a8c419bf3b465e35ca9f1a009dc2f5c8bebf4d. Signed-off-by: Anas Nashif --- boards/qemu/x86/Kconfig | 3 +-- boards/qemu/x86/Kconfig.defconfig | 14 +++----------- boards/qemu/x86/Kconfig.qemu_x86_64_kvm | 6 ------ boards/qemu/x86/board.cmake | 6 +----- boards/qemu/x86/board.yml | 7 ------- boards/qemu/x86/qemu_x86_64_kvm.dts | 9 --------- boards/qemu/x86/qemu_x86_64_kvm.yaml | 16 ---------------- boards/qemu/x86/qemu_x86_64_kvm_defconfig | 13 ------------- 8 files changed, 5 insertions(+), 69 deletions(-) delete mode 100644 boards/qemu/x86/Kconfig.qemu_x86_64_kvm delete mode 100644 boards/qemu/x86/qemu_x86_64_kvm.dts delete mode 100644 boards/qemu/x86/qemu_x86_64_kvm.yaml delete mode 100644 boards/qemu/x86/qemu_x86_64_kvm_defconfig diff --git a/boards/qemu/x86/Kconfig b/boards/qemu/x86/Kconfig index 11aa917c9e6a..68963c8b81a4 100644 --- a/boards/qemu/x86/Kconfig +++ b/boards/qemu/x86/Kconfig @@ -6,5 +6,4 @@ config BOARD_QEMU_X86 bool default y select CPU_HAS_FPU if BOARD_QEMU_X86 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY - select X86_64 if BOARD_QEMU_X86_64 || BOARD_QEMU_X86_64_KVM - select X86_CPU_HAS_CET if BOARD_QEMU_X86_64_KVM + select X86_64 if BOARD_QEMU_X86_64 diff --git a/boards/qemu/x86/Kconfig.defconfig b/boards/qemu/x86/Kconfig.defconfig index 55caeb084e47..72c6eb89820f 100644 --- a/boards/qemu/x86/Kconfig.defconfig +++ b/boards/qemu/x86/Kconfig.defconfig @@ -1,8 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2019 Intel Corp. -if BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY || \ - BOARD_QEMU_X86_64_KVM +if BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY # The EEPROM emulator must be initialized after the flash simulator config EEPROM_INIT_PRIORITY @@ -18,7 +17,7 @@ config QEMU_TARGET config HAS_COVERAGE_SUPPORT default y -endif # BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY... +endif # BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY if BOARD_QEMU_X86 @@ -48,7 +47,7 @@ config QEMU_ICOUNT_SHIFT endif # BOARD_QEMU_X86 -if BOARD_QEMU_X86_64 || BOARD_QEMU_X86_64_KVM +if BOARD_QEMU_X86_64 config KERNEL_VM_SIZE default 0x10000000 if ACPI @@ -108,10 +107,3 @@ config DEMAND_PAGING_PAGE_FRAMES_RESERVE default 6 if NEWLIB_LIBC || (COMMON_LIBC_MALLOC && COMMON_LIBC_MALLOC_ARENA_SIZE != 0) endif # BOARD_QEMU_X86_TINY - -if BOARD_QEMU_X86_64_KVM - -config SYS_CLOCK_HW_CYCLES_PER_SEC - default $(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency) - -endif # BOARD_QEMU_X86_64_KVM diff --git a/boards/qemu/x86/Kconfig.qemu_x86_64_kvm b/boards/qemu/x86/Kconfig.qemu_x86_64_kvm deleted file mode 100644 index 16feee51c41e..000000000000 --- a/boards/qemu/x86/Kconfig.qemu_x86_64_kvm +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) 2026 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -config BOARD_QEMU_X86_64_KVM - select SOC_ATOM diff --git a/boards/qemu/x86/board.cmake b/boards/qemu/x86/board.cmake index 4a9cc9452075..e0a1922e42d4 100644 --- a/boards/qemu/x86/board.cmake +++ b/boards/qemu/x86/board.cmake @@ -3,11 +3,7 @@ set(SUPPORTED_EMU_PLATFORMS qemu) -if(CONFIG_BOARD_QEMU_X86_64_KVM) - set(QEMU_BINARY_SUFFIX x86_64) - set(QEMU_CPU_TYPE host,+x2apic) - list(APPEND QEMU_EXTRA_FLAGS -rtc clock=vm --enable-kvm) -elseif(CONFIG_X86_64) +if(CONFIG_X86_64) set(QEMU_BINARY_SUFFIX x86_64) set(QEMU_CPU_TYPE qemu64,+x2apic) if("${CONFIG_MP_MAX_NUM_CPUS}" STREQUAL "1") diff --git a/boards/qemu/x86/board.yml b/boards/qemu/x86/board.yml index 7d772b1d3094..cfd84976dae2 100644 --- a/boards/qemu/x86/board.yml +++ b/boards/qemu/x86/board.yml @@ -31,10 +31,3 @@ boards: vendor: intel socs: - name: atom - - - name: qemu_x86_64_kvm - full_name: QEMU Emulation for X86 64bit, KVM enabled - socs: - - name: atom - variants: - - name: 'nokpti' diff --git a/boards/qemu/x86/qemu_x86_64_kvm.dts b/boards/qemu/x86/qemu_x86_64_kvm.dts deleted file mode 100644 index 4002a1511b57..000000000000 --- a/boards/qemu/x86/qemu_x86_64_kvm.dts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2026 Intel Corp. - * SPDX-License-Identifier: Apache-2.0 - */ -#include "qemu_x86_64.dts" - -&cpu { - clock-frequency = <25000000>; -}; diff --git a/boards/qemu/x86/qemu_x86_64_kvm.yaml b/boards/qemu/x86/qemu_x86_64_kvm.yaml deleted file mode 100644 index d2ce15211ff6..000000000000 --- a/boards/qemu/x86/qemu_x86_64_kvm.yaml +++ /dev/null @@ -1,16 +0,0 @@ -identifier: qemu_x86_64_kvm -name: QEMU Emulation for X86_64 (KVM enabled) -type: qemu -arch: x86 -toolchain: - - zephyr -supported: - - smp -simulation: - - name: qemu -testing: - default: false - ignore_tags: - - benchmark - - kernel -vendor: qemu diff --git a/boards/qemu/x86/qemu_x86_64_kvm_defconfig b/boards/qemu/x86/qemu_x86_64_kvm_defconfig deleted file mode 100644 index 2a910bb7cc63..000000000000 --- a/boards/qemu/x86/qemu_x86_64_kvm_defconfig +++ /dev/null @@ -1,13 +0,0 @@ -CONFIG_PICOLIBC_USE_MODULE=y -CONFIG_PIC_DISABLE=y -CONFIG_LOAPIC=y -CONFIG_CONSOLE=y -CONFIG_SERIAL=y -CONFIG_UART_CONSOLE=y -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_X86_DEBUG_INFO=y -CONFIG_SMP=y -CONFIG_MP_MAX_NUM_CPUS=2 -CONFIG_X86_MMU=y -CONFIG_X86_VERY_EARLY_CONSOLE=y -CONFIG_QEMU_ICOUNT=n From 4adba2d7a09e7e46ce05b2dffe893910288040ab Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 26 Aug 2026 06:11:44 -0400 Subject: [PATCH 183/455] Revert "boards: qemu: Update x86 Kconfig to be more HWMv2-y" This reverts commit 2b0b932315d589d10dc1c1a6cf1dbaaacc72c404. Signed-off-by: Anas Nashif --- boards/qemu/x86/Kconfig | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/boards/qemu/x86/Kconfig b/boards/qemu/x86/Kconfig index 68963c8b81a4..092f34f453d5 100644 --- a/boards/qemu/x86/Kconfig +++ b/boards/qemu/x86/Kconfig @@ -4,6 +4,16 @@ config BOARD_QEMU_X86 bool - default y - select CPU_HAS_FPU if BOARD_QEMU_X86 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY - select X86_64 if BOARD_QEMU_X86_64 + select CPU_HAS_FPU + +config BOARD_QEMU_X86_64 + bool + select X86_64 + +config BOARD_QEMU_X86_LAKEMONT + bool + select CPU_HAS_FPU + +config BOARD_QEMU_X86_TINY + bool + select CPU_HAS_FPU From ac89817dc9535548bce5f214ff86eb8f430d199b Mon Sep 17 00:00:00 2001 From: Jose Alberto Meza Date: Fri, 24 Apr 2026 16:07:02 -0700 Subject: [PATCH 184/455] samples: drivers: flash_shell: Add MEC175x support Add board support to use flash shell in MEC175x EVB Signed-off-by: Jose Alberto Meza --- .../boards/mec_assy6941_mec1753_qsz.overlay | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 samples/drivers/flash_shell/boards/mec_assy6941_mec1753_qsz.overlay diff --git a/samples/drivers/flash_shell/boards/mec_assy6941_mec1753_qsz.overlay b/samples/drivers/flash_shell/boards/mec_assy6941_mec1753_qsz.overlay new file mode 100644 index 000000000000..a9644e86b1ba --- /dev/null +++ b/samples/drivers/flash_shell/boards/mec_assy6941_mec1753_qsz.overlay @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Build test for jedec,spi-nor compatible (drivers/flash/spi_nor.c) + */ + +&qspi0 { + compatible = "microchip,xec-qmspi-ldma"; + status = "okay"; + pinctrl-0 = <&qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056 + &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224>; + pinctrl-1 = <&qspi_shd_cs0_n_gpio055_fp &qspi_shd_clk_gpio056_lp + &qspi_shd_io0_gpio223_lp &qspi_shd_io1_gpio224_lp>; + pinctrl-names = "default", "sleep"; + + w25q128jv: w25q128jv@0 { + compatible = "jedec,spi-nor"; + status = "okay"; + reg = <0>; + spi-max-frequency = ; + size = ; + has-dpd; + t-enter-dpd = <6000>; + t-exit-dpd = <6000>; + jedec-id = [ef 40 18]; + }; +}; From 20d0a12529a533e0eab48d2a20d01695372a0d49 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Fri, 29 May 2026 13:01:31 -0400 Subject: [PATCH 185/455] soc: st: stm32h7: fix M4 ART accelerator address configuration The ART (Adaptive Real-Time) flash cache accelerator base address is configured using only DT_REG_ADDR(DT_CHOSEN(zephyr_flash)) without accounting for CONFIG_FLASH_LOAD_OFFSET. When the M4 flash layout uses zephyr,flash = &flash0 with a non-zero CONFIG_FLASH_LOAD_OFFSET, the ART caches the wrong flash region. For example, with CONFIG_FLASH_LOAD_OFFSET=0x100000 the ART points at 0x08000000 (Bank 1) while M4 code executes from 0x08100000 (Bank 2). This results in a 0% cache hit rate and every instruction fetch paying the full flash wait-state latency penalty. Since the M4 core has no I-Cache or D-Cache, the ART accelerator is its only flash caching mechanism. Add CONFIG_FLASH_LOAD_OFFSET to the ART base address calculation so it points to the actual code execution region. This is a no-op when CONFIG_FLASH_LOAD_OFFSET=0. Signed-off-by: Farrell Aultman Signed-off-by: Murali Karicheri --- soc/st/stm32/stm32h7x/soc_m4.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/soc/st/stm32/stm32h7x/soc_m4.c b/soc/st/stm32/stm32h7x/soc_m4.c index b0c4154de22f..c7d0de123dd9 100644 --- a/soc/st/stm32/stm32h7x/soc_m4.c +++ b/soc/st/stm32/stm32h7x/soc_m4.c @@ -29,9 +29,14 @@ */ void soc_early_init_hook(void) { + uint32_t art_base = DT_REG_ADDR(DT_CHOSEN(zephyr_flash)); + +#ifdef CONFIG_FLASH_LOAD_OFFSET + art_base += CONFIG_FLASH_LOAD_OFFSET; +#endif /* Enable ART Flash cache accelerator */ LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_ART); - LL_ART_SetBaseAddress(DT_REG_ADDR(DT_CHOSEN(zephyr_flash))); + LL_ART_SetBaseAddress(art_base); LL_ART_Enable(); /* Enable hardware semaphore clock */ From fab2529e72c736df51abfded6bec13fb945bd845 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 24 Jun 2026 10:59:10 +0530 Subject: [PATCH 186/455] dts: arm: ti: mspm0: l: mspm0l111x: Fix RTC node The RTC timer present in mspm0l111x is RTC_b. This does not have power enable register, and thus will crash on init without setting `ti,rtc-x`. Signed-off-by: Ayush Singh --- dts/arm/ti/mspm0/l/mspm0l111x.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dts/arm/ti/mspm0/l/mspm0l111x.dtsi b/dts/arm/ti/mspm0/l/mspm0l111x.dtsi index 90789ee8625b..db8184d22b5a 100644 --- a/dts/arm/ti/mspm0/l/mspm0l111x.dtsi +++ b/dts/arm/ti/mspm0/l/mspm0l111x.dtsi @@ -71,3 +71,7 @@ }; }; }; + +&rtc { + ti,rtc-x; +}; From 60b9d82f75f8333eee3cdd96da2ce1604139bf0f Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 24 Jun 2026 11:00:19 +0530 Subject: [PATCH 187/455] boards: beagle: beagleconnect_zepto: Enable rtc MSPM0L1117 used in beagleconnect_zepto contains RTC_b. The alias rtc is used by the sample and test for rtc. And zephyr,rtc chosen property seems to be a Zephyr specific chosen property [0]. Not sure why the sample and test does not use the chosen property. [0]: https://docs.zephyrproject.org/latest/build/dts/api/api.html#devicetree-chosen-nodes Signed-off-by: Ayush Singh --- boards/beagle/beagleconnect_zepto/beagleconnect_zepto.dts | 6 ++++++ boards/beagle/beagleconnect_zepto/beagleconnect_zepto.yaml | 1 + 2 files changed, 7 insertions(+) diff --git a/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.dts b/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.dts index 6951f5072a1f..8ed83c4c166e 100644 --- a/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.dts +++ b/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.dts @@ -21,6 +21,7 @@ sw0 = &button0; watchdog0 = &wdt0; counter = &counterg0; + rtc = &rtc; }; chosen { @@ -28,6 +29,7 @@ zephyr,flash = &flash0; zephyr,console = &uart0; zephyr,entropy = &trng; + zephyr,rtc = &rtc; }; gpio_keys { @@ -93,3 +95,7 @@ &counterg0 { status = "okay"; }; + +&rtc { + status = "okay"; +}; diff --git a/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.yaml b/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.yaml index 554d936598fa..b5466832ee7f 100644 --- a/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.yaml +++ b/boards/beagle/beagleconnect_zepto/beagleconnect_zepto.yaml @@ -21,3 +21,4 @@ supported: - watchdog - counter - gpio + - rtc From e815ae28fa819a36ef9eb930ed34f25406a7c6e5 Mon Sep 17 00:00:00 2001 From: Samuel Slesar Date: Wed, 1 Jul 2026 12:28:02 +0200 Subject: [PATCH 188/455] dts: arm: nxp: mcxl: add MUB node Add the AON-domain MUB mailbox node for MCXL devices. MUB is disabled by default and can be enabled by board devicetree files. Signed-off-by: Samuel Slesar --- dts/arm/nxp/mcx/mcxl/nxp_mcxl_aon.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dts/arm/nxp/mcx/mcxl/nxp_mcxl_aon.dtsi b/dts/arm/nxp/mcx/mcxl/nxp_mcxl_aon.dtsi index e7a94979b3d3..d4efedbc46ed 100644 --- a/dts/arm/nxp/mcx/mcxl/nxp_mcxl_aon.dtsi +++ b/dts/arm/nxp/mcx/mcxl/nxp_mcxl_aon.dtsi @@ -75,6 +75,15 @@ status = "disabled"; }; + mbox_b: mbox@84000 { + compatible = "nxp,mbox-imx-mu"; + reg = <0x84000 0x1000>; + interrupts = <7 0>, <8 0>, <9 0>; + rx-channels = <4>; + #mbox-cells = <1>; + status = "disabled"; + }; + aon_qtmr_clock: aon-qtmr-clock { compatible = "fixed-clock"; #clock-cells = <0>; From 1e9262f8c7e0e1e97e67460963faabd130276f9e Mon Sep 17 00:00:00 2001 From: Samuel Slesar Date: Wed, 1 Jul 2026 12:28:07 +0200 Subject: [PATCH 189/455] boards: frdm_mcxl255: enable MU Enable the MUA and MUB mailbox instances for the FRDM-MCXL255 CPU0 and CPU1 targets. Advertise mailbox support in the board metadata. Signed-off-by: Samuel Slesar --- boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.dtsi | 4 ++++ boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.yaml | 1 + boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.dts | 4 ++++ boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.yaml | 1 + 4 files changed, 10 insertions(+) diff --git a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.dtsi b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.dtsi index 20315c4372f3..5035255ffbdc 100644 --- a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.dtsi +++ b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.dtsi @@ -164,3 +164,7 @@ pinctrl-0 = <&pinmux_aon_lpi2c0>; pinctrl-names = "default"; }; + +&mbox_a { + status = "okay"; +}; diff --git a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.yaml b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.yaml index 6b5b0b9ef9bb..56b42581e9bc 100644 --- a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.yaml +++ b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu0.yaml @@ -18,4 +18,5 @@ supported: - rtc - crc - watchdog + - mbox vendor: nxp diff --git a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.dts b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.dts index 7c0f66cf21f1..d971b11810cc 100644 --- a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.dts +++ b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.dts @@ -41,3 +41,7 @@ pinctrl-0 = <&pinmux_aon_lpi2c0>; pinctrl-names = "default"; }; + +&mbox_b { + status = "okay"; +}; diff --git a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.yaml b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.yaml index 332b7185dd14..f2a7b2b7c95f 100644 --- a/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.yaml +++ b/boards/nxp/frdm_mcxl255/frdm_mcxl255_mcxl255_cpu1.yaml @@ -13,4 +13,5 @@ toolchain: supported: - uart - i2c + - mbox vendor: nxp From 42bbe8044a089ed3fa1384f547ce295936cdd2c9 Mon Sep 17 00:00:00 2001 From: Abderrahmane JARMOUNI Date: Wed, 1 Jul 2026 14:19:11 +0200 Subject: [PATCH 190/455] drivers: display: advertise support for callback events Advertise support for callback events via capabilities->supported_events returned by display_get_capabilities() API. Add the new capas field to drivers using events callback. Remove events mask validation from drivers, it is now handeled at API level. Consider a wrong events mask as an invalid argument and return -EINVAL. Now -ENOTSUP is only returned upon unsupported cb invocation context. Signed-off-by: Abderrahmane JARMOUNI --- drivers/display/display_mcux_elcdif.c | 9 ++------- drivers/display/display_stm32_ltdc.c | 5 +---- include/zephyr/drivers/display.h | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/display/display_mcux_elcdif.c b/drivers/display/display_mcux_elcdif.c index 5732cca99baa..d22629eb4c04 100644 --- a/drivers/display/display_mcux_elcdif.c +++ b/drivers/display/display_mcux_elcdif.c @@ -344,6 +344,8 @@ static void mcux_elcdif_get_capabilities(const struct device *dev, capabilities->supported_pixel_formats = supported_fmts; capabilities->current_pixel_format = ((struct mcux_elcdif_data *)dev->data)->pixel_format; capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; + capabilities->supported_events = + DISPLAY_EVENT_FRAME_DONE | DISPLAY_EVENT_VSYNC | DISPLAY_EVENT_FIFO_UNDERFLOW; } static int mcux_elcdif_register_event_cb(const struct device *dev, display_event_cb_t cb, @@ -351,8 +353,6 @@ static int mcux_elcdif_register_event_cb(const struct device *dev, display_event uint32_t *out_reg_handle) { struct mcux_elcdif_data *dev_data = dev->data; - const uint32_t supported_events = - DISPLAY_EVENT_FRAME_DONE | DISPLAY_EVENT_VSYNC | DISPLAY_EVENT_FIFO_UNDERFLOW; k_sem_take(&dev_data->cb_sem, K_FOREVER); @@ -371,11 +371,6 @@ static int mcux_elcdif_register_event_cb(const struct device *dev, display_event k_sem_give(&dev_data->cb_sem); return -ENOTSUP; } - if (event_mask & ~supported_events) { - LOG_ERR("Registration failed: unsupported event supplied"); - k_sem_give(&dev_data->cb_sem); - return -ENOTSUP; - } *out_reg_handle = 1U; diff --git a/drivers/display/display_stm32_ltdc.c b/drivers/display/display_stm32_ltdc.c index c55e82dba553..b3050e433be9 100644 --- a/drivers/display/display_stm32_ltdc.c +++ b/drivers/display/display_stm32_ltdc.c @@ -238,6 +238,7 @@ static void stm32_ltdc_get_capabilities(const struct device *dev, capabilities->current_pixel_format = data->current_pixel_format; capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; + capabilities->supported_events = DISPLAY_EVENT_VSYNC | DISPLAY_EVENT_LINE_INT; } static void stm32_ltdc_partial_write(const struct device *dev, @@ -468,10 +469,6 @@ static int stm32_ltdc_display_register_event_cb(const struct device *dev, displa LOG_ERR("Registration failed: only ISR context is supported for this driver"); return -ENOSYS; } - if (event_mask & ~(DISPLAY_EVENT_VSYNC | DISPLAY_EVENT_LINE_INT)) { - LOG_ERR("Registration failed: Unsupported event requested"); - return -ENOSYS; - } /* VSync can only be detected by LTDC line interrupt, * so the line event is programmed accordingly diff --git a/include/zephyr/drivers/display.h b/include/zephyr/drivers/display.h index 81bfae76bd79..a4aed1460167 100644 --- a/include/zephyr/drivers/display.h +++ b/include/zephyr/drivers/display.h @@ -340,6 +340,8 @@ struct display_capabilities { enum display_pixel_format current_pixel_format; /** Current display orientation */ enum display_orientation current_orientation; + /** Supported callback events mask, 0 when event callback unsupported */ + uint32_t supported_events; #if defined(CONFIG_DISPLAY_COLOR_PALETTE) || defined(__DOXYGEN__) /** Color palette supported by the display, indexed by pixel value */ struct display_palette_color color_palette[CONFIG_DISPLAY_COLOR_PALETTE_MAX_SIZE]; @@ -774,6 +776,8 @@ static inline void display_get_capabilities(const struct device *dev, struct display_capabilities * capabilities) { + __ASSERT(capabilities != NULL, "display_capabilities struct is NULL"); + memset(capabilities, 0, sizeof(struct display_capabilities)); DEVICE_API_GET(display, dev)->get_capabilities(dev, capabilities); } @@ -841,8 +845,7 @@ static inline int display_set_orientation(const struct device *dev, * * @return 0 and a non-zero out_reg_handle value on success, otherwise a negative errno code. * @retval -EBUSY A callback is already registered. - * @retval -ENOTSUP One of the events is not supported, - * or the requested invocation context is not supported. + * @retval -ENOTSUP The requested callback invocation context is not supported. * @retval -ENOSYS Not implemented. * @retval -EINVAL Invalid argument. */ @@ -854,11 +857,20 @@ static inline int display_register_event_cb(const struct device *dev, __ASSERT(cb != NULL, "Registration failed: callback function pointer is NULL"); const struct display_driver_api *api = DEVICE_API_GET(display, dev); + struct display_capabilities caps; if (api->register_event_cb == NULL) { return -ENOSYS; } + api->get_capabilities(dev, &caps); + if (!caps.supported_events) { + return -ENOSYS; + } + if (event_mask == 0 || (~caps.supported_events & event_mask)) { + return -EINVAL; + } + return api->register_event_cb(dev, cb, user_data, event_mask, in_isr, out_reg_handle); } From aa51bac4d2432fe106b5815584b2955211ff4fb8 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 30 Jul 2026 13:13:25 +0530 Subject: [PATCH 191/455] soc: nordic: nrf71: fix LMAC boot address read from WICR nrfx 4.5.0 restructured NRF_WICR_Type. The Wi-Fi core boot address previously sat inside an anonymous reserved block, so wifi_setup() read it as NRF_WICR->RESERVED[0], which resolved to WICR + 0x000. The new layout names that region as the FIRMWARE group: __IOM NRF_WICR_FIRMWARE_Type FIRMWARE; /* 0x000 */ __IM uint32_t RESERVED[28]; __IOM NRF_WICR_IPCCONFIG_Type IPCCONFIG; /* 0x080 */ FIRMWARE occupies 0x000..0x00F, so RESERVED[0] silently moved from 0x000 to 0x010. The code still compiles, but now reads an unprogrammed word instead of the LMAC boot address. The LMAC VPR is started at a bogus PC, so the Wi-Fi core never boots and the IPC endpoint never binds: wifi_nrf: IPC endpoint not bound after 30000 ms wifi_nrf: nrf_wifi_fmac_dev_add_zep: dev_init failed Read the named FIRMWARE.LMACINITPC field instead. This matches the offset the WICR generation tooling programs for firmware-lmacinitpc (soc/nordic/nrf71/wicr/gen_wicr/gen_wicr.py), so the boot address is picked up from the same location it is written to. Signed-off-by: Chaitanya Tata Assisted-by: Claude:claude-opus-5 --- soc/nordic/nrf71/soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soc/nordic/nrf71/soc.c b/soc/nordic/nrf71/soc.c index 99c76f19b025..e14d3f2e3b3d 100644 --- a/soc/nordic/nrf71/soc.c +++ b/soc/nordic/nrf71/soc.c @@ -170,7 +170,7 @@ static void wifi_setup(void) /* Kickstart the LMAC processor */ NRF_WIFICORE_LRCCONF_LRC0->POWERON = (LRCCONF_POWERON_MAIN_AlwaysOn << LRCCONF_POWERON_MAIN_Pos); - NRF_WIFICORE_LMAC_VPR->INITPC = NRF_WICR->RESERVED[0]; + NRF_WIFICORE_LMAC_VPR->INITPC = (uint32_t)(uintptr_t)NRF_WICR->FIRMWARE.LMACINITPC; NRF_WIFICORE_LMAC_VPR->CPURUN = (VPR_CPURUN_EN_Running << VPR_CPURUN_EN_Pos); } #endif From 9ba91cec6611ff6a451741dd730052171018b901 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 5 Aug 2026 14:07:20 +0200 Subject: [PATCH 192/455] Bluetooth: MPL: Implement playing speed The playback_speed_param value was not used to correct adjust the position while in the playing or state. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/mpl.c | 97 +++++++++++++++++-- tests/bsim/bluetooth/audio/src/mcc_test.c | 3 - .../audio/src/media_controller_test.c | 3 - 3 files changed, 90 insertions(+), 13 deletions(-) diff --git a/subsys/bluetooth/audio/mpl.c b/subsys/bluetooth/audio/mpl.c index 9b20835c53ba..85073dee6f7f 100644 --- a/subsys/bluetooth/audio/mpl.c +++ b/subsys/bluetooth/audio/mpl.c @@ -2081,11 +2081,63 @@ static int8_t get_playback_speed(void) return media_player.playback_speed_param; } +static int8_t get_supported_playback_speed(int8_t speed) +{ + if (speed > media_player.playback_speed_param) { + /* MCS spec section 3.8.1 states: + * If the value written is not supported and is greater than the existing Playback + * Speed characteristic, then the server should set the Playback Speed + * characteristic to the next higher supported playback speed + */ + if (speed > MEDIA_PROXY_PLAYBACK_SPEED_UNITY) { + return MEDIA_PROXY_PLAYBACK_SPEED_DOUBLE; + } else if (speed > MEDIA_PROXY_PLAYBACK_SPEED_HALF) { + return MEDIA_PROXY_PLAYBACK_SPEED_UNITY; + } else if (speed > MEDIA_PROXY_PLAYBACK_SPEED_QUARTER) { + return MEDIA_PROXY_PLAYBACK_SPEED_HALF; + } else { + return MEDIA_PROXY_PLAYBACK_SPEED_QUARTER; + } + } else if (speed < media_player.playback_speed_param) { + /* MCS spec section 3.8.1 states: + * If the value written is not supported and is less than the existing Playback + * Speed characteristic, then the server should set the Playback Speed + * characteristic to the next lower supported playback speed + */ + if (speed < MEDIA_PROXY_PLAYBACK_SPEED_HALF) { + return MEDIA_PROXY_PLAYBACK_SPEED_QUARTER; + } else if (speed < MEDIA_PROXY_PLAYBACK_SPEED_UNITY) { + return MEDIA_PROXY_PLAYBACK_SPEED_HALF; + } else if (speed < MEDIA_PROXY_PLAYBACK_SPEED_DOUBLE) { + return MEDIA_PROXY_PLAYBACK_SPEED_UNITY; + } else { + return MEDIA_PROXY_PLAYBACK_SPEED_DOUBLE; + } + } else { + return speed; + } +} + static void set_playback_speed(int8_t speed) { /* Set new speed parameter and notify, if different from current */ if (speed != media_player.playback_speed_param) { - media_player.playback_speed_param = speed; + /* This MPL only supports MEDIA_PROXY_PLAYBACK_SPEED_QUARTER, + * MEDIA_PROXY_PLAYBACK_SPEED_HALF, MEDIA_PROXY_PLAYBACK_SPEED_UNITY and + * MEDIA_PROXY_PLAYBACK_SPEED_DOUBLE. + * For unsupported values the MCS specification states: + * + * MCS spec section 3.8.1 states: + * If the server does not support the value written, the server shall set + * the Playback Speed characteristic to a supported value. + */ + int8_t supported_playback_speed = get_supported_playback_speed(speed); + + if (speed != supported_playback_speed) { + LOG_DBG("Changed speed from %d to %d", speed, supported_playback_speed); + } + + media_player.playback_speed_param = supported_playback_speed; media_proxy_pl_playback_speed_cb(media_player.playback_speed_param); } } @@ -2330,19 +2382,50 @@ static uint8_t get_content_ctrl_id(void) return media_player.content_ctrl_id; } -static void pos_work_cb(struct k_work *work) +/** Calculates the new relative position depending on the sate and seeking/playing speed factor + * + * @return New relative postion in centiseconds (may be negative) + */ +static int32_t get_pos_diff_cs(void) { - const int32_t pos_diff_cs = TRACK_POS_WORK_DELAY_MS / 10; /* position is in centiseconds*/ - - ARG_UNUSED(work); + int32_t pos_diff_ms = TRACK_POS_WORK_DELAY_MS; if (media_player.state == MEDIA_PROXY_STATE_SEEKING) { /* When seeking, apply the seeking speed factor */ - set_relative_track_position(pos_diff_cs * media_player.seeking_speed_factor); + pos_diff_ms *= media_player.seeking_speed_factor; } else if (media_player.state == MEDIA_PROXY_STATE_PLAYING) { - set_relative_track_position(pos_diff_cs); + /* When playing, apply the playing speed */ + switch (media_player.playback_speed_param) { + case MEDIA_PROXY_PLAYBACK_SPEED_QUARTER: + pos_diff_ms /= 4; + break; + case MEDIA_PROXY_PLAYBACK_SPEED_HALF: + pos_diff_ms /= 2; + break; + case MEDIA_PROXY_PLAYBACK_SPEED_UNITY: + /* no-op */ + break; + case MEDIA_PROXY_PLAYBACK_SPEED_DOUBLE: + pos_diff_ms *= 2; + break; + default: + LOG_WRN("Unexpected playback_speed_param: %d", + media_player.playback_speed_param); + break; + } + } else { + LOG_ERR("Unexpected media_player.state: %u", media_player.state); } + return pos_diff_ms / 10; /* position is in centiseconds*/ +} + +static void pos_work_cb(struct k_work *work) +{ + ARG_UNUSED(work); + + set_relative_track_position(get_pos_diff_cs()); + if (media_player.track_pos == media_player.group->track->duration) { /* Go to next track */ do_next_track(&media_player); diff --git a/tests/bsim/bluetooth/audio/src/mcc_test.c b/tests/bsim/bluetooth/audio/src/mcc_test.c index a21cf98bd3a7..25ed3693a6a2 100644 --- a/tests/bsim/bluetooth/audio/src/mcc_test.c +++ b/tests/bsim/bluetooth/audio/src/mcc_test.c @@ -1833,9 +1833,6 @@ static void test_set_playback_speed(int8_t pb_speed) } WAIT_FOR_FLAG(playback_speed_set); - if (g_pb_speed != pb_speed) { - FAIL("Playback speed failed: Incorrect playback speed\n"); - } LOG_INF("Playback speed set succeeded"); } diff --git a/tests/bsim/bluetooth/audio/src/media_controller_test.c b/tests/bsim/bluetooth/audio/src/media_controller_test.c index dfedfeba006d..2580a99726a1 100644 --- a/tests/bsim/bluetooth/audio/src/media_controller_test.c +++ b/tests/bsim/bluetooth/audio/src/media_controller_test.c @@ -1409,9 +1409,6 @@ void test_media_controller_player(struct media_player *player) } WAIT_FOR_FLAG(playback_speed); - if (g_pb_speed != pb_speed) { - FAIL("Playback speed failed: Incorrect playback speed\n"); - } LOG_INF("Playback speed set succeeded"); /* Read seeking speed *************************************/ From ab1c316bf56c434859012dfc69966ba94050222b Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 7 Aug 2026 13:07:27 -0400 Subject: [PATCH 193/455] tests: fs: lib_link: give the suite a real test case The lib_link suite was registered with ZTEST_SUITE() but contained no ZTEST() cases: the intended assertion was the build itself (the FS libraries must compile and link with only CONFIG_FILE_SYSTEM_LIB_LINK enabled). An empty ztest suite leaves twister's synthetic scenario test case without any status, so every platform this scenario runs on reports "A None status detected in instance ..." - 35 counted warnings per CI twister-build run. Add a test case that takes the address of an API symbol from the libraries meant for direct application use (FAT's f_mount, littlefs' lfs_mount). This gives the suite a real, reportable test case and also strengthens the link-time claim: the linker now has to resolve symbols from the libraries instead of potentially garbage-collecting all of their objects out of the image. Assisted-by: Claude:claude-fable-5 Signed-off-by: Anas Nashif --- tests/subsys/fs/lib_link/src/main.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/subsys/fs/lib_link/src/main.c b/tests/subsys/fs/lib_link/src/main.c index 1b019a25d9b8..88a2806e4392 100644 --- a/tests/subsys/fs/lib_link/src/main.c +++ b/tests/subsys/fs/lib_link/src/main.c @@ -4,7 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include #include +#include +#include + ZTEST_SUITE(lib_link, NULL, NULL, NULL, NULL, NULL); + +ZTEST(lib_link, test_fs_libraries_linked) +{ + /* + * The primary assertion of this test is the build itself: with only + * CONFIG_FILE_SYSTEM_LIB_LINK enabled (no CONFIG_FILE_SYSTEM), the + * underlying file system libraries must compile and link. Referencing + * an API symbol from the libraries meant for direct application use + * additionally forces the linker to resolve them, so the libraries + * cannot be silently garbage-collected out of the image. + */ + zassert_not_null(f_mount, "FAT library not linked"); + zassert_not_null(lfs_mount, "littlefs library not linked"); +} From 68b8cd44d8a78dba9b8990201f870645163a984c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 18 Aug 2026 19:30:34 +0200 Subject: [PATCH 194/455] drivers: ethernet: nxp: qos: limit driver to supported SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Limit the nxp based HAL driver to the SoCs, that it supports. Signed-off-by: Fin Maaß --- drivers/ethernet/eth_nxp_enet_qos/Kconfig | 1 + drivers/ethernet/mdio/Kconfig.nxp_enet_qos | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ethernet/eth_nxp_enet_qos/Kconfig b/drivers/ethernet/eth_nxp_enet_qos/Kconfig index c796eaa3dbb7..216d8792538b 100644 --- a/drivers/ethernet/eth_nxp_enet_qos/Kconfig +++ b/drivers/ethernet/eth_nxp_enet_qos/Kconfig @@ -5,6 +5,7 @@ menuconfig ETH_NXP_ENET_QOS bool "NXP ENET QOS Ethernet Driver" default y depends on DT_HAS_NXP_ENET_QOS_ENABLED + depends on SOC_FAMILY_MCXA || SOC_FAMILY_MCXN depends on NET_BUF_FIXED_DATA_SIZE select PINCTRL help diff --git a/drivers/ethernet/mdio/Kconfig.nxp_enet_qos b/drivers/ethernet/mdio/Kconfig.nxp_enet_qos index d94ce7186bae..849dbce56dcf 100644 --- a/drivers/ethernet/mdio/Kconfig.nxp_enet_qos +++ b/drivers/ethernet/mdio/Kconfig.nxp_enet_qos @@ -4,7 +4,7 @@ config MDIO_NXP_ENET_QOS bool "NXP ENET QoS MDIO driver" default y - depends on DT_HAS_NXP_ENET_QOS_ENABLED + depends on ETH_NXP_ENET_QOS depends on DT_HAS_SNPS_DWMAC_MDIO_ENABLED help Enable NXP ENET QOS (Quality of Service) MDIO driver. From 3a463df499714413918a6af258cbeaf8cdc544c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Sun, 9 Aug 2026 20:12:44 +0200 Subject: [PATCH 195/455] drivers: clock: nxp: add parts for qos ethernet controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add parts for qos ethernet controller Assisted-by: Claude:claude-opus-5 Signed-off-by: Fin Maaß --- .../clock_control/clock_control_nxp_mc_cgm.c | 158 ++++++++++++++++++ include/zephyr/dt-bindings/clock/nxp_mc_cgm.h | 2 + 2 files changed, 160 insertions(+) diff --git a/drivers/clock_control/clock_control_nxp_mc_cgm.c b/drivers/clock_control/clock_control_nxp_mc_cgm.c index 5e432d565888..24601b5e059b 100644 --- a/drivers/clock_control/clock_control_nxp_mc_cgm.c +++ b/drivers/clock_control/clock_control_nxp_mc_cgm.c @@ -41,6 +41,61 @@ const clock_pcfs_config_t pcfs_config = {.maxAllowableIDDchange = NXP_PLL_MAXIDO .clkSrcFreq = NXP_PLL_CLKSRCFREQ}; #endif +#define MC_CGM_EMAC_NODE DT_NODELABEL(emac) + +/* + * The EMAC RX/TX/TS source muxes have to be attached before the MAC leaves + * reset, and which source is correct depends on the PHY interface selected in + * devicetree. Only set them up on SoCs that have an EMAC and only when it is + * actually enabled. + */ +#if defined(FSL_FEATURE_CLOCK_HAS_EMAC) && (FSL_FEATURE_CLOCK_HAS_EMAC != 0U) && \ + DT_NODE_HAS_STATUS_OKAY(MC_CGM_EMAC_NODE) +#define MC_CGM_HAS_EMAC 1 + +/* + * Clock rates the PHY drives into the SoC, both fixed by IEEE 802.3: RMII + * supplies a single 50 MHz reference, MII separate transmit and receive clocks + * that run at 25 MHz for 100 Mbps. + */ +#define MC_CGM_EMAC_RMII_REF_CLK_HZ 50000000U +#define MC_CGM_EMAC_MII_CLK_HZ 25000000U + +#if DT_ENUM_HAS_VALUE(MC_CGM_EMAC_NODE, phy_connection_type, rmii) +/* + * The one reference clock feeds all three domains, and the MAC clocks its + * MII-side logic at half the RMII rate. + */ +#define MC_CGM_EMAC_TXPAD_CLK_HZ MC_CGM_EMAC_RMII_REF_CLK_HZ +#define MC_CGM_EMAC_RXPAD_CLK_HZ 0U +#define MC_CGM_EMAC_RX_ATTACH kEMAC_RMII_TX_CLK_to_EMAC_RX +#define MC_CGM_EMAC_RX_SRC CLOCK_EMAC_RMII_TX_CLK +#define MC_CGM_EMAC_TS_ATTACH kEMAC_RMII_TX_CLK_to_EMAC_TS +#define MC_CGM_EMAC_TS_SRC CLOCK_EMAC_RMII_TX_CLK +#define MC_CGM_EMAC_CLK_DIV 2U +#elif DT_ENUM_HAS_VALUE(MC_CGM_EMAC_NODE, phy_connection_type, mii) +/* + * The PHY drives the transmit and receive clocks on separate pads, already at + * the MII-side rate. The timestamp unit is fed from the transmit clock, which + * means it follows the link speed - fine at 100 Mbps, ten times slower at + * 10 Mbps. + */ +#define MC_CGM_EMAC_TXPAD_CLK_HZ MC_CGM_EMAC_MII_CLK_HZ +#define MC_CGM_EMAC_RXPAD_CLK_HZ MC_CGM_EMAC_MII_CLK_HZ +#define MC_CGM_EMAC_RX_ATTACH kEMAC_RX_CLK_to_EMAC_RX +#define MC_CGM_EMAC_RX_SRC CLOCK_EMAC_RX_CLK +#define MC_CGM_EMAC_TS_ATTACH kEMAC_RMII_TX_CLK_to_EMAC_TS +#define MC_CGM_EMAC_TS_SRC CLOCK_EMAC_RMII_TX_CLK +#define MC_CGM_EMAC_CLK_DIV 1U +#else +#error "Unsupported PHY connection type for the MCXE Ethernet MAC" +#endif + +/* The transmit clock always comes off the same pad, in either mode. */ +#define MC_CGM_EMAC_TX_ATTACH kEMAC_RMII_TX_CLK_to_EMAC_TX +#define MC_CGM_EMAC_TX_SRC CLOCK_EMAC_RMII_TX_CLK +#endif + /* * SDK defines FSL_FEATURE_SOC__COUNT as `(N)` with parentheses, which * breaks Zephyr's LISTIFY (it token-pastes LEN into a macro name and needs @@ -119,6 +174,9 @@ static const struct mc_cgm_gate_entry mc_cgm_gate_map[] = { LISTIFY(MC_CGM_COUNT(FSL_FEATURE_SOC_I2S_COUNT), MC_CGM_GATE_ENTRY, (,), SAI, Sai), #endif +#if defined(MC_CGM_HAS_EMAC) + { MCUX_EMAC_CLK, kCLOCK_Emac }, +#endif }; /* @@ -189,6 +247,77 @@ static const struct mc_cgm_rate_entry *mc_cgm_lookup_rate(uint32_t subsys) return NULL; } +#if defined(MC_CGM_HAS_EMAC) +/* + * SELSTAT sits in the same bits of every mux status register, so one mask + * covers all three. Assert it rather than leaving it to chance. + */ +#define MC_CGM_EMAC_SELSTAT_MASK MC_CGM_MUX_7_CSS_SELSTAT_MASK +BUILD_ASSERT(MC_CGM_MUX_8_CSS_SELSTAT_MASK == MC_CGM_EMAC_SELSTAT_MASK); +BUILD_ASSERT(MC_CGM_MUX_9_CSS_SELSTAT_MASK == MC_CGM_EMAC_SELSTAT_MASK); + +/* + * Every EMAC clock domain is derived from a clock the PHY drives into the SoC, + * so these must run only once the pads are muxed: the glitchless MC_CGM mux + * refuses to switch to a source that is not toggling and silently leaves the + * domain on FIRC, which is close enough to keep framing packets but far enough + * off to corrupt every one of them. CLOCK_AttachClk() reports success either + * way, so check the status register. + */ +struct mc_cgm_emac_clk { + volatile const uint32_t *css; + uint32_t src; + clock_attach_id_t attach; + clock_div_name_t div_name; +}; + +static const struct mc_cgm_emac_clk mc_cgm_emac_rx_clk = { + .attach = MC_CGM_EMAC_RX_ATTACH, + .div_name = kCLOCK_DivEmacRxClk, + .css = &MC_CGM->MUX_7_CSS, + .src = MC_CGM_EMAC_RX_SRC, +}; + +static const struct mc_cgm_emac_clk mc_cgm_emac_tx_clk = { + .attach = MC_CGM_EMAC_TX_ATTACH, + .div_name = kCLOCK_DivEmacTxClk, + .css = &MC_CGM->MUX_8_CSS, + .src = MC_CGM_EMAC_TX_SRC, +}; + +static const struct mc_cgm_emac_clk mc_cgm_emac_ts_clk = { + .attach = MC_CGM_EMAC_TS_ATTACH, + .div_name = kCLOCK_DivEmacTsClk, + .css = &MC_CGM->MUX_9_CSS, + .src = MC_CGM_EMAC_TS_SRC, +}; + +static int mc_cgm_emac_attach(const struct mc_cgm_emac_clk *clk) +{ + /* Tell the SDK what the PHY drives into the pads; software state only. */ + CLOCK_SetEmacRmiiTxClkFreq(MC_CGM_EMAC_TXPAD_CLK_HZ); + if (MC_CGM_EMAC_RXPAD_CLK_HZ != 0U) { + CLOCK_SetEmacRxClkFreq(MC_CGM_EMAC_RXPAD_CLK_HZ); + } + + if (CLOCK_AttachClk(clk->attach) != kStatus_Success) { + return -EIO; + } + + if (FIELD_GET(MC_CGM_EMAC_SELSTAT_MASK, *clk->css) != clk->src) { + LOG_ERR("EMAC clock did not switch to source %u; " + "is the pin muxed and is the PHY driving it?", clk->src); + return -EIO; + } + + if (CLOCK_SetClkDiv(clk->div_name, MC_CGM_EMAC_CLK_DIV) != kStatus_Success) { + return -EIO; + } + + return 0; +} +#endif /* defined(MC_CGM_HAS_EMAC) */ + static int mc_cgm_clock_control_on(const struct device *dev, clock_control_subsys_t sub_system) { uint32_t clock_name = (uint32_t)sub_system; @@ -209,6 +338,14 @@ static int mc_cgm_clock_control_on(const struct device *dev, clock_control_subsy case MCUX_TEMPSENSE_CLK: CLOCK_EnableClock(kCLOCK_TempSensor); return 0; +#endif +#if defined(MC_CGM_HAS_EMAC) + case MCUX_EMACRX_CLK: + return mc_cgm_emac_attach(&mc_cgm_emac_rx_clk); + case MCUX_EMACTX_CLK: + return mc_cgm_emac_attach(&mc_cgm_emac_tx_clk); + case MCUX_EMACTS_CLK: + return mc_cgm_emac_attach(&mc_cgm_emac_ts_clk); #endif case MCUX_SIRC_CLK: return 0; @@ -237,6 +374,12 @@ static int mc_cgm_clock_control_off(const struct device *dev, clock_control_subs case MCUX_TEMPSENSE_CLK: CLOCK_DisableClock(kCLOCK_TempSensor); return 0; +#endif +#if defined(MC_CGM_HAS_EMAC) + case MCUX_EMACRX_CLK: + case MCUX_EMACTX_CLK: + case MCUX_EMACTS_CLK: + return 0; #endif case MCUX_SIRC_CLK: return 0; @@ -273,8 +416,23 @@ static int mc_cgm_get_subsys_rate(const struct device *dev, clock_control_subsys *rate = CLOCK_GetCoreClkFreq(); return 0; case MCUX_AIPSPLAT_CLK: +#if defined(MC_CGM_HAS_EMAC) + /* The EMAC CSR (register) interface is clocked from AIPS_PLAT_CLK. */ + case MCUX_EMAC_CLK: +#endif *rate = CLOCK_GetAipsPlatClkFreq(); return 0; +#if defined(MC_CGM_HAS_EMAC) + case MCUX_EMACRX_CLK: + *rate = CLOCK_GetEmacRxClkFreq(); + return 0; + case MCUX_EMACTX_CLK: + *rate = CLOCK_GetEmacTxClkFreq(); + return 0; + case MCUX_EMACTS_CLK: + *rate = CLOCK_GetEmacTsClkFreq(); + return 0; +#endif case MCUX_HSE_CLK: *rate = CLOCK_GetHseClkFreq(); return 0; diff --git a/include/zephyr/dt-bindings/clock/nxp_mc_cgm.h b/include/zephyr/dt-bindings/clock/nxp_mc_cgm.h index 160e56fb786f..ce0cfe36d51e 100644 --- a/include/zephyr/dt-bindings/clock/nxp_mc_cgm.h +++ b/include/zephyr/dt-bindings/clock/nxp_mc_cgm.h @@ -112,6 +112,8 @@ #define MCUX_EMACRX_CLK MCUX_MC_CGM_CLK_ID(0x2C, 0x01) #define MCUX_EMACTX_CLK MCUX_MC_CGM_CLK_ID(0x2C, 0x02) #define MCUX_EMACTS_CLK MCUX_MC_CGM_CLK_ID(0x2C, 0x03) +/** EMAC module clock: gates the IP and clocks its CSR (register) interface */ +#define MCUX_EMAC_CLK MCUX_MC_CGM_CLK_ID(0x2C, 0x05) #define MCUX_TEMPSENSE_CLK MCUX_MC_CGM_CLK_ID(0x2C, 0x04) From 54d88b4bf56a678f9879749bdeca2951060292d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Sun, 23 Aug 2026 19:39:27 +0200 Subject: [PATCH 196/455] drivers: ethernet: dwc_mac: select ARM_MPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit select ARM_MPU if needed for the descriptors. Signed-off-by: Fin Maaß --- drivers/ethernet/dwc_mac/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/ethernet/dwc_mac/Kconfig b/drivers/ethernet/dwc_mac/Kconfig index 514da474e7f3..999319e053bf 100644 --- a/drivers/ethernet/dwc_mac/Kconfig +++ b/drivers/ethernet/dwc_mac/Kconfig @@ -11,6 +11,9 @@ config ETH_DWC_ETHER_QOS_CORE bool depends on NET_BUF_FIXED_DATA_SIZE select CACHE_MANAGEMENT if DCACHE + # The DMA descriptor rings are not cache maintained, so they need an + # uncached region, which on ARM can only be carved out by the MPU. + select ARM_MPU if CPU_HAS_ARM_MPU && DCACHE help This is a driver for the Synopsys DesignWare Ethernet MAC 10/100/1G Quality-of-Service, also referred to as "dwc_ether_qos". @@ -20,6 +23,9 @@ config ETH_DWC_ETHER_1000_CORE bool depends on NET_BUF_FIXED_DATA_SIZE select CACHE_MANAGEMENT if DCACHE + # The DMA descriptor rings are not cache maintained, so they need an + # uncached region, which on ARM can only be carved out by the MPU. + select ARM_MPU if CPU_HAS_ARM_MPU && DCACHE help This is a driver for the Synopsys DesignWare Ethernet MAC 10/100/1G Universal, also referred to as "dwc_ether_mac10_100_1000_universal". From 58c9e2b6b81a87fb7b6a3ca04af08e88d6f6f579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Sun, 9 Aug 2026 20:16:59 +0200 Subject: [PATCH 197/455] drivers: ethernet: dwc_mac: nxp: add support for the mcxe31b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the NXP MCXE31B mcu there is currently no driver support for the ethernet controller, add it via the generic dwc_mac driver. Assisted-by: Claude:claude-opus-5 Signed-off-by: Fin Maaß --- drivers/ethernet/dwc_mac/CMakeLists.txt | 1 + drivers/ethernet/dwc_mac/Kconfig | 14 ++ .../ethernet/dwc_mac/eth_nxp_dwc_ether_qos.c | 217 ++++++++++++++++++ dts/arm/nxp/mcx/mcxe/nxp_mcxe31x_common.dtsi | 21 +- 4 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 drivers/ethernet/dwc_mac/eth_nxp_dwc_ether_qos.c diff --git a/drivers/ethernet/dwc_mac/CMakeLists.txt b/drivers/ethernet/dwc_mac/CMakeLists.txt index 2b4d10f17785..4d63a5c5e206 100644 --- a/drivers/ethernet/dwc_mac/CMakeLists.txt +++ b/drivers/ethernet/dwc_mac/CMakeLists.txt @@ -16,6 +16,7 @@ zephyr_library_sources_ifdef(CONFIG_ETH_DWC_ETHER_MULTICAST_FILTER_PERFECT eth_d # zephyr-keep-sorted-start zephyr_library_sources_ifdef(CONFIG_ETH_DWMAC_MMU eth_dwmac_mmu.c) zephyr_library_sources_ifdef(CONFIG_ETH_ESP32_DWC_ETHER_1000 eth_esp32_dwc_ether_1000.c) +zephyr_library_sources_ifdef(CONFIG_ETH_NXP_DWC_ETHER_QOS eth_nxp_dwc_ether_qos.c) zephyr_library_sources_ifdef(CONFIG_ETH_STM32_DWC_ETHER_1000 eth_stm32_dwc_ether_1000.c) zephyr_library_sources_ifdef(CONFIG_ETH_STM32_DWC_ETHER_QOS eth_stm32_dwc_ether_qos.c) # zephyr-keep-sorted-stop diff --git a/drivers/ethernet/dwc_mac/Kconfig b/drivers/ethernet/dwc_mac/Kconfig index 999319e053bf..91a01f463835 100644 --- a/drivers/ethernet/dwc_mac/Kconfig +++ b/drivers/ethernet/dwc_mac/Kconfig @@ -42,6 +42,20 @@ config ETH_ESP32_DWC_ETHER_1000 help This enables the Synopsys DesignWare MAC driver for ESP32 SoCs. +config ETH_NXP_DWC_ETHER_QOS + bool "Driver for DWC Ethernet QoS-based NXP" + depends on SOC_SERIES_MCXE31X + depends on DT_HAS_NXP_ENET_QOS_ENABLED + select ETH_DWC_ETHER_QOS_CORE + select NOCACHE_MEMORY if ARCH_HAS_NOCACHE_MEMORY_SUPPORT + select PINCTRL + select HWINFO + select CRC + default y + help + This enables the Synopsys DesignWare MAC driver on NXP MCXE series + SoCs, where the IP is named EMAC. + config ETH_STM32_DWC_ETHER_QOS bool "Driver for DWC Ethernet QoS-based STM32" depends on !ETH_STM32_HAL diff --git a/drivers/ethernet/dwc_mac/eth_nxp_dwc_ether_qos.c b/drivers/ethernet/dwc_mac/eth_nxp_dwc_ether_qos.c new file mode 100644 index 000000000000..f43a8f994e80 --- /dev/null +++ b/drivers/ethernet/dwc_mac/eth_nxp_dwc_ether_qos.c @@ -0,0 +1,217 @@ +/* + * Driver for Synopsys DesignWare MAC + * + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * + * SPDX-License-Identifier: Apache-2.0 + * + * NXP specific glue. + */ + +#include +LOG_MODULE_REGISTER(dwmac_plat, CONFIG_ETHERNET_LOG_LEVEL); + +#define DT_DRV_COMPAT nxp_enet_qos + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eth_dwmac_priv.h" + +/* The DMA bus master interface is 32-bit on this IP */ +#define DATA_BUS_WIDTH 32 + +DWMAC_ASSERT_BUFFER_ALIGNMENT(DATA_BUS_WIDTH); + +#if DT_INST_ENUM_HAS_VALUE(0, phy_connection_type, mii) +#define PHY_MODE 0U +#elif DT_INST_ENUM_HAS_VALUE(0, phy_connection_type, rmii) +#define PHY_MODE 1U +#else +#error "Unsupported PHY connection type" +#endif + +/* + * OUI used when devicetree carries no MAC address and one has to be derived + * from the chip's unique ID. NXP prints a per-board address from this same OUI + * on the board label, but does not store it anywhere the SoC can read; set + * local-mac-address in devicetree to use that one instead. + */ +#define NXP_OUI_BYTE_0 0x00 +#define NXP_OUI_BYTE_1 0x04 +#define NXP_OUI_BYTE_2 0x9f + +/* Locally administered address bit of the first MAC octet */ +#define ETH_MAC_LAA_BIT 0x02 + +PINCTRL_DT_INST_DEFINE(0); +static const struct pinctrl_dev_config *eth0_pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0); + +/* The mc_cgm clock cell is itself named "name", hence the repetition. */ +#define NXP_ETH_CLOCK_SUBSYS(clk) \ + (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(0, clk, name) + +static const clock_control_subsys_t eth0_clocks[] = { + NXP_ETH_CLOCK_SUBSYS(tx), + NXP_ETH_CLOCK_SUBSYS(rx), + NXP_ETH_CLOCK_SUBSYS(ptp), + NXP_ETH_CLOCK_SUBSYS(mac), +}; + +int dwmac_bus_init(const struct device *dev) +{ + const struct dwmac_config *cfg = dev->config; + int ret; + + /* Mux the pads first: the clocks below are derived from what they carry. */ + ret = pinctrl_apply_state(eth0_pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Could not configure ethernet pins"); + return ret; + } + + /* Select the PHY interface. */ + DCM_GPR->DCMRWF1 = (DCM_GPR->DCMRWF1 & ~DCM_GPR_DCMRWF1_RMII_MII_SEL_MASK) | + DCM_GPR_DCMRWF1_RMII_MII_SEL(PHY_MODE); + + /* + * The transmit, receive and timestamp clocks are derived from clocks the + * PHY drives into the SoC, which is why the pads are muxed first. + */ + for (size_t n = 0; n < ARRAY_SIZE(eth0_clocks); n++) { + ret = clock_control_on(cfg->clock, eth0_clocks[n]); + if (ret != 0) { + LOG_ERR("Failed to enable ethernet clock #%zu (%d)", n, ret); + return ret; + } + } + + return 0; +} + +#define DESCRIPTOR_ALIGNMENT ((DATA_BUS_WIDTH) / (BITS_PER_BYTE)) +#if defined(CONFIG_NOCACHE_MEMORY) +#define __desc_mem __nocache __aligned(DESCRIPTOR_ALIGNMENT) +#else +/* + * The core driver maintains the cache for the packet buffers but not for the + * descriptors, so those have to live in memory the DMA and the CPU see alike. + */ +BUILD_ASSERT(!IS_ENABLED(CONFIG_DCACHE), + "DMA descriptors would be cached; enable CONFIG_ARM_MPU to get a nocache region"); +#define __desc_mem __aligned(DESCRIPTOR_ALIGNMENT) +#endif + +/* Descriptor rings in uncached memory */ +static struct dwmac_dma_desc dwmac_tx_descs[NB_TX_DESCS] __desc_mem; +static struct dwmac_dma_desc dwmac_rx_descs[NB_RX_DESCS] __desc_mem; + +static int nxp_load_mac_addr(const struct net_eth_mac_config *cfg, uint8_t *mac_addr) +{ + uint8_t unique_device_id[16] = {0}; + ssize_t uuid_length; + uint32_t hash; + int ret; + + ret = net_eth_mac_load(cfg, mac_addr); + if (ret != -ENODATA) { + if (ret < 0) { + LOG_ERR("Failed to load MAC address (%d)", ret); + } + + return ret; + } + + /* + * Nothing defined by the user, hash the chip's unique ID. Note this is + * not universally unique, it just is probably unique on a network. + */ + uuid_length = hwinfo_get_device_id(unique_device_id, + sizeof(unique_device_id)); + if (uuid_length <= 0) { + /* + * Hashing the empty buffer would give every affected board the + * same address, so refuse rather than hand out a duplicate. + */ + return (uuid_length < 0) ? (int)uuid_length : -ENODATA; + } + + hash = crc24_pgp(unique_device_id, (size_t)uuid_length); + + /* Setting LAA bit because it is not guaranteed universally unique */ + mac_addr[0] = NXP_OUI_BYTE_0 | ETH_MAC_LAA_BIT; + mac_addr[1] = NXP_OUI_BYTE_1; + mac_addr[2] = NXP_OUI_BYTE_2; + mac_addr[3] = FIELD_GET(0xFF0000, hash); + mac_addr[4] = FIELD_GET(0x00FF00, hash); + mac_addr[5] = FIELD_GET(0x0000FF, hash); + + return 0; +} + +#define NXP_ETH_IRQ_CONNECT(name) \ + do { \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, name, irq), \ + DT_INST_IRQ_BY_NAME(0, name, priority), dwmac_isr, \ + DEVICE_DT_INST_GET(0), 0); \ + irq_enable(DT_INST_IRQ_BY_NAME(0, name, irq)); \ + } while (0) + +int dwmac_platform_init(const struct device *dev) +{ + const struct net_eth_mac_config mac_cfg = NET_ETH_MAC_DT_INST_CONFIG_INIT(0); + struct dwmac_priv *p = dev->data; + + p->tx_descs = dwmac_tx_descs; + p->rx_descs = dwmac_rx_descs; + + /* basic configuration for this platform */ + DWMAC_REG_WRITE(MAC_CONF, + MAC_CONF_PS | + MAC_CONF_FES | + MAC_CONF_DM); + DWMAC_REG_WRITE(DMA_SYSBUS_MODE, + DMA_SYSBUS_MODE_AAL | + DMA_SYSBUS_MODE_FB); + + /* + * Set up IRQs (still masked for now). The MAC raises DMA transfer + * completion on the dedicated tx/rx lines and everything else on the + * common line, so all three share the same handler. + */ + NXP_ETH_IRQ_CONNECT(common); + NXP_ETH_IRQ_CONNECT(tx); + NXP_ETH_IRQ_CONNECT(rx); + + return nxp_load_mac_addr(&mac_cfg, p->mac_addr); +} + +/* Our private device instance */ +static const struct dwmac_config dwmac_config = { + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(0)), + .phy_dev = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(0, phy_handle)), + .clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), + .mac_clk = NXP_ETH_CLOCK_SUBSYS(mac), +#if defined(CONFIG_PTP_CLOCK_DWC_MAC) + .ptp_clock = DEVICE_DT_GET(DT_INST_CHILD(0, ptp_clock)), + .ptp_clk = NXP_ETH_CLOCK_SUBSYS(ptp), +#endif +}; + +static struct dwmac_priv dwmac_instance; + +ETH_NET_DEVICE_DT_INST_DEFINE(0, + dwmac_probe, + NULL, + &dwmac_instance, + &dwmac_config, + CONFIG_ETH_INIT_PRIORITY, + &dwmac_api, + NET_ETH_MTU); diff --git a/dts/arm/nxp/mcx/mcxe/nxp_mcxe31x_common.dtsi b/dts/arm/nxp/mcx/mcxe/nxp_mcxe31x_common.dtsi index 72f08986f24d..ceb5ae9997a4 100644 --- a/dts/arm/nxp/mcx/mcxe/nxp_mcxe31x_common.dtsi +++ b/dts/arm/nxp/mcx/mcxe/nxp_mcxe31x_common.dtsi @@ -292,12 +292,29 @@ }; emac: emac@480000 { - compatible = "nxp,emac", "snps,dwmac"; + compatible = "nxp,enet-qos", "snps,dwmac"; reg = <0x480000 0x120c>; - interrupts = <105 0>; + clocks = <&mc_cgm MCUX_EMAC_CLK>, + <&mc_cgm MCUX_EMACTX_CLK>, + <&mc_cgm MCUX_EMACRX_CLK>, + <&mc_cgm MCUX_EMACTS_CLK>; + clock-names = "mac", "tx", "rx", "ptp"; + interrupts = <105 0>, <106 0>, <107 0>; + interrupt-names = "common", "tx", "rx"; snps,multicast-filter-bins = <64>; snps,perfect-filter-entries = <3>; status = "disabled"; + + mdio: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + ptp_clock: ptp-clock { + compatible = "snps,dwmac-ptp-clock"; + }; }; emios_0: emios@88000 { From 13d9e58382745c88667c0eeb337c0000c3cf6fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Sun, 9 Aug 2026 20:21:10 +0200 Subject: [PATCH 198/455] boards: nxp: add ethernet support for the FRDM-MCXE31B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ethernet support for the FRDM-MCXE31B board. Assisted-by: Claude:claude-opus-5 Signed-off-by: Fin Maaß --- boards/nxp/frdm_mcxe31b/Kconfig.defconfig | 9 +++++ boards/nxp/frdm_mcxe31b/doc/index.rst | 34 ++++++++++++++++ .../frdm_mcxe31b/frdm_mcxe31b-pinctrl.dtsi | 40 +++++++++++++++++++ boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts | 23 +++++++++++ boards/nxp/frdm_mcxe31b/frdm_mcxe31b.yaml | 1 + 5 files changed, 107 insertions(+) create mode 100644 boards/nxp/frdm_mcxe31b/Kconfig.defconfig diff --git a/boards/nxp/frdm_mcxe31b/Kconfig.defconfig b/boards/nxp/frdm_mcxe31b/Kconfig.defconfig new file mode 100644 index 000000000000..a098536a9b9a --- /dev/null +++ b/boards/nxp/frdm_mcxe31b/Kconfig.defconfig @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_FRDM_MCXE31B + +configdefault NET_L2_ETHERNET + default y + +endif # BOARD_FRDM_MCXE31B diff --git a/boards/nxp/frdm_mcxe31b/doc/index.rst b/boards/nxp/frdm_mcxe31b/doc/index.rst index 669f26a6ba43..c204c3e0e695 100644 --- a/boards/nxp/frdm_mcxe31b/doc/index.rst +++ b/boards/nxp/frdm_mcxe31b/doc/index.rst @@ -71,6 +71,40 @@ controller, refer to the device reference manual. +-------+-------------+---------------------------+ | PTA6 | FLEXCAN0 | CAN0 RX | +-------+-------------+---------------------------+ +| PTB4 | EMAC | Ethernet MDIO | ++-------+-------------+---------------------------+ +| PTB5 | EMAC | Ethernet MDC | ++-------+-------------+---------------------------+ +| PTC0 | EMAC | Ethernet RMII RXD1 | ++-------+-------------+---------------------------+ +| PTC1 | EMAC | Ethernet RMII RXD0 | ++-------+-------------+---------------------------+ +| PTC2 | EMAC | Ethernet RMII TXD0 | ++-------+-------------+---------------------------+ +| PTC3 | GPIO | Ethernet PHY reset | ++-------+-------------+---------------------------+ +| PTC17 | EMAC | Ethernet RMII RX_DV | ++-------+-------------+---------------------------+ +| PTD7 | EMAC | Ethernet RMII TXD1 | ++-------+-------------+---------------------------+ +| PTD11 | EMAC | Ethernet RMII REF_CLK | ++-------+-------------+---------------------------+ +| PTD12 | EMAC | Ethernet RMII TX_EN | ++-------+-------------+---------------------------+ + +Ethernet +======== + +The board carries a Microchip LAN8741 10/100 Mbit/s PHY on MDIO address 0, +connected to the MCXE31B EMAC over RMII. The EMAC is a Synopsys DesignWare +Ethernet QoS core and is driven by the generic +:zephyr_file:`drivers/ethernet/dwc_mac` driver, not by the NXP HAL based +``eth_nxp_enet_qos`` driver. + +The PHY sources the 50 MHz RMII reference clock, which the SoC takes in on +``PTD11`` and divides by two to clock the MAC's MII side. The driver also +supports MII, selected with ``phy-connection-type``, though this board is +wired for RMII. System Clock ============ diff --git a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b-pinctrl.dtsi b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b-pinctrl.dtsi index 4e6ec9687729..102007cecf6a 100644 --- a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b-pinctrl.dtsi +++ b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b-pinctrl.dtsi @@ -135,6 +135,46 @@ }; }; + pinmux_emac: pinmux_emac { + group1 { + /* RMII outputs. The slowest slew rate matches NXP's + * reference pin configuration for this board. + */ + pinmux = , + , + ; + output-enable; + slew-rate = "slowest"; + }; + + group2 { + pinmux = , + , + ; + input-enable; + }; + + group3 { + /* 50 MHz RMII reference clock supplied by the PHY */ + pinmux = ; + input-enable; + slew-rate = "slowest"; + }; + }; + + pinmux_emac_mdio: pinmux_emac_mdio { + group1 { + pinmux = <(PTB4_EMAC_MII_RMII_MDIO_O | PTB4_EMAC_MII_RMII_MDIO_I)>; + input-enable; + output-enable; + }; + + group2 { + pinmux = ; + output-enable; + }; + }; + pinmux_sai_0: pinmux_sai_0 { group1 { /* Each entry combines the pad's output mux with its diff --git a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts index af7061945af9..cec531413be4 100644 --- a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts +++ b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts @@ -186,6 +186,29 @@ status = "okay"; }; +&emac { + pinctrl-0 = <&pinmux_emac>; + pinctrl-names = "default"; + phy-connection-type = "rmii"; + phy-handle = <ð_phy>; + status = "okay"; +}; + +&mdio { + pinctrl-0 = <&pinmux_emac_mdio>; + pinctrl-names = "default"; + status = "okay"; + + /* Microchip LAN8741 */ + eth_phy: ethernet-phy@0 { + compatible = "ethernet-phy"; + reg = <0x00>; + reset-gpios = <&gpioc_l 3 GPIO_ACTIVE_LOW>; + reset-assert-duration-us = <25000>; + reset-deassertion-timeout-ms = <25>; + }; +}; + /* * SAI0 on header J1: D0 = PTB2, BCLK = PTC12, SYNC = PTC13. The * transmitter drives all three pads and each pad's input buffer feeds diff --git a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.yaml b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.yaml index e5bcccef863b..6c311f45446f 100644 --- a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.yaml +++ b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.yaml @@ -23,4 +23,5 @@ supported: - pwm - i2s - spi + - netif:eth vendor: nxp From bd2e91a55ed27a6efaf13e9ad9e79e18c2d39ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Sun, 9 Aug 2026 20:23:00 +0200 Subject: [PATCH 199/455] tests: net: ptp: add frdm_mcxe31b board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add frdm_mcxe31b board to the test. Signed-off-by: Fin Maaß --- samples/net/ptp/tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/net/ptp/tests.yaml b/samples/net/ptp/tests.yaml index bf4c9f287764..04a86b055199 100644 --- a/samples/net/ptp/tests.yaml +++ b/samples/net/ptp/tests.yaml @@ -34,3 +34,4 @@ tests: - stm32h7s78_dk/stm32h7s7xx/ext_flash_app - stm32h573i_dk - esp32p4x_function_ev_board/esp32p4/hpcore + - frdm_mcxe31b From 81eb5db08f68162cd6f54f99113aa1b6dac71a13 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 10 Aug 2026 20:29:35 +0300 Subject: [PATCH 200/455] Bluetooth: Host: Remove deprecated CONFIG_BT_FIXED_PASSKEY CONFIG_BT_FIXED_PASSKEY, bt_passkey_set() and BT_PASSKEY_INVALID were deprecated in commit 82cfb5a05602 ("Bluetooth: Host: Deprecate BT_FIXED_PASSKEY"), released in Zephyr 4.3, in favour of the app_passkey callback gated on CONFIG_BT_APP_PASSKEY. Two releases have passed, so remove them. The replacement covers what the option did: an application that returns a constant from bt_conn_auth_cb.app_passkey gets a fixed passkey, and one that returns BT_PASSKEY_RAND gets a Host generated one, which is what BT_PASSKEY_INVALID was used to fall back to. DISPLAY_FIXED() could only ever be true with a fixed passkey configured, so the four conditions using it reduce to the JUST_WORKS test they were already OR'd with. Likewise the two branches in get_io_capa() that upgraded the reported capability when a fixed passkey was set collapse to the values they already returned otherwise; the CONFIG_BT_APP_PASSKEY block above them covers that case for the replacement API. Behaviour is therefore unchanged for anyone not enabling the removed option. Drop the accompanying build warning and the hardened.csv entry, both of which existed only to steer users away from the option. Fixes: #36005 Signed-off-by: Johan Hedberg Assisted-by: Claude:claude-opus-5 --- doc/releases/release-notes-4.5.rst | 3 ++ include/zephyr/bluetooth/conn.h | 24 ------------ scripts/kconfig/hardened.csv | 1 - subsys/bluetooth/host/CMakeLists.txt | 6 --- subsys/bluetooth/host/Kconfig | 10 ----- subsys/bluetooth/host/smp.c | 57 +++++----------------------- 6 files changed, 12 insertions(+), 89 deletions(-) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index f7797c50cecc..feca32739986 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -117,6 +117,9 @@ Removed APIs and options * ``_bt_gatt_ccc`` * ``BT_GATT_CCC_INITIALIZER`` * ``CONFIG_BT_CONN_TX_MAX`` + * ``CONFIG_BT_FIXED_PASSKEY`` + * ``bt_passkey_set()`` + * ``BT_PASSKEY_INVALID`` * Mesh diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 577f19ef3fc2..69f5cdfbcadf 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -2819,30 +2819,6 @@ int bt_le_oob_get_sc_data(struct bt_conn *conn, const struct bt_le_oob_sc_data **oobd_local, const struct bt_le_oob_sc_data **oobd_remote); -/** - * DEPRECATED - use @ref BT_PASSKEY_RAND instead. Special passkey value that can be used to disable - * a previously set fixed passkey. - */ -#define BT_PASSKEY_INVALID 0xffffffff - -/** @brief Set a fixed passkey to be used for pairing. - * - * This API is only available when the CONFIG_BT_FIXED_PASSKEY - * configuration option has been enabled. - * - * Sets a fixed passkey to be used for pairing. If set, the - * pairing_confirm() callback will be called for all incoming pairings. - * - * @deprecated Use @ref BT_PASSKEY_RAND and the app_passkey callback from @ref bt_conn_auth_cb - * instead. - * - * @param passkey A valid passkey (0 - 999999) or BT_PASSKEY_INVALID - * to disable a previously set fixed passkey. - * - * @return 0 on success or a negative error code on failure. - */ -__deprecated int bt_passkey_set(unsigned int passkey); - /** Info Structure for OOB pairing */ struct bt_conn_oob_info { /** Type of OOB pairing method */ diff --git a/scripts/kconfig/hardened.csv b/scripts/kconfig/hardened.csv index 280014754b13..3d45a631b1b0 100644 --- a/scripts/kconfig/hardened.csv +++ b/scripts/kconfig/hardened.csv @@ -4,7 +4,6 @@ BOUNDS_CHECK_BYPASS_MITIGATION,y BT_CONN_DISABLE_SECURITY,n BT_KEYS_LOG_LEVEL_DBG,n BT_SMP_LOG_LEVEL_DBG,n -BT_FIXED_PASSKEY,n BT_LOG_SNIFFER_INFO,n BT_OOB_DATA_FIXED,n BT_SMP_ENFORCE_MITM,y diff --git a/subsys/bluetooth/host/CMakeLists.txt b/subsys/bluetooth/host/CMakeLists.txt index 8d25cbf64852..c8958ef7793e 100644 --- a/subsys/bluetooth/host/CMakeLists.txt +++ b/subsys/bluetooth/host/CMakeLists.txt @@ -90,12 +90,6 @@ if(CONFIG_BT_SMP_LOG_LEVEL_DBG OR CONFIG_BT_KEYS_LOG_LEVEL_DBG OR CONFIG_BT_LOG_ production." ) endif() -if(CONFIG_BT_FIXED_PASSKEY) - message(WARNING "CONFIG_BT_FIXED_PASSKEY is enabled - A fixed passkey is easy to deduce during the pairing procedure, do not use in - production." - ) -endif() if(CONFIG_BT_OOB_DATA_FIXED) message(WARNING "CONFIG_BT_OOB_DATA_FIXED is enabled. A hardcoded OOB data set will be stored in the image, do not use in diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 3a44336f75dc..066f09503a7b 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -670,18 +670,8 @@ config BT_SMP_USB_HCI_CTLR_WORKAROUND It opens up for a potential vulnerability as the central cannot detect if the keys are distributed over an encrypted link. -config BT_FIXED_PASSKEY - bool "Use a fixed passkey for pairing [DEPRECATED]" - select DEPRECATED - help - This option is deprecated, use BT_APP_PASSKEY instead. - With this option enabled, the application will be able to call the - bt_passkey_set() API to set a fixed passkey. If set, the - pairing_confirm() callback will be called for all incoming pairings. - config BT_APP_PASSKEY bool "Allow the application to provide passkeys for pairing" - depends on !BT_FIXED_PASSKEY help With this option enabled, the application will be able to provide passkeys for pairing using the app_passkey() callback. If the application does not provide a passkey, a diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index 185ff02e852b..5750c33fd238 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -222,12 +222,6 @@ struct bt_smp { atomic_t bondable; }; -static unsigned int fixed_passkey = BT_PASSKEY_RAND; - -#define DISPLAY_FIXED(smp) (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && \ - fixed_passkey != BT_PASSKEY_RAND && \ - (smp)->method == PASSKEY_DISPLAY) - #if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) /* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */ static const uint8_t gen_method_legacy[5 /* remote */][5 /* local */] = { @@ -378,11 +372,7 @@ static uint8_t get_io_capa(struct bt_smp *smp) #endif /* CONFIG_BT_APP_PASSKEY */ if (smp_auth_cb->passkey_entry) { - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { - return BT_SMP_IO_KEYBOARD_DISPLAY; - } else { - return BT_SMP_IO_KEYBOARD_ONLY; - } + return BT_SMP_IO_KEYBOARD_ONLY; } if (smp_auth_cb->passkey_display) { @@ -390,11 +380,7 @@ static uint8_t get_io_capa(struct bt_smp *smp) } no_callbacks: - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { - return BT_SMP_IO_DISPLAY_ONLY; - } else { - return BT_SMP_IO_NO_INPUT_OUTPUT; - } + return BT_SMP_IO_NO_INPUT_OUTPUT; } #if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) @@ -2511,22 +2497,18 @@ static uint8_t legacy_request_tk(struct bt_smp *smp) break; case PASSKEY_DISPLAY: { - uint32_t passkey; + uint32_t passkey = BT_PASSKEY_RAND; - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { - passkey = fixed_passkey; #if defined(CONFIG_BT_APP_PASSKEY) - } else if (smp_auth_cb && smp_auth_cb->app_passkey) { + if (smp_auth_cb && smp_auth_cb->app_passkey) { passkey = smp_auth_cb->app_passkey(conn); if (passkey != BT_PASSKEY_RAND && passkey > 999999) { LOG_WRN("App-provided passkey is out of valid range: %u", passkey); return BT_SMP_ERR_UNSPECIFIED; } -#endif /* CONFIG_BT_APP_PASSKEY */ - } else { - passkey = BT_PASSKEY_RAND; } +#endif /* CONFIG_BT_APP_PASSKEY */ if (passkey == BT_PASSKEY_RAND) { if (bt_rand(&passkey, sizeof(passkey))) { @@ -2603,7 +2585,7 @@ static uint8_t legacy_pairing_req(struct bt_smp *smp) } /* ask for consent if pairing is not due to sending SecReq*/ - if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + if (smp->method == JUST_WORKS && !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && smp_auth_cb && smp_auth_cb->pairing_confirm) { atomic_set_bit(smp->flags, SMP_FLAG_USER); @@ -2843,7 +2825,7 @@ static uint8_t legacy_pairing_rsp(struct bt_smp *smp) } /* ask for consent if this is due to received SecReq */ - if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + if (smp->method == JUST_WORKS && atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && smp_auth_cb && smp_auth_cb->pairing_confirm) { atomic_set_bit(smp->flags, SMP_FLAG_USER); @@ -3299,7 +3281,7 @@ static uint8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf) } if (!IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) && - (DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + smp->method == JUST_WORKS && !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && smp_auth_cb && smp_auth_cb->pairing_confirm) { atomic_set_bit(smp->flags, SMP_FLAG_USER); @@ -3542,7 +3524,7 @@ static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf) } if (!IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) && - (DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + smp->method == JUST_WORKS && atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && smp_auth_cb && smp_auth_cb->pairing_confirm) { atomic_set_bit(smp->flags, SMP_FLAG_USER); @@ -4455,10 +4437,6 @@ __maybe_unused static uint8_t display_passkey(struct bt_smp *smp) const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp); uint32_t passkey = BT_PASSKEY_RAND; - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { - passkey = fixed_passkey; - } - #if defined(CONFIG_BT_APP_PASSKEY) if (smp_auth_cb && smp_auth_cb->app_passkey) { passkey = smp_auth_cb->app_passkey(conn); @@ -6285,23 +6263,6 @@ int bt_smp_auth_pairing_confirm(struct bt_conn *conn) } #endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ -#if defined(CONFIG_BT_FIXED_PASSKEY) -int bt_passkey_set(unsigned int passkey) -{ - if (passkey == BT_PASSKEY_INVALID || passkey == BT_PASSKEY_RAND) { - fixed_passkey = BT_PASSKEY_RAND; - return 0; - } - - if (passkey > 999999) { - return -EINVAL; - } - - fixed_passkey = passkey; - return 0; -} -#endif /* CONFIG_BT_FIXED_PASSKEY */ - int bt_smp_start_security(struct bt_conn *conn) { switch (conn->role) { From e3414c1174d74faf34323969f97729b04d2be3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:23:46 +0000 Subject: [PATCH 201/455] drivers: sensor: lsm6dsv16x: propagate sample_fetch errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lsm6dsv16x_sample_fetch() accumulated the return value of the accel/gyro/temp/shub fetch helpers in 'ret' but ended with 'return 0;', so bus errors during a fetch were reported as success and stale data could be served by channel_get. Return 'ret' instead, matching the sibling lsm6dso driver. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c index 35e1ec8b407e..2ebaa00d5fd4 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c @@ -981,7 +981,7 @@ static int lsm6dsv16x_sample_fetch(const struct device *dev, return -ENOTSUP; } - return 0; + return ret; } static inline void lsm6dsv16x_accel_convert(struct sensor_value *val, int raw_val, From 391abe5392eade999d9f410d8b8274081149e48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:25:06 +0000 Subject: [PATCH 202/455] drivers: sensor: lsm6dsv16x: fix UTIL_AND typo in I3C streaming guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The I3C instantiation macro used the non-existent UNTIL_AND instead of UTIL_AND, making IF_ENABLED silently expand to nothing and dropping the .rtio_ctx, .iodev and .bus_type initializers, so I3C streaming faulted on the first interrupt via a NULL RTIO context. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c index 2ebaa00d5fd4..a9948f1e2eb0 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c @@ -1653,8 +1653,8 @@ static int lsm6dsv16x_pm_action(const struct device *dev, enum pm_device_action CONFIG_I3C_RTIO), \ (LSM6DSV16X_I3C_RTIO_DEFINE(inst, prefix))); \ static struct lsm6dsv16x_data prefix##_data_##inst = { \ - IF_ENABLED(UNTIL_AND(CONFIG_LSM6DSV16X_STREAM, \ - CONFIG_I3C_RTIO), \ + IF_ENABLED(UTIL_AND(CONFIG_LSM6DSV16X_STREAM, \ + CONFIG_I3C_RTIO), \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \ .iodev = &prefix##_i3c_iodev_##inst, \ .bus_type = RTIO_BUS_I3C,)) \ From e748ef965e65662aa7b596677b6615ed28b2c7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:24:52 +0000 Subject: [PATCH 203/455] drivers: sensor: lsm6dsv16x: fix accel FS index in drdy streaming path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The data-ready streaming path stored the raw full-scale register index in accel_fs_idx, while the decoder tables are ordered by g-value. On LSM6DSV32X this made decoded accel samples half their true value. Convert with LSM6DSV16X_ACCEL_FS_VAL_TO_FS_IDX() as the one-shot and FIFO paths already do. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c index 6312f690ddc1..d5f3f68db088 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c @@ -510,9 +510,7 @@ static void lsm6dsv16x_read_status_cb(struct rtio *r, const struct rtio_sqe *sqe ARG_UNUSED(result); const struct device *dev = arg; -#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c) const struct lsm6dsv16x_config *config = dev->config; -#endif struct lsm6dsv16x_data *lsm6dsv16x = dev->data; struct rtio *rtio = lsm6dsv16x->rtio_ctx; struct gpio_dt_spec *irq_gpio = lsm6dsv16x->drdy_gpio; @@ -608,7 +606,8 @@ static void lsm6dsv16x_read_status_cb(struct rtio *r, const struct rtio_sqe *sqe struct lsm6dsv16x_rtio_data hdr = { .header = { .is_fifo = false, - .accel_fs_idx = lsm6dsv16x->accel_fs, + .accel_fs_idx = LSM6DSV16X_ACCEL_FS_VAL_TO_FS_IDX( + config->accel_fs_map[lsm6dsv16x->accel_fs]), .gyro_fs = lsm6dsv16x->gyro_fs, .timestamp = lsm6dsv16x->timestamp, }, From fb30cb59206a922cddd743751b2d22fc27ec334d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:26:02 +0000 Subject: [PATCH 204/455] drivers: sensor: lsm6dsv16x: fix OOB odr_map index for high-accuracy ODR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit data->accel_freq and data->gyro_freq hold the full mode-encoded ODR byte (mode in the upper nibble, e.g. 0x18 for HA01 500 Hz), so using them directly as a column index into lsm6dsv16x_odr_map[3][13] reads out of bounds for high-accuracy devicetree ODRs. Mask to the low nibble, which is the actual CTRL ODR field; the mode nibble is already handled via the row index. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c index a9948f1e2eb0..790b2283eedc 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c @@ -746,7 +746,7 @@ static int lsm6dsv16x_accel_get_config(const struct device *dev, mode = (odr >> 4) & 0xf; - val->val1 = lsm6dsv16x_odr_map[mode][data->accel_freq]; + val->val1 = lsm6dsv16x_odr_map[mode][data->accel_freq & 0x0f]; val->val2 = 0; break; } @@ -824,7 +824,7 @@ static int lsm6dsv16x_gyro_get_config(const struct device *dev, mode = (odr >> 4) & 0xf; - val->val1 = lsm6dsv16x_odr_map[mode][data->gyro_freq]; + val->val1 = lsm6dsv16x_odr_map[mode][data->gyro_freq & 0x0f]; val->val2 = 0; break; } From 8a9d9c6b3ae0bbd4ded736756a9b2fc2bcc546ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:25:42 +0000 Subject: [PATCH 205/455] drivers: sensor: lsm6dsv16x: fix fractional part of humidity value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lsm6dsv16x_hum_convert() stored the entire relative humidity value in micro-percent in val2, so converting the sensor_value back to a float roughly doubled the reading. Store only the fractional remainder in val2, as required by the sensor_value contract. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c index 790b2283eedc..0b15466aa1e5 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c @@ -1170,8 +1170,8 @@ static inline void lsm6dsv16x_hum_convert(struct sensor_value *val, rh /= (ht->x1 - ht->x0); /* convert humidity to integer and fractional part */ - val->val1 = rh; - val->val2 = rh * 1000000; + val->val1 = (int32_t)rh; + val->val2 = (rh - (int32_t)rh) * 1000000; } static inline void lsm6dsv16x_press_convert(struct sensor_value *val, From 42be044ae82b545ea40a9fc058c1209ee6110f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:28:41 +0000 Subject: [PATCH 206/455] drivers: sensor: lsm6dsv16x: fix FIFO sensor hub tag type mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FIFO decoder mapped the SLAVEn tag index directly onto the compile-time sensor hub list, ignoring the runtime detected-device mapping (shub_ext), so frames were decoded as the wrong channel when a compiled-in external sensor was not populated. Carry shub_ext and num_ext_dev in the FIFO header and index through them, and fix the off-by-one bounds check in lsm6dsv16x_shub_type(). Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.c | 12 ++++++++++-- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.h | 4 ++++ .../sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c | 5 +++++ drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_shub.c | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.c index 66791fd306ab..c494861abebf 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.c @@ -257,7 +257,11 @@ static int lsm6dsv16x_decoder_get_frame_count(const uint8_t *buffer, case LSM6DSV16X_SENSORHUB_SLAVE2_TAG: case LSM6DSV16X_SENSORHUB_SLAVE3_TAG: { uint8_t k = fifo_tag - LSM6DSV16X_SENSORHUB_SLAVE1_TAG; - enum sensor_channel ctype = lsm6dsv16x_shub_type(k); + enum sensor_channel ctype = SENSOR_CHAN_COMMON_COUNT; + + if (k < edata->num_ext_dev) { + ctype = lsm6dsv16x_shub_type(edata->shub_ext[k]); + } switch (ctype) { case SENSOR_CHAN_MAGN_XYZ: @@ -618,7 +622,11 @@ static int lsm6dsv16x_decode_fifo(const uint8_t *buffer, struct sensor_chan_spec case LSM6DSV16X_SENSORHUB_SLAVE2_TAG: case LSM6DSV16X_SENSORHUB_SLAVE3_TAG: { uint8_t k = fifo_tag - LSM6DSV16X_SENSORHUB_SLAVE1_TAG; - enum sensor_channel ctype = lsm6dsv16x_shub_type(k); + enum sensor_channel ctype = SENSOR_CHAN_COMMON_COUNT; + + if (k < edata->num_ext_dev) { + ctype = lsm6dsv16x_shub_type(edata->shub_ext[k]); + } if ((uintptr_t)buffer < *fit) { /* This frame was already decoded, move on to the next frame */ diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.h b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.h index 4ec25ec974f3..ea094861dd84 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.h +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_decoder.h @@ -37,6 +37,10 @@ struct lsm6dsv16x_fifo_data { uint16_t temp_batch_odr: 4; uint16_t sflp_batch_odr: 3; uint16_t reserved_2: 1; +#if defined(CONFIG_LSM6DSV16X_SENSORHUB) + uint8_t num_ext_dev; + uint8_t shub_ext[LSM6DSV16X_SHUB_MAX_NUM_TARGETS]; +#endif } __attribute__((__packed__)); struct lsm6dsv16x_rtio_data { diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c index d5f3f68db088..1e4128be8248 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c @@ -465,6 +465,11 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, }; /* clang-format on */ +#if defined(CONFIG_LSM6DSV16X_SENSORHUB) + hdr.num_ext_dev = lsm6dsv16x->num_ext_dev; + memcpy(hdr.shub_ext, lsm6dsv16x->shub_ext, sizeof(hdr.shub_ext)); +#endif + memcpy(buf, &hdr, sizeof(hdr)); read_buf = buf + sizeof(hdr); diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_shub.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_shub.c index 0244d7515979..d5c9362ee048 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_shub.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_shub.c @@ -496,7 +496,7 @@ enum sensor_channel lsm6dsv16x_shub_type(uint8_t k) { struct lsm6dsv16x_shub_slist *sp; - if (k > LSM6DSV16X_SHUB_MAX_NUM_TARGETS) { + if (k >= ARRAY_SIZE(lsm6dsv16x_shub_slist)) { return SENSOR_CHAN_COMMON_COUNT; } From 0be3aba9e101a653da3e9c5da651fc33c73c9a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 10 Aug 2026 23:26:42 +0000 Subject: [PATCH 207/455] drivers: sensor: lsm6dsv16x: fix double completion of iodev_sqe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On sensor_clock_get_cycles() failure, the submit-sample path called rtio_iodev_sqe_err() and then jumped to the shared err label, which called rtio_iodev_sqe_err() again on the same (already freed) SQE. Drop the first call so the err block performs the single completion. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio.c index ea11290d86b3..81568ddc5daa 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio.c @@ -117,7 +117,6 @@ static void lsm6dsv16x_submit_sample(const struct device *dev, struct rtio_iodev rc = sensor_clock_get_cycles(&cycles); if (rc != 0) { LOG_ERR("Failed to get sensor clock cycles"); - rtio_iodev_sqe_err(iodev_sqe, rc); goto err; } From af09c37a036654719c38d083ee562b8ad594c203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 11 Aug 2026 03:00:03 +0000 Subject: [PATCH 208/455] drivers: sensor: lsm6dsv16x: preserve interrupt routes in stream config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The streaming path wrote full interrupt-route structs containing only its own bit, so arming the DRDY stream trigger cleared the FIFO watermark/full routing and vice versa, silently losing events when both are requested. Read-modify-write the route as the trigger path already does. Also honor the trigger disable flag in the DRDY path, which unconditionally enabled the route even when asked to disable it. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- .../st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c index 1e4128be8248..4f8b94bcbe8e 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c @@ -20,18 +20,20 @@ static void lsm6dsv16x_config_drdy(const struct device *dev, struct trigger_conf { const struct lsm6dsv16x_config *config = dev->config; stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx; - lsm6dsv16x_pin_int_route_t pin_int = { 0 }; + lsm6dsv16x_pin_int_route_t pin_int; int16_t buf[3]; /* dummy read: re-trigger interrupt */ lsm6dsv16x_acceleration_raw_get(ctx, buf); - pin_int.drdy_xl = PROPERTY_ENABLE; - - /* Set pin interrupt */ + /* Set pin interrupt, preserving already routed sources */ if ((config->drdy_pin == 1) || (ON_I3C_BUS(config) && (!I3C_INT_PIN(config)))) { + lsm6dsv16x_pin_int1_route_get(ctx, &pin_int); + pin_int.drdy_xl = trig_cfg.int_drdy ? PROPERTY_ENABLE : PROPERTY_DISABLE; lsm6dsv16x_pin_int1_route_set(ctx, &pin_int); } else { + lsm6dsv16x_pin_int2_route_get(ctx, &pin_int); + pin_int.drdy_xl = trig_cfg.int_drdy ? PROPERTY_ENABLE : PROPERTY_DISABLE; lsm6dsv16x_pin_int2_route_set(ctx, &pin_int); } } @@ -82,6 +84,7 @@ static void lsm6dsv16x_config_fifo(const struct device *dev, struct trigger_conf stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx; uint8_t fifo_wtm = 0; lsm6dsv16x_pin_int_route_t pin_int = { 0 }; + lsm6dsv16x_pin_int_route_t route; lsm6dsv16x_fifo_xl_batch_t xl_batch = LSM6DSVXXX_DT_XL_NOT_BATCHED; lsm6dsv16x_fifo_gy_batch_t gy_batch = LSM6DSVXXX_DT_GY_NOT_BATCHED; lsm6dsv16x_fifo_temp_batch_t temp_batch = LSM6DSVXXX_DT_TEMP_NOT_BATCHED; @@ -206,11 +209,17 @@ static void lsm6dsv16x_config_fifo(const struct device *dev, struct trigger_conf lsm6dsv16x_sh_master_set(ctx, PROPERTY_ENABLE); #endif /* CONFIG_LSM6DSV16X_SENSORHUB */ - /* Set pin interrupt (fifo_th could be on or off) */ + /* Set pin interrupt (fifo_th could be on or off), preserving already routed sources */ if ((config->drdy_pin == 1) || (ON_I3C_BUS(config) && (!I3C_INT_PIN(config)))) { - lsm6dsv16x_pin_int1_route_set(ctx, &pin_int); + lsm6dsv16x_pin_int1_route_get(ctx, &route); + route.fifo_th = pin_int.fifo_th; + route.fifo_full = pin_int.fifo_full; + lsm6dsv16x_pin_int1_route_set(ctx, &route); } else { - lsm6dsv16x_pin_int2_route_set(ctx, &pin_int); + lsm6dsv16x_pin_int2_route_get(ctx, &route); + route.fifo_th = pin_int.fifo_th; + route.fifo_full = pin_int.fifo_full; + lsm6dsv16x_pin_int2_route_set(ctx, &route); } } From 457ca4ab0ae2aaac1908bcc52b9b729bab45ca6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 14:31:07 +0100 Subject: [PATCH 209/455] instrumentation: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- subsys/instrumentation/common/instr_common.c | 39 ++------- subsys/instrumentation/include/instr_buffer.h | 85 +------------------ .../instrumentation/ringbuffer/ringbuffer.c | 44 +--------- 3 files changed, 15 insertions(+), 153 deletions(-) diff --git a/subsys/instrumentation/common/instr_common.c b/subsys/instrumentation/common/instr_common.c index 71e6d0155d47..9c78e0879b25 100644 --- a/subsys/instrumentation/common/instr_common.c +++ b/subsys/instrumentation/common/instr_common.c @@ -296,7 +296,7 @@ void instr_dump_buffer_uart(void) DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); uint8_t *transferring_buf; - uint32_t transferring_length, instr_buffer_max_length; + uint32_t transferring_length; /* Make sure instrumentation is disabled. */ instr_disable(); @@ -304,19 +304,15 @@ void instr_dump_buffer_uart(void) /* Initiator mark */ printk("-*-#"); - instr_buffer_max_length = instr_buffer_capacity_get(); - - while (!instr_buffer_is_empty()) { + while (!ring_buf_is_empty(instr_buffer_get_ring_buf())) { transferring_length = - instr_buffer_get_claim( - &transferring_buf, - instr_buffer_max_length); + ring_buf_get_ptr(instr_buffer_get_ring_buf(), &transferring_buf, 0); for (uint32_t i = 0; i < transferring_length; i++) { uart_poll_out(uart_dev, transferring_buf[i]); } - instr_buffer_get_finish(transferring_length); + ring_buf_consume(instr_buffer_get_ring_buf(), transferring_length); } /* Terminator mark */ @@ -506,30 +502,12 @@ static void set_up_record(struct instr_record *record, enum instr_event_types ty static bool instr_record_data_put(struct instr_record *record) { - uint32_t total_size = 0U; - - uint8_t *data = (uint8_t *) record, *buf; - uint32_t length = sizeof(struct instr_record), claimed_size; - /* If record won't fit, free enough space in the buffer */ - if (instr_buffer_space_get() < sizeof(struct instr_record)) { - instr_buffer_get(NULL, sizeof(struct instr_record)); - } - - do { - claimed_size = instr_buffer_put_claim(&buf, length); - memcpy(buf, data, claimed_size); - total_size += claimed_size; - length -= claimed_size; - data += claimed_size; - } while (length && claimed_size); - - if (length && claimed_size == 0) { - instr_buffer_put_finish(0); - return false; + if (ring_buf_space_get(instr_buffer_get_ring_buf()) < sizeof(struct instr_record)) { + ring_buf_consume(instr_buffer_get_ring_buf(), sizeof(struct instr_record)); } - instr_buffer_put_finish(total_size); + ring_buf_put(instr_buffer_get_ring_buf(), (uint8_t *)record, sizeof(struct instr_record)); return true; } @@ -585,7 +563,8 @@ void instr_event_handler(enum instr_event_types type, void *callee, void *caller struct instr_record record; if (!IS_ENABLED(CONFIG_INSTRUMENTATION_MODE_CALLGRAPH_BUFFER_OVERWRITE) && - instr_buffer_space_get() < sizeof(struct instr_record)) { + ring_buf_space_get(instr_buffer_get_ring_buf()) < + sizeof(struct instr_record)) { _instr_tracing_disabled = true; return; } diff --git a/subsys/instrumentation/include/instr_buffer.h b/subsys/instrumentation/include/instr_buffer.h index 08392d0ff689..8497da2bfe31 100644 --- a/subsys/instrumentation/include/instr_buffer.h +++ b/subsys/instrumentation/include/instr_buffer.h @@ -9,6 +9,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -20,89 +21,11 @@ extern "C" { void instr_buffer_init(void); /** - * @brief Instrumentation buffer is empty or not. + * @brief Get the instrumentation ring buffer. * - * @return true if the ring buffer is empty, or false if not. + * @return Pointer to the instrumentation ring buffer. */ -bool instr_buffer_is_empty(void); - -/** - * @brief Get free space in the instrumentation buffer. - * - * @return Instrumentation buffer free space (in bytes). - */ -uint32_t instr_buffer_space_get(void); - -/** - * @brief Get instrumentation buffer capacity (max size). - * - * @return Instrumentation buffer capacity (in bytes). - */ -uint32_t instr_buffer_capacity_get(void); - -/** - * @brief Try to allocate buffer in the instrumentation buffer. - * - * @param data Pointer to the address. It's set to a location - * within the instrumentation buffer. - * @param size Requested buffer size (in bytes). - * - * @return Size of allocated buffer which can be smaller than - * requested if there isn't enough free space or buffer wraps. - */ -uint32_t instr_buffer_put_claim(uint8_t **data, uint32_t size); - -/** - * @brief Indicate number of bytes written to the allocated buffer. - * - * @param size Number of bytes written to the allocated buffer. - * - * @retval 0 Successful operation. - * @retval -EINVAL Given @a size exceeds free space of instrumentation buffer. - */ -int instr_buffer_put_finish(uint32_t size); - -/** - * @brief Write data to instrumentation buffer. - * - * @param data Address of data. - * @param size Data size (in bytes). - * - * @retval Number of bytes written to instrumentation buffer. - */ -uint32_t instr_buffer_put(uint8_t *data, uint32_t size); - -/** - * @brief Get address of the first valid data in instrumentation buffer. - * - * @param data Pointer to the address. It's set to a location pointing to - * the first valid data within the instrumentation buffer. - * @param size Requested buffer size (in bytes). - * - * @return Size of valid buffer which can be smaller than requested - * if there isn't enough valid data or buffer wraps. - */ -uint32_t instr_buffer_get_claim(uint8_t **data, uint32_t size); - -/** - * @brief Indicate number of bytes read from claimed buffer. - * - * @param size Number of bytes read from claimed buffer. - * - * @retval 0 Successful operation. - * @retval -EINVAL Given @a size exceeds available data of instrumentation buffer. - */ -int instr_buffer_get_finish(uint32_t size); - -/** - * @brief Read data from instrumentation buffer to output buffer. - * - * @param data Address of the output buffer. - * @param size Data size (in bytes). - * - * @retval Number of bytes written to the output buffer. - */ -uint32_t instr_buffer_get(uint8_t *data, uint32_t size); +struct ring_buf *instr_buffer_get_ring_buf(void); #ifdef __cplusplus } diff --git a/subsys/instrumentation/ringbuffer/ringbuffer.c b/subsys/instrumentation/ringbuffer/ringbuffer.c index afac0c6eed68..99600a18d28b 100644 --- a/subsys/instrumentation/ringbuffer/ringbuffer.c +++ b/subsys/instrumentation/ringbuffer/ringbuffer.c @@ -10,34 +10,9 @@ static struct ring_buf instr_ring_buf; static uint8_t instr_buffer[CONFIG_INSTRUMENTATION_MODE_CALLGRAPH_TRACE_BUFFER_SIZE + 1]; -uint32_t instr_buffer_put_claim(uint8_t **data, uint32_t size) +struct ring_buf *instr_buffer_get_ring_buf(void) { - return ring_buf_put_claim(&instr_ring_buf, data, size); -} - -int instr_buffer_put_finish(uint32_t size) -{ - return ring_buf_put_finish(&instr_ring_buf, size); -} - -uint32_t instr_buffer_put(uint8_t *data, uint32_t size) -{ - return ring_buf_put(&instr_ring_buf, data, size); -} - -uint32_t instr_buffer_get_claim(uint8_t **data, uint32_t size) -{ - return ring_buf_get_claim(&instr_ring_buf, data, size); -} - -int instr_buffer_get_finish(uint32_t size) -{ - return ring_buf_get_finish(&instr_ring_buf, size); -} - -uint32_t instr_buffer_get(uint8_t *data, uint32_t size) -{ - return ring_buf_get(&instr_ring_buf, data, size); + return &instr_ring_buf; } void instr_buffer_init(void) @@ -45,18 +20,3 @@ void instr_buffer_init(void) ring_buf_init(&instr_ring_buf, sizeof(instr_buffer), instr_buffer); } - -bool instr_buffer_is_empty(void) -{ - return ring_buf_is_empty(&instr_ring_buf); -} - -uint32_t instr_buffer_capacity_get(void) -{ - return ring_buf_capacity_get(&instr_ring_buf); -} - -uint32_t instr_buffer_space_get(void) -{ - return ring_buf_space_get(&instr_ring_buf); -} From 42087db7de92dcdcb31eccc02c056a1c5211e0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Sun, 21 Jun 2026 13:31:53 +0200 Subject: [PATCH 210/455] net: ssh: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate the SSH transport TX path off the deprecated claim/finish API to the new _ptr/commit/consume pattern. Signed-off-by: Måns Ansgariusson --- subsys/net/lib/ssh/ssh_transport.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/subsys/net/lib/ssh/ssh_transport.c b/subsys/net/lib/ssh/ssh_transport.c index a99de2928876..3bac30cf796b 100644 --- a/subsys/net/lib/ssh/ssh_transport.c +++ b/subsys/net/lib/ssh/ssh_transport.c @@ -19,6 +19,7 @@ LOG_MODULE_REGISTER(ssh, CONFIG_SSH_LOG_LEVEL); #include #include +#include #include @@ -265,8 +266,7 @@ int ssh_transport_input(struct ssh_transport *transport) switch (transport->recv_state) { case SSH_RECV_STATE_IDENTITY_INIT: { *rx_pkt = (struct ssh_payload) { - .size = MIN(sizeof(transport->rx_buf), - SSH_IDENTITY_MAX_LEN), + .size = min(sizeof(transport->rx_buf), SSH_IDENTITY_MAX_LEN), .len = 0, .data = transport->rx_buf }; @@ -667,19 +667,19 @@ int update_channels(struct ssh_transport *transport) /* Send any pending data */ while (channel->tx_window_rem > 0 && !ring_buf_is_empty(&channel->tx_ring_buf)) { uint8_t *data; - uint32_t len = MIN(channel->tx_mtu, channel->tx_window_rem); + uint32_t len = min(channel->tx_mtu, channel->tx_window_rem); struct ssh_channel_event event; /* Assuming up to 256 bytes overhead for headers and random padding */ BUILD_ASSERT(sizeof(transport->tx_buf) > 256); - len = MIN(len, sizeof(transport->tx_buf) - 256); - len = ring_buf_get_claim(&channel->tx_ring_buf, &data, len); + len = min(len, sizeof(transport->tx_buf) - 256); + len = min(ring_buf_get_ptr(&channel->tx_ring_buf, &data, 0), len); channel->tx_window_rem -= len; ret = ssh_connection_send_channel_data( transport, channel->remote_channel, data, len); - ring_buf_get_finish(&channel->tx_ring_buf, len); + ring_buf_consume(&channel->tx_ring_buf, len); if (ret != 0) { /* Close channel? */ break; @@ -694,20 +694,20 @@ int update_channels(struct ssh_transport *transport) while (channel->tx_window_rem > 0 && !ring_buf_is_empty(&channel->tx_stderr_ring_buf)) { uint8_t *data; - uint32_t len = MIN(channel->tx_mtu, channel->tx_window_rem); + uint32_t len = min(channel->tx_mtu, channel->tx_window_rem); struct ssh_channel_event event; /* Assuming up to 256 bytes overhead for headers and random padding */ BUILD_ASSERT(sizeof(transport->tx_buf) > 256); - len = MIN(len, sizeof(transport->tx_buf) - 256); - len = ring_buf_get_claim(&channel->tx_stderr_ring_buf, &data, len); + len = min(len, sizeof(transport->tx_buf) - 256); + len = min(ring_buf_get_ptr(&channel->tx_stderr_ring_buf, &data, 0), len); channel->tx_window_rem -= len; ret = ssh_connection_send_channel_extended_data( transport, channel->remote_channel, SSH_EXTENDED_DATA_STDERR, data, len); - ring_buf_get_finish(&channel->tx_stderr_ring_buf, len); + ring_buf_consume(&channel->tx_stderr_ring_buf, len); if (ret != 0) { /* Close channel? */ break; @@ -723,7 +723,7 @@ int update_channels(struct ssh_transport *transport) if (channel->rx_window_rem == 0) { uint32_t available_space; - available_space = MIN(ring_buf_space_get(&channel->rx_ring_buf), + available_space = min(ring_buf_space_get(&channel->rx_ring_buf), ring_buf_space_get(&channel->rx_stderr_ring_buf)); if (available_space > 0) { channel->rx_window_rem = available_space; From e60007a219bc76dc0b25c218ecd9f968cabcacfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 13:30:22 +0100 Subject: [PATCH 211/455] openthread: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- modules/openthread/platform/uart.c | 53 +++++++++++------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/modules/openthread/platform/uart.c b/modules/openthread/platform/uart.c index fea39030b657..f9a36a172d0c 100644 --- a/modules/openthread/platform/uart.c +++ b/modules/openthread/platform/uart.c @@ -57,34 +57,28 @@ static uint16_t write_length; static void uart_rx_handle(const struct device *dev) { + int rc; uint8_t *data; - uint32_t len; - uint32_t rd_len; bool new_data = false; - do { - len = ring_buf_put_claim( - ot_uart.rx_ringbuf, &data, - ot_uart.rx_ringbuf->size); - if (len > 0) { - rd_len = uart_fifo_read(dev, data, len); - if (rd_len > 0) { - new_data = true; - } - - int err = ring_buf_put_finish( - ot_uart.rx_ringbuf, rd_len); - (void)err; - __ASSERT_NO_MSG(err == 0); - } else { - uint8_t dummy; + while (true) { + rc = ring_buf_put_ptr(ot_uart.rx_ringbuf, &data, 0); + if (rc == 0) { + uint8_t discard; - /* No space in the ring buffer - consume byte. */ LOG_WRN("RX ring buffer full."); - - rd_len = uart_fifo_read(dev, &dummy, 1); + if (uart_fifo_read(dev, &discard, 1) <= 0) { + break; + } + continue; + } + rc = uart_fifo_read(dev, data, rc); + if (rc <= 0) { + break; } - } while (rd_len && (rd_len == len)); + ring_buf_commit(ot_uart.rx_ringbuf, rc); + new_data = true; + } if (new_data) { otSysEventSignalPending(); @@ -189,21 +183,12 @@ void otPlatUartSendDone(void) void platformUartProcess(otInstance *aInstance) { uint32_t len = 0; - const uint8_t *data; + uint8_t *data; /* Process UART RX */ - while ((len = ring_buf_get_claim( - ot_uart.rx_ringbuf, - (uint8_t **)&data, - ot_uart.rx_ringbuf->size)) > 0) { - int err; - + while ((len = ring_buf_get_ptr(ot_uart.rx_ringbuf, &data, 0)) > 0) { otPlatUartReceived(data, len); - err = ring_buf_get_finish( - ot_uart.rx_ringbuf, - len); - (void)err; - __ASSERT_NO_MSG(err == 0); + ring_buf_consume(ot_uart.rx_ringbuf, len); } /* Process UART TX */ From 792a2b44be4167bd246eb6490ed1ffc3b8f2e05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Tue, 11 Aug 2026 08:56:49 +0200 Subject: [PATCH 212/455] drivers: eth_wch9120: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- drivers/ethernet/offload/eth_wch9120.c | 28 +++++--------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/drivers/ethernet/offload/eth_wch9120.c b/drivers/ethernet/offload/eth_wch9120.c index 4a6b51a867a1..7b2c0ed80249 100644 --- a/drivers/ethernet/offload/eth_wch9120.c +++ b/drivers/ethernet/offload/eth_wch9120.c @@ -310,8 +310,7 @@ static int ch9120_configure_interrupt(void) static void ch9120_uart_cb(const struct device *uart_dev, void *user_data) { int rx; - int ret; - uint32_t claimed_len = 0; + uint32_t space; uint32_t total_size = 0; uint8_t *buf; struct ch9120_runtime *data = (struct ch9120_runtime *)user_data; @@ -329,42 +328,25 @@ static void ch9120_uart_cb(const struct device *uart_dev, void *user_data) if (uart_irq_rx_ready(uart_dev) > 0) { while (true) { - - if (!claimed_len) { - if (total_size > 0) { - ret = ring_buf_put_finish(&sck->rx_buf, total_size); - __ASSERT_NO_MSG(ret == 0); - total_size = 0; - } - - claimed_len = ring_buf_put_claim(&sck->rx_buf, &buf, UINT32_MAX); - } - - if (!claimed_len) { + space = ring_buf_put_ptr(&sck->rx_buf, &buf, total_size); + if (!space) { LOG_ERR("Rx buffer doesn't have enough space"); ch9120_uart_flush_rx_fifo(uart_dev); break; } - rx = uart_fifo_read(uart_dev, buf, claimed_len); + rx = uart_fifo_read(uart_dev, buf, space); if (rx <= 0) { break; } - buf += rx; total_size += rx; - claimed_len -= rx; } } if (total_size > 0) { - ret = ring_buf_put_finish(&sck->rx_buf, total_size); - __ASSERT_NO_MSG(ret == 0); + ring_buf_commit(&sck->rx_buf, total_size); k_sem_give(&sck->rx_sem); - } else { - if (claimed_len > 0) { - ring_buf_put_finish(&sck->rx_buf, 0); - } } } From d313858bd6b1c954a73ed94cd4267100438dcfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 13:37:05 +0100 Subject: [PATCH 213/455] drivers: hdlc_rcp_if: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- drivers/hdlc_rcp_if/hdlc_rcp_if_uart.c | 36 ++++++++++++-------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/hdlc_rcp_if/hdlc_rcp_if_uart.c b/drivers/hdlc_rcp_if/hdlc_rcp_if_uart.c index 955d5e514062..2d4df7ec2f21 100644 --- a/drivers/hdlc_rcp_if/hdlc_rcp_if_uart.c +++ b/drivers/hdlc_rcp_if/hdlc_rcp_if_uart.c @@ -70,27 +70,25 @@ static void ot_uart_rx_cb(struct k_work *item) uint8_t *data; uint32_t len; - len = ring_buf_get_claim(otuart->rx_ringbuf, &data, - otuart->rx_ringbuf->size); + len = ring_buf_get_ptr(otuart->rx_ringbuf, &data, 0); if (len > 0) { otuart->cb(data, len, otuart->param); - ring_buf_get_finish(otuart->rx_ringbuf, len); + ring_buf_consume(otuart->rx_ringbuf, len); } } static void uart_tx_handle(const struct device *dev) { - uint32_t tx_len = 0, len; + int rc; + uint32_t tx_len; + uint32_t len; uint8_t *data; - len = ring_buf_get_claim( - ot_uart.tx_ringbuf, &data, - ot_uart.tx_ringbuf->size); + len = ring_buf_get_ptr(ot_uart.tx_ringbuf, &data, 0); if (len > 0) { - tx_len = uart_fifo_fill(dev, data, len); - int err = ring_buf_get_finish(ot_uart.tx_ringbuf, tx_len); - (void)err; - __ASSERT_NO_MSG(err == 0); + rc = uart_fifo_fill(dev, data, len); + tx_len = rc > 0 ? (uint32_t)rc : 0; + ring_buf_consume(ot_uart.tx_ringbuf, tx_len); } else { uart_irq_tx_disable(dev); } @@ -98,18 +96,16 @@ static void uart_tx_handle(const struct device *dev) static void uart_rx_handle(const struct device *dev) { - uint32_t rd_len = 0, len; + int rc; + uint32_t rd_len; + uint32_t len; uint8_t *data; - len = ring_buf_put_claim( - ot_uart.rx_ringbuf, &data, - ot_uart.rx_ringbuf->size); + len = ring_buf_put_ptr(ot_uart.rx_ringbuf, &data, 0); if (len > 0) { - rd_len = uart_fifo_read(dev, data, len); - - int err = ring_buf_put_finish(ot_uart.rx_ringbuf, rd_len); - (void)err; - __ASSERT_NO_MSG(err == 0); + rc = uart_fifo_read(dev, data, len); + rd_len = rc > 0 ? (uint32_t)rc : 0; + ring_buf_commit(ot_uart.rx_ringbuf, rd_len); } } From 5141f969933335173f638e6bb1befaa6ce06f396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 13:20:38 +0100 Subject: [PATCH 214/455] drivers: net: Update ring_buf usage to new zero-copy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- drivers/net/ppp.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index 62d9111c2baa..363f09d2702a 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -914,10 +914,8 @@ static int ppp_consume_ringbuf(struct ppp_driver_context *ppp) { uint8_t *data; size_t len, tmp; - int ret; - len = ring_buf_get_claim(&ppp->rx_ringbuf, &data, - CONFIG_NET_PPP_RINGBUF_SIZE); + len = ring_buf_get_ptr(&ppp->rx_ringbuf, &data, 0); if (len == 0) { LOG_DBG("Ringbuf %p is empty!", &ppp->rx_ringbuf); return 0; @@ -939,10 +937,7 @@ static int ppp_consume_ringbuf(struct ppp_driver_context *ppp) } } while (--tmp); - ret = ring_buf_get_finish(&ppp->rx_ringbuf, len); - if (ret < 0) { - LOG_DBG("Cannot flush ring buffer (%d)", ret); - } + ring_buf_consume(&ppp->rx_ringbuf, len); return -EAGAIN; } From 28eda8875e6f420943178ec3b38b703f2a5a152b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Ansgariusson?= Date: Wed, 25 Mar 2026 10:14:13 +0100 Subject: [PATCH 215/455] drivers: eswifi: Update ring_buf usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the usage of ring_buf to utilize the new put_ptr/commit/get_ptr/consume pattern instead of the traditional claim/finish approach. This change is part of a larger refactor aimed at streamlining the ring_buf API and improving its efficiency. Signed-off-by: Måns Ansgariusson --- drivers/wifi/eswifi/eswifi_bus_uart.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/wifi/eswifi/eswifi_bus_uart.c b/drivers/wifi/eswifi/eswifi_bus_uart.c index 19c26c4ea171..b6e3f9712394 100644 --- a/drivers/wifi/eswifi/eswifi_bus_uart.c +++ b/drivers/wifi/eswifi/eswifi_bus_uart.c @@ -60,10 +60,8 @@ static void eswifi_iface_uart_isr(const struct device *uart_dev, int rx = 0; uint8_t *dst; uint32_t partial_size = 0; - uint32_t total_size = 0; ARG_UNUSED(user_data); - uart_irq_update(uart->dev); if (uart_irq_rx_ready(uart->dev) <= 0) { @@ -71,10 +69,7 @@ static void eswifi_iface_uart_isr(const struct device *uart_dev, } while (true) { - if (!partial_size) { - partial_size = ring_buf_put_claim(&uart->rx_rb, &dst, - UINT32_MAX); - } + partial_size = ring_buf_put_ptr(&uart->rx_rb, &dst, 0); if (!partial_size) { LOG_ERR("Rx buffer doesn't have enough space"); eswifi_iface_uart_flush(uart); @@ -85,13 +80,8 @@ static void eswifi_iface_uart_isr(const struct device *uart_dev, if (rx <= 0) { break; } - - dst += rx; - total_size += rx; - partial_size -= rx; + ring_buf_commit(&uart->rx_rb, rx); } - - ring_buf_put_finish(&uart->rx_rb, total_size); } static char get_fsm_char(int fsm) @@ -114,7 +104,7 @@ static char get_fsm_char(int fsm) static int eswifi_uart_get_resp(struct eswifi_uart_data *uart) { - uint8_t c; + uint8_t c = 0; while (ring_buf_get(&uart->rx_rb, &c, 1) > 0) { LOG_DBG("FSM: %c, RX: 0x%02x : %c", From d105f71b1593fbaeaff24e482726339b4f41efb9 Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Thu, 20 Aug 2026 12:51:37 +0300 Subject: [PATCH 216/455] manifest: tf-m: update to v2.3.1 From v2.3.0. Signed-off-by: Tomi Fontanilles --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 60024bc99122..8d8edbf8995e 100644 --- a/west.yml +++ b/west.yml @@ -403,7 +403,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 5202b76233e148d8a60bc059156ed11a16e0d88c + revision: e0b7b16a58d8f54a5833bd91f7227cf08742c8f3 path: modules/tee/tf-m/trusted-firmware-m groups: - tee From 3696b2eef851a64dc11a1e76b65dcd3e9ff53a7e Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Thu, 20 Aug 2026 12:57:12 +0300 Subject: [PATCH 217/455] manifest: tf-m-tests: update to v2.3.1 From v2.3.0. Signed-off-by: Tomi Fontanilles --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 8d8edbf8995e..bef2e4a61181 100644 --- a/west.yml +++ b/west.yml @@ -387,7 +387,7 @@ manifest: groups: - debug - name: tf-m-tests - revision: 2f9d323198329cc3df0d6145ec924222cf4ae440 + revision: 420c95daa7a25143bb88f807a1e2c432dd4372e8 path: modules/tee/tf-m/tf-m-tests groups: - testing From 081d893141d246bc02cfaf88e3ef8937dd76d433 Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Thu, 20 Aug 2026 12:58:20 +0300 Subject: [PATCH 218/455] manifest: tf-psa-crypto: reapply updated TF-M patch In conjunction with the TF-M 2.3.1 update. Signed-off-by: Tomi Fontanilles --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index bef2e4a61181..0d9b7cc3df21 100644 --- a/west.yml +++ b/west.yml @@ -393,7 +393,7 @@ manifest: - testing - tee - name: tf-psa-crypto - revision: 000d24cb5e411a1d68f05b5a98ffd9a5cfdc17c2 + revision: 765af96a20a2b408474dcd950372ba9af4d26b62 path: modules/crypto/tf-psa-crypto groups: - crypto From 1497b85a1dcaabafe1566fe4ea90c522e7b5b687 Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Thu, 13 Aug 2026 14:14:58 +0300 Subject: [PATCH 219/455] doc: release-notes: document TF-M update From v2.3.0 to v2.3.1. Signed-off-by: Tomi Fontanilles --- doc/releases/release-notes-4.5.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index feca32739986..e70cbc690dfd 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -665,8 +665,11 @@ Libraries / Subsystems * TF-M - * TF-M was updated from version 2.2.2 to version 2.3.0. Release notes can be - found `here `_. + * TF-M was updated from version 2.2.2 to version 2.3.1. Release notes can be + found at: + + * https://trustedfirmware-m.readthedocs.io/en/latest/releases/2.3.0.html + * https://trustedfirmware-m.readthedocs.io/en/tf-mv2.3.1/releases/2.3.1.html * TF-M can now be compiled using LLVM by setting ``ZEPHYR_TOOLCHAIN_VARIANT`` to ``zephyr/llvm``. From 813355d830d777fcb77b1aa063a0525022f3b69b Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Sun, 16 Aug 2026 12:34:05 +0200 Subject: [PATCH 220/455] Bluetooth: BAP: UC: Fix group->has_been_connected The ep->unicast_group was never assigned and thus has_been_connected was never set for the BAP unicast groups. Change to use the stream->group instead. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/bap_unicast_client.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index ec88f924408b..81716b36a375 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -326,20 +326,22 @@ static void unicast_client_ep_iso_sent(struct bt_iso_chan *chan) static void unicast_client_ep_iso_connected(struct bt_bap_ep *ep) { const struct bt_bap_stream_ops *stream_ops; - struct bt_bap_stream *stream; + struct bt_bap_stream *stream = ep->stream; - if (ep->unicast_group != NULL) { - ep->unicast_group->has_been_connected = true; + if (stream == NULL) { + LOG_ERR("No stream for ep %p", ep); + return; } - if (ep->state != BT_BAP_EP_STATE_QOS_CONFIGURED && ep->state != BT_BAP_EP_STATE_ENABLING) { - LOG_DBG("endpoint in invalid state: %s", bt_bap_ep_state_str(ep->state)); + if (stream->group == NULL) { + LOG_ERR("No group for stream %p for ep %p", stream, ep); return; } - stream = ep->stream; - if (stream == NULL) { - LOG_ERR("No stream for ep %p", ep); + ((struct bt_bap_unicast_group *)stream->group)->has_been_connected = true; + + if (ep->state != BT_BAP_EP_STATE_QOS_CONFIGURED && ep->state != BT_BAP_EP_STATE_ENABLING) { + LOG_DBG("endpoint in invalid state: %s", bt_bap_ep_state_str(ep->state)); return; } From e7264055cbe0997cbd45f31b84415ac9014c8091 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Sun, 16 Aug 2026 12:35:04 +0200 Subject: [PATCH 221/455] Bluetooth: BAP: Remove bt_bap_ep group references Remove the unused references to the unicast group and broadcast source. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/bap_broadcast_source.c | 2 -- subsys/bluetooth/audio/bap_endpoint.h | 4 ---- 2 files changed, 6 deletions(-) diff --git a/subsys/bluetooth/audio/bap_broadcast_source.c b/subsys/bluetooth/audio/bap_broadcast_source.c index 3e27a694f14b..cca39c454929 100644 --- a/subsys/bluetooth/audio/bap_broadcast_source.c +++ b/subsys/bluetooth/audio/bap_broadcast_source.c @@ -387,7 +387,6 @@ broadcast_source_setup_stream(uint8_t index, struct bt_bap_stream *stream, bt_bap_stream_attach(NULL, stream, ep); stream->qos = &ep->qos; stream->group = source; - ep->broadcast_source = source; return 0; } @@ -538,7 +537,6 @@ static void broadcast_source_cleanup(struct bt_bap_broadcast_source *source) bt_bap_iso_unbind_ep(stream->ep->iso, stream->ep); stream->iso = NULL; stream->ep->stream = NULL; - stream->ep->broadcast_source = NULL; stream->ep = NULL; stream->codec_cfg = NULL; stream->qos = NULL; diff --git a/subsys/bluetooth/audio/bap_endpoint.h b/subsys/bluetooth/audio/bap_endpoint.h index 63f6e82fd9eb..873bc5aafc6f 100644 --- a/subsys/bluetooth/audio/bap_endpoint.h +++ b/subsys/bluetooth/audio/bap_endpoint.h @@ -58,10 +58,6 @@ struct bt_bap_ep { /* Used by the unicast server and client */ bool receiver_ready; - - /* TODO: Create a union to reduce memory usage */ - struct bt_bap_unicast_group *unicast_group; - struct bt_bap_broadcast_source *broadcast_source; }; struct bt_bap_unicast_group_cig_param { From 71dd77264c94e9c1ee7241157e387b8dba6f2af3 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Sat, 15 Aug 2026 09:18:36 +0200 Subject: [PATCH 222/455] Bluetooth: Audio: expand bt_bap_unicast_group_info Add the CIG parameters of the unicast group (SDU intervals, transport latencies, framing, packing and, when CONFIG_BT_ISO_TEST_PARAMS is enabled, the flush timeouts and the ISO interval) as well as the has_been_connected state to struct bt_bap_unicast_group_info, so that applications can retrieve all relevant information about a group. The framing is stored internally using the ISO values, and is converted back to the BAP QoS configuration values when reported. The existing bsim tests for bt_bap_unicast_group_get_info have been expanded to verify the new values, including for a group with asymmetric parameters and for a reconfigured group. Assisted-by: Copilot coding agent Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/bap.h | 73 +++++++++++++- subsys/bluetooth/audio/bap_unicast_client.c | 17 ++++ .../audio/src/bap_unicast_client_test.c | 97 +++++++++++++++---- .../audio/src/cap_initiator_unicast_test.c | 43 +++++++- 4 files changed, 206 insertions(+), 24 deletions(-) diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 0d2322db7677..7ed49e6d326f 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -1573,9 +1573,10 @@ int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_gro bt_bap_unicast_group_foreach_stream_func_t func, void *user_data); -/** Structure holding information of audio stream endpoint */ +/** Structure holding information of a unicast group */ struct bt_bap_unicast_group_info { - /** Presentation delay for sink ASEs + /** + * @brief Presentation delay for sink ASEs (central to peripheral audio direction) * * Will be @ref BT_BAP_PD_UNSET if no sink streams have been added to group. * The value does not reflect what has been configured on any remote ASEs, but only the @@ -1583,13 +1584,79 @@ struct bt_bap_unicast_group_info { */ uint32_t sink_pd; - /** Presentation delay for source ASEs + /** + * @brief Presentation delay for source ASEs (peripheral to central audio direction) * * Will be @ref BT_BAP_PD_UNSET if no source streams have been added to group. * The value does not reflect what has been configured on any remote ASEs, but only the * local value from when the group was created or reconfigured. */ uint32_t source_pd; + + /** + * @brief Central to Peripheral SDU interval in microseconds + * + * Will be 0 if no sink streams have been added to the group. + */ + uint32_t c_to_p_interval; + + /** + * @brief Peripheral to Central SDU interval in microseconds + * + * Will be 0 if no source streams have been added to the group. + */ + uint32_t p_to_c_interval; + + /** + * @brief Central to Peripheral maximum transport latency in milliseconds + * + * Will be 0 if no sink streams have been added to the group. + */ + uint16_t c_to_p_latency; + + /** + * @brief Peripheral to Central maximum transport latency in milliseconds + * + * Will be 0 if no source streams have been added to the group. + */ + uint16_t p_to_c_latency; + + /** @brief The framing of the streams in the group */ + enum bt_bap_qos_cfg_framing framing; + + /** + * @brief The packing of the group + * + * @ref BT_ISO_PACKING_SEQUENTIAL or @ref BT_ISO_PACKING_INTERLEAVED. + */ + uint8_t packing; + + /** + * @brief Whether any stream in the group has been connected + * + * If this is true, then the group can no longer be modified with e.g. + * bt_bap_unicast_group_reconfig() or bt_bap_unicast_group_add_streams(). + */ + bool has_been_connected; + +#if defined(CONFIG_BT_ISO_TEST_PARAMS) || defined(__DOXYGEN__) + /** + * @brief Central to Peripheral flush timeout in multiples of the ISO interval + * + * Will be 0 if no sink streams have been added to the group. + */ + uint8_t c_to_p_ft; + + /** + * @brief Peripheral to Central flush timeout in multiples of the ISO interval + * + * Will be 0 if no source streams have been added to the group. + */ + uint8_t p_to_c_ft; + + /** @brief ISO interval in 1.25 ms units */ + uint16_t iso_interval; +#endif /* CONFIG_BT_ISO_TEST_PARAMS */ }; /** diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 81716b36a375..5d8a207c2152 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -3292,6 +3292,23 @@ int bt_bap_unicast_group_get_info(const struct bt_bap_unicast_group *unicast_gro info->sink_pd = unicast_group->sink_pd; info->source_pd = unicast_group->source_pd; + info->c_to_p_interval = unicast_group->cig_param.c_to_p_interval; + info->p_to_c_interval = unicast_group->cig_param.p_to_c_interval; + info->c_to_p_latency = unicast_group->cig_param.c_to_p_latency; + info->p_to_c_latency = unicast_group->cig_param.p_to_c_latency; + /* The framing is stored as the ISO value, so it is converted back to the BAP value */ + if (unicast_group->cig_param.framing == BT_ISO_FRAMING_FRAMED) { + info->framing = BT_BAP_QOS_CFG_FRAMING_FRAMED; + } else { + info->framing = BT_BAP_QOS_CFG_FRAMING_UNFRAMED; + } + info->packing = unicast_group->cig_param.packing; + info->has_been_connected = unicast_group->has_been_connected; + IF_ENABLED(CONFIG_BT_ISO_TEST_PARAMS, ({ + info->c_to_p_ft = unicast_group->cig_param.c_to_p_ft; + info->p_to_c_ft = unicast_group->cig_param.p_to_c_ft; + info->iso_interval = unicast_group->cig_param.iso_interval; + })); return 0; } diff --git a/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c b/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c index 7338e1844ade..36308ddad968 100644 --- a/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c @@ -44,6 +44,8 @@ LOG_MODULE_REGISTER(bap_unicast_client_test); extern enum bst_result_t bst_result; +#define CIG_PACKING BT_ISO_PACKING_SEQUENTIAL + static struct audio_test_stream test_streams[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT]; static struct bt_bap_ep *g_sinks[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT]; static struct bt_bap_ep *g_sources[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT]; @@ -712,10 +714,74 @@ static void codec_configure_streams(size_t stream_cnt) } } +static void check_unicast_group_info(struct bt_bap_unicast_group *unicast_group, + const struct bt_bap_qos_cfg *rx_qos, + const struct bt_bap_qos_cfg *tx_qos, + bool expected_has_been_connected) +{ + struct bt_bap_unicast_group_info info; + int err; + + err = bt_bap_unicast_group_get_info(unicast_group, &info); + if (err != 0) { + FAIL("Unable to get unicast group info: %d\n", err); + return; + } + + if (info.sink_pd != tx_qos->pd) { + FAIL("Unexpected sink PD %u (expected %u)\n", info.sink_pd, tx_qos->pd); + return; + } + + if (info.source_pd != rx_qos->pd) { + FAIL("Unexpected source PD %u (expected %u)\n", info.source_pd, rx_qos->pd); + return; + } + + if (info.c_to_p_interval != tx_qos->interval) { + FAIL("Unexpected C to P interval %u (expected %u)\n", info.c_to_p_interval, + tx_qos->interval); + return; + } + + if (info.p_to_c_interval != rx_qos->interval) { + FAIL("Unexpected P to C interval %u (expected %u)\n", info.p_to_c_interval, + rx_qos->interval); + return; + } + + if (info.c_to_p_latency != tx_qos->latency) { + FAIL("Unexpected C to P latency %u (expected %u)\n", info.c_to_p_latency, + tx_qos->latency); + return; + } + + if (info.p_to_c_latency != rx_qos->latency) { + FAIL("Unexpected P to C latency %u (expected %u)\n", info.p_to_c_latency, + rx_qos->latency); + return; + } + + if (info.framing != tx_qos->framing) { + FAIL("Unexpected framing %u (expected %u)\n", info.framing, tx_qos->framing); + return; + } + + if (info.packing != CIG_PACKING) { + FAIL("Unexpected packing %u (expected %u)\n", info.packing, CIG_PACKING); + return; + } + + if (info.has_been_connected != expected_has_been_connected) { + FAIL("Unexpected has_been_connected %d (expected %d)\n", info.has_been_connected, + expected_has_been_connected); + return; + } +} + static void qos_configure_streams(struct bt_bap_unicast_group *unicast_group, size_t stream_cnt) { - struct bt_bap_unicast_group_info info; int err; UNSET_FLAG(flag_stream_qos_configured); @@ -734,22 +800,7 @@ static void qos_configure_streams(struct bt_bap_unicast_group *unicast_group, (void)k_sleep(K_MSEC(1U)); } - err = bt_bap_unicast_group_get_info(unicast_group, &info); - if (err != 0) { - FAIL("Unable to QoS configure streams: %d\n", err); - return; - } - - if (info.sink_pd != preset_16_2_1.qos.pd) { - FAIL("Unexpected sink PD %u (expected %u)\n", info.sink_pd, preset_16_2_1.qos.pd); - return; - } - - if (info.source_pd != preset_16_2_1.qos.pd) { - FAIL("Unexpected source PD %u (expected %u)\n", info.source_pd, - preset_16_2_1.qos.pd); - return; - } + check_unicast_group_info(unicast_group, &preset_16_2_1.qos, &preset_16_2_1.qos, false); } static int enable_stream(struct bt_bap_stream *stream) @@ -1092,7 +1143,7 @@ static size_t create_unicast_group(struct bt_bap_unicast_group **unicast_group) param.params = pair_params; param.params_count = pair_cnt; - param.packing = BT_ISO_PACKING_SEQUENTIAL; + param.packing = CIG_PACKING; /* Require controller support for CIGs */ err = bt_bap_unicast_group_create(¶m, unicast_group); @@ -1278,7 +1329,7 @@ static void test_main_async_group(void) struct bt_bap_unicast_group_param param = { .params = &pair_param, .params_count = 1U, - .packing = BT_ISO_PACKING_SEQUENTIAL, + .packing = CIG_PACKING, }; struct bt_bap_unicast_group *unicast_group; int err; @@ -1292,6 +1343,8 @@ static void test_main_async_group(void) return; } + check_unicast_group_info(unicast_group, &rx_qos, &tx_qos, false); + deinit(); PASS("Unicast client async group parameters passed\n"); @@ -1318,7 +1371,7 @@ static void test_main_reconf_group(void) struct bt_bap_unicast_group_param param = { .params = &pair_param, .params_count = 1U, - .packing = BT_ISO_PACKING_SEQUENTIAL, + .packing = CIG_PACKING, }; struct bt_bap_unicast_group *unicast_group; int err; @@ -1332,6 +1385,8 @@ static void test_main_reconf_group(void) return; } + check_unicast_group_info(unicast_group, &preset_16_2_1.qos, &preset_16_2_1.qos, false); + rx_param.qos = &preset_16_2_2.qos; tx_param.qos = &preset_16_2_2.qos; err = bt_bap_unicast_group_reconfig(unicast_group, ¶m); @@ -1341,6 +1396,8 @@ static void test_main_reconf_group(void) return; } + check_unicast_group_info(unicast_group, &preset_16_2_2.qos, &preset_16_2_2.qos, false); + deinit(); PASS("Unicast client async group parameters passed\n"); diff --git a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c index ad36856c6315..28087d74e440 100644 --- a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c @@ -641,7 +641,8 @@ static void unicast_group_create(struct bt_cap_unicast_group **out_unicast_group static bool unicast_group_foreach_stream_cb(struct bt_cap_stream *cap_stream, void *user_data) { - const uint32_t expected_pd = cap_stream->bap_stream.qos->pd; + const struct bt_bap_qos_cfg *qos = cap_stream->bap_stream.qos; + const uint32_t expected_pd = qos->pd; struct bt_cap_unicast_group *unicast_group = user_data; struct bt_bap_unicast_group_info bap_info; struct bt_cap_unicast_group_info cap_info; @@ -672,12 +673,52 @@ static bool unicast_group_foreach_stream_cb(struct bt_cap_stream *cap_stream, vo expected_pd); return false; } + + if (bap_info.c_to_p_interval != qos->interval) { + FAIL("Unexpected C to P interval %u (expected %u)\n", + bap_info.c_to_p_interval, qos->interval); + return false; + } + + if (bap_info.c_to_p_latency != qos->latency) { + FAIL("Unexpected C to P latency %u (expected %u)\n", + bap_info.c_to_p_latency, qos->latency); + return false; + } } else { if (bap_info.source_pd != expected_pd) { FAIL("Unexpected source PD %u (expected %u)\n", bap_info.source_pd, expected_pd); return false; } + + if (bap_info.p_to_c_interval != qos->interval) { + FAIL("Unexpected P to C interval %u (expected %u)\n", + bap_info.p_to_c_interval, qos->interval); + return false; + } + + if (bap_info.p_to_c_latency != qos->latency) { + FAIL("Unexpected P to C latency %u (expected %u)\n", + bap_info.p_to_c_latency, qos->latency); + return false; + } + } + + if (bap_info.framing != qos->framing) { + FAIL("Unexpected framing %u (expected %u)\n", bap_info.framing, qos->framing); + return false; + } + + if (bap_info.packing != BT_ISO_PACKING_SEQUENTIAL) { + FAIL("Unexpected packing %u (expected %u)\n", bap_info.packing, + BT_ISO_PACKING_SEQUENTIAL); + return false; + } + + if (!bap_info.has_been_connected) { + FAIL("Expected has_been_connected to be true after start\n"); + return false; } return true; From b694104662a22b530eb680190d4969254bce522d Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Sat, 15 Aug 2026 09:21:59 +0200 Subject: [PATCH 223/455] doc: releases: Add new fields in bt_bap_unicast_group_info bt_bap_unicast_group_info has new fields. Signed-off-by: Emil Gydesen --- doc/releases/release-notes-4.5.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index e70cbc690dfd..746946e55443 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -375,6 +375,16 @@ New APIs and options * :c:func:`bt_ascs_unregister` * :c:func:`bt_bap_unicast_client_qos_from_group` * :c:func:`bt_bap_qos_cfg_eq` + * :c:member:`bt_bap_unicast_group_info.c_to_p_interval` + * :c:member:`bt_bap_unicast_group_info.p_to_c_interval` + * :c:member:`bt_bap_unicast_group_info.c_to_p_latency` + * :c:member:`bt_bap_unicast_group_info.p_to_c_latency` + * :c:member:`bt_bap_unicast_group_info.framing` + * :c:member:`bt_bap_unicast_group_info.packing` + * :c:member:`bt_bap_unicast_group_info.has_been_connected` + * :c:member:`bt_bap_unicast_group_info.c_to_p_ft` + * :c:member:`bt_bap_unicast_group_info.p_to_c_ft` + * :c:member:`bt_bap_unicast_group_info.iso_interval` * Host From 3940f961872fccbc3eab9c5c0e9c0fe61975733f Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Sun, 16 Aug 2026 12:58:03 +0200 Subject: [PATCH 224/455] Bluetooth: BAP: UC: Add missing ISO_TEST_PARAM for reconfig bt_bap_unicast_group_reconfig did not properly set the fields related to CONFIG_BT_ISO_TEST_PARAMS nor packing. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/bap_unicast_client.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 5d8a207c2152..88c18687e1e5 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -3079,6 +3079,13 @@ int bt_bap_unicast_group_reconfig(struct bt_bap_unicast_group *unicast_group, struct bt_bap_unicast_group_stream_param *rx_param = stream_param->rx_param; struct bt_bap_unicast_group_stream_param *tx_param = stream_param->tx_param; + unicast_group->cig_param.packing = param->packing; + IF_ENABLED(CONFIG_BT_ISO_TEST_PARAMS, ({ + unicast_group->cig_param.c_to_p_ft = param->c_to_p_ft; + unicast_group->cig_param.p_to_c_ft = param->p_to_c_ft; + unicast_group->cig_param.iso_interval = param->iso_interval; + })); + if (rx_param != NULL) { struct bt_bap_iso *bap_iso = CONTAINER_OF(rx_param->stream->iso, struct bt_bap_iso, chan); From 9b9230207d7cd3a9ef81e8fa84e1629e725fc94c Mon Sep 17 00:00:00 2001 From: Cheng Chang Date: Mon, 17 Aug 2026 16:24:47 +0800 Subject: [PATCH 225/455] bluetooth: host: bip: fix role validation in client connect Move the responder role check outside the is_bip_primary_connect() conditional so it applies to all connection types. Remove the initiator role check from the secondary path since a responder role is valid for secondary connections initiated by the remote device. This ensures that a BIP instance configured as responder cannot initiate any client connections, regardless of connection type. Signed-off-by: Cheng Chang --- subsys/bluetooth/host/classic/bip.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/subsys/bluetooth/host/classic/bip.c b/subsys/bluetooth/host/classic/bip.c index 8e8277393859..6c14fbd3bd62 100644 --- a/subsys/bluetooth/host/classic/bip.c +++ b/subsys/bluetooth/host/classic/bip.c @@ -1345,11 +1345,12 @@ static int bt_bip_client_connect(struct bt_bip *bip, struct bt_bip_client *clien return -EINVAL; } + if (bip->role == BT_BIP_ROLE_RESPONDER) { + LOG_ERR("Invalid role responder"); + return -EINVAL; + } + if (is_bip_primary_connect(type)) { - if (bip->role == BT_BIP_ROLE_RESPONDER) { - LOG_ERR("Invalid role responder"); - return -EINVAL; - } if (primary_server != NULL) { LOG_ERR("primary server should be NULL"); @@ -1364,11 +1365,6 @@ static int bt_bip_client_connect(struct bt_bip *bip, struct bt_bip_client *clien } else { struct bt_bip *primary_bip; - if (bip->role == BT_BIP_ROLE_INITIATOR) { - LOG_ERR("Invalid role initiator"); - return -EINVAL; - } - if (primary_server == NULL || primary_server->_bip == NULL) { LOG_ERR("Invalid primary client"); return -EINVAL; From 0c345a0c1a7e3283a3a8b6d431d9544ea9d715db Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 17 Aug 2026 14:12:36 +0300 Subject: [PATCH 226/455] Bluetooth: tester: Use bt_le_ext_adv_get_info() for the BIS event address The BIS data path setup event read the advertising address directly from the host-internal bt_le_ext_adv structure. Use the public API instead. Assisted-by: Claude:claude-opus-5 Signed-off-by: Johan Hedberg --- tests/bluetooth/tester/src/btp_gap.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/bluetooth/tester/src/btp_gap.c b/tests/bluetooth/tester/src/btp_gap.c index 2993b0804a73..92c1612172e1 100644 --- a/tests/bluetooth/tester/src/btp_gap.c +++ b/tests/bluetooth/tester/src/btp_gap.c @@ -2762,6 +2762,7 @@ static void iso_broadcaster_connected(struct bt_iso_chan *chan) }; struct btp_gap_bis_data_path_setup_ev ev; struct bt_le_ext_adv *ext_adv = tester_gap_ext_adv_get(0); + struct bt_le_ext_adv_info info; err = bt_iso_chan_get_index(chan); if (err < 0) { @@ -2779,7 +2780,13 @@ static void iso_broadcaster_connected(struct bt_iso_chan *chan) return; } - bt_addr_le_copy(&ev.address, &ext_adv->random_addr); + err = bt_le_ext_adv_get_info(ext_adv, &info); + if (err != 0) { + LOG_ERR("Failed to get advertising set info: %d", err); + return; + } + + bt_addr_le_copy(&ev.address, info.addr); tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_BIS_DATA_PATH_SETUP, &ev, sizeof(ev)); } From a345efb0034ab6026db01bff5a57f16fc779ab25 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 17 Aug 2026 20:20:13 +0300 Subject: [PATCH 227/455] Bluetooth: Host: Report the real advertising address from get_info bt_le_ext_adv_get_info() handed out a pointer to the advertising set's random_addr, which is only written when a per-set random address is programmed. When the set advertises with a public identity address the reported address was never populated, and with a controller without the extended advertising feature it was never written at all. Instead of adding a second address field alongside random_addr, widen the meaning of the existing one: rename it to adv_addr, the address the set advertises with regardless of type. The per-set random address cases keep being updated in bt_id_set_adv_random_addr(), so the reported address stays correct across RPA and NRPA rotation, and le_update_private_addr() refreshes the legacy advertiser, which advertises with the device-wide random address. The cases that do not program a per-set address - a public identity address, and the device-wide random address on controllers without the extended advertising feature, e.g. an RPA set through bt_id_set_private_addr() when privacy is enabled - are saved by a new bt_id_save_adv_addr(), which the advertising parameter setting calls once the controller has accepted the parameters. Saving only at that point ensures a failed parameter update does not overwrite the reported address with one the controller never took into use. Widening the field is safe for the existing readers: the connection responder address and OOB paths that read it only execute in configurations where the set advertises with a random address, in which case the value is unchanged, and a pending random address is programmed from the field before bt_id_save_adv_addr() runs. For the BT_HCI_OWN_ADDR_RPA_OR_* types the controller substitutes a locally generated RPA whenever the peer is in the resolving list, which the host cannot observe; the configured fallback address is reported in that case, as documented on the info struct. Fixes: #112667 Assisted-by: Claude:claude-fable-5 Signed-off-by: Johan Hedberg --- include/zephyr/bluetooth/bluetooth.h | 21 +++++- subsys/bluetooth/host/adv.c | 12 ++-- subsys/bluetooth/host/hci_core.h | 9 ++- subsys/bluetooth/host/id.c | 64 +++++++++++++++++-- subsys/bluetooth/host/id.h | 10 +++ .../id/bt_id_set_adv_private_addr/src/main.c | 2 +- .../id/bt_id_set_adv_random_addr/src/main.c | 4 +- .../id/bt_le_ext_adv_oob_get_local/src/main.c | 2 +- .../src/test_suite_invalid_inputs.c | 2 +- 9 files changed, 109 insertions(+), 17 deletions(-) diff --git a/include/zephyr/bluetooth/bluetooth.h b/include/zephyr/bluetooth/bluetooth.h index 352e7b84d1fd..f51d42cf9de8 100644 --- a/include/zephyr/bluetooth/bluetooth.h +++ b/include/zephyr/bluetooth/bluetooth.h @@ -1525,7 +1525,26 @@ struct bt_le_ext_adv_info { /** Advertising Set ID */ uint8_t sid; - /** Current local advertising address used. */ + /** @brief Current local advertising address used. + * + * The address the set advertises with, whether that is an identity + * address, a static random address, an RPA or an NRPA. For a set + * advertising with an RPA this is the RPA itself, not the identity + * address it resolves to. The value is determined when the + * advertising parameters are set, so it is only meaningful once + * bt_le_ext_adv_create() or bt_le_ext_adv_update_param() has + * succeeded, and reads as @ref BT_ADDR_LE_ANY before that. + * + * The pointer is valid for as long as the advertising set object + * itself, i.e. until bt_le_ext_adv_delete(), and tracks the set + * across reconfiguration and private address rotation. Copy the + * address if it is needed beyond that. + * + * @note If the set was configured to let the controller resolve the + * address against its resolving list, the controller may substitute a + * locally generated RPA that the host cannot observe. The configured + * fallback address is reported in that case. + */ const bt_addr_le_t *addr; /** Extended advertising state. */ diff --git a/subsys/bluetooth/host/adv.c b/subsys/bluetooth/host/adv.c index 2a5a10d5ca09..5bb061857c20 100644 --- a/subsys/bluetooth/host/adv.c +++ b/subsys/bluetooth/host/adv.c @@ -989,6 +989,8 @@ static int adv_start_legacy(struct bt_le_ext_adv *adv, return err; } + bt_id_save_adv_addr(adv, set_param.own_addr_type); + if (!dir_adv) { err = le_adv_update(adv, ad, ad_len, sd, sd_len, false, scannable); if (err) { @@ -1188,12 +1190,14 @@ static int le_ext_adv_param_set(struct bt_le_ext_adv *adv, atomic_set_bit(adv->flags, BT_ADV_PARAMS_SET); if (atomic_test_and_clear_bit(adv->flags, BT_ADV_RANDOM_ADDR_PENDING)) { - err = bt_id_set_adv_random_addr(adv, &adv->random_addr.a); + err = bt_id_set_adv_random_addr(adv, &adv->adv_addr.a); if (err) { return err; } } + bt_id_save_adv_addr(adv, own_addr_type); + atomic_set_bit_to(adv->flags, BT_ADV_CONNECTABLE, param->options & BT_LE_ADV_OPT_CONN); atomic_set_bit_to(adv->flags, BT_ADV_SCANNABLE, scannable); @@ -1411,7 +1415,7 @@ int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv, info->id = adv->id; info->sid = adv->sid; info->tx_power = adv->tx_power; - info->addr = &adv->random_addr; + info->addr = &adv->adv_addr; if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) { info->ext_adv_state = BT_LE_EXT_ADV_STATE_ENABLED; @@ -2192,11 +2196,11 @@ void bt_hci_le_adv_set_terminated(struct net_buf *buf) conn->le.resp_addr.type = BT_ADDR_LE_RANDOM; if (bt_addr_eq(&conn->le.resp_addr.a, BT_ADDR_ANY)) { bt_addr_copy(&conn->le.resp_addr.a, - &adv->random_addr.a); + &adv->adv_addr.a); } } else if (adv->options & BT_LE_ADV_OPT_USE_NRPA) { bt_addr_le_copy(&conn->le.resp_addr, - &adv->random_addr); + &adv->adv_addr); } else { bt_addr_le_copy(&conn->le.resp_addr, &bt_dev.id_addr[conn->id]); diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 7d0ba8f74e9d..6aad977c9479 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -175,8 +175,13 @@ struct bt_le_ext_adv { const struct bt_le_ext_adv_cb *cb; #endif /* defined(CONFIG_BT_EXT_ADV) */ - /* Current local Random Address */ - bt_addr_le_t random_addr; + /* Address this set advertises with. Updated by + * bt_id_set_adv_random_addr() when a per-set random address is + * programmed, and by bt_id_save_adv_addr() for the address types + * that are not programmed per-set. bt_le_ext_adv_get_info() hands + * out a pointer to it. + */ + bt_addr_le_t adv_addr; /* Current target address */ bt_addr_le_t target_addr; diff --git a/subsys/bluetooth/host/id.c b/subsys/bluetooth/host/id.c index bb196fd0d573..e90916bb9b85 100644 --- a/subsys/bluetooth/host/id.c +++ b/subsys/bluetooth/host/id.c @@ -179,13 +179,20 @@ int bt_id_set_adv_random_addr(struct bt_le_ext_adv *adv, if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) && BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features))) { - return set_random_address(addr); + err = set_random_address(addr); + if (err != 0) { + return err; + } + + bt_id_save_adv_addr(adv, BT_HCI_OWN_ADDR_RANDOM); + + return 0; } LOG_DBG("%s", bt_addr_str(addr)); if (!atomic_test_bit(adv->flags, BT_ADV_PARAMS_SET)) { - bt_addr_le_copy_addr(&adv->random_addr, addr, BT_ADDR_LE_RANDOM); + bt_addr_le_copy_addr(&adv->adv_addr, addr, BT_ADDR_LE_RANDOM); atomic_set_bit(adv->flags, BT_ADV_RANDOM_ADDR_PENDING); return 0; } @@ -206,8 +213,8 @@ int bt_id_set_adv_random_addr(struct bt_le_ext_adv *adv, return err; } - if (&adv->random_addr.a != addr) { - bt_addr_le_copy_addr(&adv->random_addr, addr, BT_ADDR_LE_RANDOM); + if (&adv->adv_addr.a != addr) { + bt_addr_le_copy_addr(&adv->adv_addr, addr, BT_ADDR_LE_RANDOM); } return 0; @@ -648,6 +655,14 @@ static void le_update_private_addr(void) return; } + if (IS_ENABLED(CONFIG_BT_BROADCASTER) && adv != NULL && + !atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) { + /* The legacy advertiser advertises with the device-wide + * random address that was just refreshed. + */ + bt_id_save_adv_addr(adv, BT_HCI_OWN_ADDR_RANDOM); + } + if (IS_ENABLED(CONFIG_BT_BROADCASTER) && IS_ENABLED(CONFIG_BT_EXT_ADV) && BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features)) { @@ -2047,6 +2062,45 @@ int bt_id_set_adv_own_addr(struct bt_le_ext_adv *adv, uint32_t options, return 0; } +void bt_id_save_adv_addr(struct bt_le_ext_adv *adv, uint8_t own_addr_type) +{ + switch (own_addr_type) { + case BT_HCI_OWN_ADDR_PUBLIC: + case BT_HCI_OWN_ADDR_RPA_OR_PUBLIC: + /* The identity address is not programmed into the controller, + * so it has to be recorded here. + * + * For BT_HCI_OWN_ADDR_RPA_OR_PUBLIC the controller substitutes + * a locally generated RPA whenever the peer is in the resolving + * list, which the host cannot observe, so the configured + * fallback address is recorded instead. + */ + bt_addr_le_copy(&adv->adv_addr, &bt_dev.id_addr[adv->id]); + break; + case BT_HCI_OWN_ADDR_RANDOM: + case BT_HCI_OWN_ADDR_RPA_OR_RANDOM: + if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) && + BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features))) { + /* With extended advertising the per-set random address + * is always programmed through + * bt_id_set_adv_random_addr(), which saves it, so + * there is nothing to do here. Without it the set + * advertises with the device-wide random address, + * which is not always set through that function: with + * privacy it comes from bt_id_set_private_addr(), and + * when scanning with a static random identity it is + * already in place. + */ + bt_addr_le_copy_addr(&adv->adv_addr, + &bt_dev.random_addr, + BT_ADDR_LE_RANDOM); + } + break; + default: + break; + } +} + #if defined(CONFIG_BT_CLASSIC) int bt_br_oob_get_local(struct bt_br_oob *oob) { @@ -2181,7 +2235,7 @@ int bt_le_ext_adv_oob_get_local(struct bt_le_ext_adv *adv, le_force_rpa_timeout(); } - bt_addr_le_copy(&oob->addr, &adv->random_addr); + bt_addr_le_copy(&oob->addr, &adv->adv_addr); } else { bt_addr_le_copy(&oob->addr, &bt_dev.id_addr[adv->id]); } diff --git a/subsys/bluetooth/host/id.h b/subsys/bluetooth/host/id.h index bc4fabaa58f7..14236c865f05 100644 --- a/subsys/bluetooth/host/id.h +++ b/subsys/bluetooth/host/id.h @@ -45,6 +45,16 @@ int bt_id_set_scan_own_addr(bool active_scan, uint8_t *own_addr_type); int bt_id_set_adv_own_addr(struct bt_le_ext_adv *adv, uint32_t options, bool dir_adv, uint8_t *own_addr_type); +/* Set adv->adv_addr, the address the set advertises with, for the own + * address types whose address is not programmed per-set through + * bt_id_set_adv_random_addr(): identity addresses, and the device-wide + * random address on controllers without the extended advertising feature. + * In the advertising parameter setting path this must be called only after + * the parameters have been accepted by the controller, so that a failed + * parameter command does not change the reported address. + */ +void bt_id_save_adv_addr(struct bt_le_ext_adv *adv, uint8_t own_addr_type); + bool bt_id_adv_random_addr_check(const struct bt_le_adv_param *param); bool bt_id_scan_random_addr_check(void); diff --git a/tests/bluetooth/host/id/bt_id_set_adv_private_addr/src/main.c b/tests/bluetooth/host/id/bt_id_set_adv_private_addr/src/main.c index df8226343b45..130e4d2da9db 100644 --- a/tests/bluetooth/host/id/bt_id_set_adv_private_addr/src/main.c +++ b/tests/bluetooth/host/id/bt_id_set_adv_private_addr/src/main.c @@ -127,7 +127,7 @@ ZTEST(bt_id_set_adv_private_addr, test_set_adv_private_address_with_valid_ref_pr zassert_true(atomic_test_bit(adv_param.flags, BT_ADV_RANDOM_ADDR_PENDING), "Flags were not correctly set"); - zassert_mem_equal(&adv_param.random_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), + zassert_mem_equal(&adv_param.adv_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), "Incorrect address was set"); #endif diff --git a/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c b/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c index f8517f5cdf38..bb7a2b390a9c 100644 --- a/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c +++ b/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c @@ -91,7 +91,7 @@ ZTEST(bt_id_set_adv_random_addr, test_ext_adv_enabled) zassert_true(atomic_test_bit(adv_param.flags, BT_ADV_RANDOM_ADDR_PENDING), "Flags were not correctly set"); - zassert_mem_equal(&adv_param.random_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), + zassert_mem_equal(&adv_param.adv_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), "Incorrect address was set"); } @@ -133,6 +133,6 @@ ZTEST(bt_id_set_adv_random_addr, test_ext_adv_enabled_hci_set_adv_set_random_add zassert_equal(cp.handle, adv_param.handle, "Incorrect handle value was set"); zassert_mem_equal(&cp.bdaddr, BT_RPA_ADDR, sizeof(bt_addr_t), "Incorrect address was set"); - zassert_mem_equal(&adv_param.random_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), + zassert_mem_equal(&adv_param.adv_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), "Incorrect address was set"); } diff --git a/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/main.c b/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/main.c index a9a99698a52a..43bb7de07b4d 100644 --- a/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/main.c +++ b/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/main.c @@ -90,7 +90,7 @@ ZTEST(bt_le_ext_adv_oob_get_local, test_get_local_out_of_band_information_privac atomic_set_bit(bt_dev.flags, BT_DEV_READY); atomic_clear_bit(adv.flags, BT_ADV_USE_IDENTITY); - bt_addr_le_copy(&adv.random_addr, BT_RPA_LE_ADDR); + bt_addr_le_copy(&adv.adv_addr, BT_RPA_LE_ADDR); err = bt_le_ext_adv_oob_get_local(&adv, &oob); diff --git a/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/test_suite_invalid_inputs.c b/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/test_suite_invalid_inputs.c index e3df22493289..aa96fb44acae 100644 --- a/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/test_suite_invalid_inputs.c +++ b/tests/bluetooth/host/id/bt_le_ext_adv_oob_get_local/src/test_suite_invalid_inputs.c @@ -184,7 +184,7 @@ ZTEST(bt_le_ext_adv_oob_get_local_invalid_inputs, test_get_local_oob_information atomic_set_bit(bt_dev.flags, BT_DEV_READY); atomic_clear_bit(adv.flags, BT_ADV_USE_IDENTITY); - bt_addr_le_copy(&adv.random_addr, BT_RPA_LE_ADDR); + bt_addr_le_copy(&adv.adv_addr, BT_RPA_LE_ADDR); err = bt_le_ext_adv_oob_get_local(&adv, &oob); From e67a8763b60c49cfa3844d86d2f3b62e745a0ca3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 17 Aug 2026 20:20:14 +0300 Subject: [PATCH 228/455] Bluetooth: tests: Add unit coverage for the saved advertising address Add a unit test suite for bt_id_save_adv_addr(), covering the public identity address types, the device-wide random address fallback on a controller without the extended advertising feature - including an RPA set through bt_id_set_private_addr() when privacy is enabled - and that a per-set random address is left untouched. Also extend the legacy branch test of bt_id_set_adv_random_addr() to assert the saved address, not just the return value. Assisted-by: Claude:claude-opus-5 Signed-off-by: Johan Hedberg --- .../id/bt_id_save_adv_addr/CMakeLists.txt | 29 ++++ .../host/id/bt_id_save_adv_addr/prj.conf | 8 ++ .../host/id/bt_id_save_adv_addr/src/main.c | 135 ++++++++++++++++++ .../host/id/bt_id_save_adv_addr/tests.yaml | 11 ++ .../id/bt_id_set_adv_random_addr/src/main.c | 5 + 5 files changed, 188 insertions(+) create mode 100644 tests/bluetooth/host/id/bt_id_save_adv_addr/CMakeLists.txt create mode 100644 tests/bluetooth/host/id/bt_id_save_adv_addr/prj.conf create mode 100644 tests/bluetooth/host/id/bt_id_save_adv_addr/src/main.c create mode 100644 tests/bluetooth/host/id/bt_id_save_adv_addr/tests.yaml diff --git a/tests/bluetooth/host/id/bt_id_save_adv_addr/CMakeLists.txt b/tests/bluetooth/host/id/bt_id_save_adv_addr/CMakeLists.txt new file mode 100644 index 000000000000..1f18d2a98201 --- /dev/null +++ b/tests/bluetooth/host/id/bt_id_save_adv_addr/CMakeLists.txt @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) + +project(bt_id_save_adv_addr) + +find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +include_directories(BEFORE + ${ZEPHYR_BASE}/tests/bluetooth/host/id/mocks +) + +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host host_mocks) +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host/id/mocks mocks) + +target_link_libraries(testbinary PRIVATE mocks host_mocks) + +target_sources(testbinary + PRIVATE + src/main.c + + ${ZEPHYR_BASE}/subsys/bluetooth/host/id.c + ${ZEPHYR_BASE}/subsys/bluetooth/common/addr.c + ${ZEPHYR_BASE}/subsys/logging/log_minimal.c + ${ZEPHYR_BASE}/subsys/bluetooth/common/bt_str.c + ${ZEPHYR_BASE}/subsys/bluetooth/host/uuid.c + ${ZEPHYR_BASE}/lib/utils/hex.c + ${ZEPHYR_BASE}/lib/uuid/uuid.c +) diff --git a/tests/bluetooth/host/id/bt_id_save_adv_addr/prj.conf b/tests/bluetooth/host/id/bt_id_save_adv_addr/prj.conf new file mode 100644 index 000000000000..5f8cdcef0178 --- /dev/null +++ b/tests/bluetooth/host/id/bt_id_save_adv_addr/prj.conf @@ -0,0 +1,8 @@ +CONFIG_ZTEST=y +CONFIG_BT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_ID_MAX=2 +CONFIG_ASSERT=y +CONFIG_ASSERT_LEVEL=2 +CONFIG_ASSERT_VERBOSE=y +CONFIG_ASSERT_ON_ERRORS=y diff --git a/tests/bluetooth/host/id/bt_id_save_adv_addr/src/main.c b/tests/bluetooth/host/id/bt_id_save_adv_addr/src/main.c new file mode 100644 index 000000000000..16f58011ebb8 --- /dev/null +++ b/tests/bluetooth/host/id/bt_id_save_adv_addr/src/main.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2026 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mocks/hci_core.h" +#include "testing_common_defs.h" + +#include +#include +#include + +#include +#include + +DEFINE_FFF_GLOBALS; + +static void fff_reset_rule_before(const struct ztest_unit_test *test, void *fixture) +{ + (void)memset(&bt_dev, 0x00, sizeof(struct bt_dev)); + + HCI_CORE_FFF_FAKES_LIST(RESET_FAKE); +} + +ZTEST_RULE(fff_reset_rule, fff_reset_rule_before, NULL); + +ZTEST_SUITE(bt_id_save_adv_addr, NULL, NULL, NULL, NULL, NULL); + +/* + * Test recording the advertising address for a set advertising with a public + * identity address. + * + * Constraints: + * - Own address type is BT_HCI_OWN_ADDR_PUBLIC + * + * Expected behaviour: + * - Advertising address is loaded with the identity address of the set + */ +ZTEST(bt_id_save_adv_addr, test_public_identity_address) +{ + struct bt_le_ext_adv adv = {0}; + + bt_addr_le_copy(&bt_dev.id_addr[BT_ID_DEFAULT], BT_LE_ADDR); + + bt_id_save_adv_addr(&adv, BT_HCI_OWN_ADDR_PUBLIC); + + zassert_mem_equal(&adv.adv_addr, BT_LE_ADDR, sizeof(bt_addr_le_t), + "Incorrect address was set"); +} + +/* + * Test recording the advertising address for a set advertising with a public + * identity address as the fallback of controller-based address resolution. + * + * Constraints: + * - Own address type is BT_HCI_OWN_ADDR_RPA_OR_PUBLIC + * + * Expected behaviour: + * - Advertising address is loaded with the identity address of the set + */ +ZTEST(bt_id_save_adv_addr, test_rpa_or_public_identity_address) +{ + struct bt_le_ext_adv adv = {0}; + + bt_addr_le_copy(&bt_dev.id_addr[BT_ID_DEFAULT], BT_LE_ADDR); + + bt_id_save_adv_addr(&adv, BT_HCI_OWN_ADDR_RPA_OR_PUBLIC); + + zassert_mem_equal(&adv.adv_addr, BT_LE_ADDR, sizeof(bt_addr_le_t), + "Incorrect address was set"); +} + +/* + * Test recording the advertising address for a set advertising with a random + * address on a controller without the extended advertising feature. This + * covers a device-wide random address that was not set through + * bt_id_set_adv_random_addr(), e.g. an RPA programmed through + * bt_id_set_private_addr() when privacy is enabled. + * + * Constraints: + * - Own address type is BT_HCI_OWN_ADDR_RANDOM + * - The controller extended advertising feature bit isn't set + * + * Expected behaviour: + * - Advertising address is loaded with the device-wide random address + */ +ZTEST(bt_id_save_adv_addr, test_random_address_no_ext_adv) +{ + struct bt_le_ext_adv adv = {0}; + + Z_TEST_SKIP_IFDEF(CONFIG_BT_EXT_ADV); + + bt_addr_copy(&bt_dev.random_addr, BT_RPA_ADDR); + + bt_id_save_adv_addr(&adv, BT_HCI_OWN_ADDR_RANDOM); + + zassert_equal(adv.adv_addr.type, BT_ADDR_LE_RANDOM, "Incorrect address type was set"); + zassert_mem_equal(&adv.adv_addr.a, BT_RPA_ADDR, sizeof(bt_addr_t), + "Incorrect address was set"); +} + +/* + * Test recording the advertising address for a set advertising with a random + * address on a controller with the extended advertising feature. The per-set + * random address is recorded when it is programmed through + * bt_id_set_adv_random_addr(), so recording must leave it untouched. + * + * Constraints: + * - Own address type is BT_HCI_OWN_ADDR_RANDOM + * - The controller extended advertising feature bit is set + * + * Expected behaviour: + * - Advertising address is left as it was + */ +ZTEST(bt_id_save_adv_addr, test_random_address_ext_adv) +{ + struct bt_le_ext_adv adv = {0}; + + Z_TEST_SKIP_IFNDEF(CONFIG_BT_EXT_ADV); + + bt_addr_le_copy(&adv.adv_addr, BT_RPA_LE_ADDR); + + /* Set the extended advertising feature bit, the setter equivalent of + * what BT_LE_FEAT_TEST() reads. + */ + bt_dev.le.features[(BT_LE_FEAT_BIT_EXT_ADV) >> 3] |= BIT((BT_LE_FEAT_BIT_EXT_ADV) & 7); + + bt_addr_copy(&bt_dev.random_addr, BT_ADDR); + + bt_id_save_adv_addr(&adv, BT_HCI_OWN_ADDR_RANDOM); + + zassert_mem_equal(&adv.adv_addr, BT_RPA_LE_ADDR, sizeof(bt_addr_le_t), + "Address was unexpectedly overwritten"); +} diff --git a/tests/bluetooth/host/id/bt_id_save_adv_addr/tests.yaml b/tests/bluetooth/host/id/bt_id_save_adv_addr/tests.yaml new file mode 100644 index 000000000000..59f4eb9e8a47 --- /dev/null +++ b/tests/bluetooth/host/id/bt_id_save_adv_addr/tests.yaml @@ -0,0 +1,11 @@ +common: + tags: + - bluetooth + - host +tests: + bluetooth.host.bt_id_save_adv_addr.default: + type: unit + bluetooth.host.bt_id_save_adv_addr.bt_ext_adv_enabled: + type: unit + extra_configs: + - CONFIG_BT_EXT_ADV=y diff --git a/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c b/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c index bb7a2b390a9c..f19ad6986a41 100644 --- a/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c +++ b/tests/bluetooth/host/id/bt_id_set_adv_random_addr/src/main.c @@ -57,6 +57,11 @@ ZTEST(bt_id_set_adv_random_addr, test_no_ext_adv) expect_not_called_net_buf_simple_add(); zassert_ok(err, "Unexpected error code '%d' was returned", err); + + zassert_equal(adv_param.adv_addr.type, BT_ADDR_LE_RANDOM, + "Incorrect address type was set"); + zassert_mem_equal(&adv_param.adv_addr.a, BT_RPA_ADDR, sizeof(bt_addr_t), + "Incorrect address was set"); } /* From e7434cba1e54e4de0bd53154789d625e1ec312c7 Mon Sep 17 00:00:00 2001 From: Bill Waters Date: Mon, 17 Aug 2026 15:48:09 -0700 Subject: [PATCH 229/455] tests: drivers: comparator: enable hysteresis on pse84 lpcomp loopback The kit_pse84_eval low-power comparator loopback test drives the comparator in ULP mode against the local VREF with hysteresis disabled. On some boards the slow ULP comparator glitches as the GPIO loopback edge crosses VREF, latching a spurious falling-edge event and failing test_trigger_falling_edge_pending. Enable hysteresis to reject the near-threshold glitches. Assisted-by: AI (GitHub Copilot) Signed-off-by: Bill Waters --- .../gpio_loopback/boards/kit_pse84_eval_common.overlay | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/drivers/comparator/gpio_loopback/boards/kit_pse84_eval_common.overlay b/tests/drivers/comparator/gpio_loopback/boards/kit_pse84_eval_common.overlay index 342fa845e692..a7c18a613e13 100644 --- a/tests/drivers/comparator/gpio_loopback/boards/kit_pse84_eval_common.overlay +++ b/tests/drivers/comparator/gpio_loopback/boards/kit_pse84_eval_common.overlay @@ -28,6 +28,8 @@ status = "okay"; output-mode = "direct"; power = "ulp"; + /* Reject glitches when the slow ULP comparator crosses local_vref. */ + hysteresis; intr-type = "both"; input-p = "gpio"; input-n = "local_vref"; From a9f4198488df85e173d9c1862437fc375d5d41ae Mon Sep 17 00:00:00 2001 From: Arkadiusz Grzelka Date: Wed, 26 Aug 2026 11:32:17 +0200 Subject: [PATCH 230/455] drivers: i2c: microchip: sercom g1: Reject an unreachable bitrate i2c_baudrate_calc() clamps BAUD to its maximum when the requested bitrate cannot be reached and reports success. On this board with a 72 MHz SERCOM core clock, a node asking for 100 kHz got 137.1 kHz, measured with a logic analyzer. The same branch catches the unsigned subtraction wrapping, which it does for any reference clock below roughly 12 times the bitrate: a 1 MHz reference asked for 400 kHz produces 1.9 kHz. Both directions are now an error rather than a silent substitution. Clamping downwards is left alone - a slower bus is always within spec. The subtraction is guarded instead of being checked after the fact, so the two directions are told apart before BAUD is computed. Every bitrate the reference clock is too slow to reach now fails in one place, the clamped ones included, and a bitrate too slow for the register keeps failing as before. Both speed ranges run the same two tests with their own limits. Reachable bitrates keep the BAUD values they had; the clamps are no loss either, since each produced exactly the registers that the smallest valid BAUD of its range produces. The refusal reaches the caller as -EINVAL rather than -EIO: a configuration that cannot be satisfied is not a transfer failure, and a caller that retries on -EIO would otherwise retry forever. The other -EIO in the function is left alone - a reference clock that reads back as zero really is a failure to talk to the hardware. Signed-off-by: Arkadiusz Grzelka --- drivers/i2c/i2c_mchp_sercom_g1.c | 65 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/drivers/i2c/i2c_mchp_sercom_g1.c b/drivers/i2c/i2c_mchp_sercom_g1.c index 09a5b197c9fc..5e82655ae01a 100644 --- a/drivers/i2c/i2c_mchp_sercom_g1.c +++ b/drivers/i2c/i2c_mchp_sercom_g1.c @@ -240,7 +240,10 @@ static void i2c_write_addr(const struct device *dev, uint16_t addr, bool is_read /* Calculates baud rate register values for requested I2C bitrate */ static bool i2c_baudrate_calc(uint32_t bitrate, uint32_t sys_clock_rate, uint32_t *baud_val) { - uint32_t baud_value = 0U; + uint32_t baud_offset; + uint32_t baud_min; + uint32_t baud_max; + uint32_t baud_value; /* Reference clock frequency must be at least two times the baud rate */ if (sys_clock_rate < (2U * bitrate)) { @@ -251,55 +254,51 @@ static bool i2c_baudrate_calc(uint32_t bitrate, uint32_t sys_clock_rate, uint32_ if (bitrate > I2C_BITRATE_FAST_PLUS) { /* HS mode baud calculation: BAUD = (f_ref / f_scl) - 2 */ - baud_value = (sys_clock_rate / bitrate) - 2U; + baud_offset = 2U; } else { /* Standard, FM and FM+ baud calculation: * BAUD = (f_ref / f_scl) - ((f_ref * T_RISE_ns) / 1,000,000,000) - 10 */ - baud_value = (sys_clock_rate / bitrate) - - ((sys_clock_rate * I2C_TRISE_DEFAULT_NS) / 1000000000U) - 10U; + baud_offset = ((sys_clock_rate * I2C_TRISE_DEFAULT_NS) / 1000000000U) + 10U; } if (bitrate <= I2C_BITRATE_FAST) { /* For I2C clock speed up to 400 kHz, the value of BAUD<7:0> * determines both SCL_L and SCL_H with SCL_L = SCL_H */ - if (baud_value > (I2C_BAUD_MAX * 2U)) { - /* Set baud rate to the maximum possible value */ - baud_value = I2C_BAUD_MAX; - } else if (baud_value <= 1U) { - /* Baud value cannot be 0. Set baud rate to minimum possible */ - baud_value = 1U; - } else { - baud_value /= 2U; - } + baud_min = 2U; + baud_max = I2C_BAUD_MAX * 2U; + } else { + /* To maintain the ratio of SCL_L:SCL_H to 2:1, the max value of + * BAUD_LOW<15:8>:BAUD<7:0> can be 0xFF:0x7F. Hence BAUD_LOW + BAUD + * can not exceed 255+127 = 382 + */ + baud_min = 4U; + baud_max = I2C_BAUD_LOW_HIGH_MAX - 1U; + } + + /* Tested before the subtraction below, which would otherwise wrap */ + if ((sys_clock_rate / bitrate) < (baud_offset + baud_min)) { + LOG_ERR("Reference clock %u Hz is too slow for I2C bitrate %u Hz", sys_clock_rate, + bitrate); + return false; + } - *baud_val = baud_value; + baud_value = (sys_clock_rate / bitrate) - baud_offset; - return true; + if (baud_value > baud_max) { + LOG_ERR("Reference clock %u Hz is too fast for I2C bitrate %u Hz", sys_clock_rate, + bitrate); + return false; } - /* To maintain the ratio of SCL_L:SCL_H to 2:1, the max value of - * BAUD_LOW<15:8>:BAUD<7:0> can be 0xFF:0x7F. Hence BAUD_LOW + BAUD - * can not exceed 255+127 = 382 - */ - if (baud_value >= I2C_BAUD_LOW_HIGH_MAX) { - /* Set baud rate to the maximum possible value while - * maintaining SCL_L:SCL_H to 2:1 - */ - baud_value = (0xFFUL << 8U) | (0x7FU); - } else if (baud_value <= 3U) { - /* Baud value cannot be 0. Set baud rate to minimum possible - * value while maintaining SCL_L:SCL_H to 2:1 - */ - baud_value = (2UL << 8U) | 1U; + if (bitrate <= I2C_BITRATE_FAST) { + *baud_val = baud_value / 2U; } else { /* For Fm+ mode, I2C SCL_L:SCL_H to 2:1 */ - baud_value = ((((baud_value * 2U) / 3U) << 8U) | (baud_value / 3U)); + *baud_val = ((((baud_value * 2U) / 3U) << 8U) | (baud_value / 3U)); } - *baud_val = baud_value; - return true; } @@ -383,7 +382,7 @@ static int i2c_apply_speed(const struct device *dev, uint32_t config) if (!i2c_set_baudrate(dev, f_scl, f_ref)) { LOG_ERR("Failed to set baudrate"); - return -EIO; + return -EINVAL; } return 0; From 081a58a286fc003e240b5d6a95e3ac6450196fcb Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:09:49 +0100 Subject: [PATCH 231/455] boards: telink: tlsr9518adk80d: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- boards/telink/tlsr9518adk80d/tlsr9518adk80d.dts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/boards/telink/tlsr9518adk80d/tlsr9518adk80d.dts b/boards/telink/tlsr9518adk80d/tlsr9518adk80d.dts index 44d8fe509077..1dfb0ad25f2b 100644 --- a/boards/telink/tlsr9518adk80d/tlsr9518adk80d.dts +++ b/boards/telink/tlsr9518adk80d/tlsr9518adk80d.dts @@ -93,34 +93,42 @@ }; &flash { + #address-cells = <1>; + #size-cells = <1>; reg = <0x20000000 0x100000>; + ranges = <0x0 0x20000000 0x100000>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "mcuboot"; reg = <0x00000000 0x10000>; }; slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x10000 0x70000>; }; slot1_partition: partition@80000 { + compatible = "zephyr,mapped-partition"; label = "image-1"; reg = <0x80000 0x70000>; }; scratch_partition: partition@f0000 { + compatible = "zephyr,mapped-partition"; label = "image-scratch"; reg = <0xf0000 0x4000>; }; storage_partition: partition@f4000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0xf4000 0x0000b000>; /* region <0xff000 0x1000> is reserved for Telink B91 SDK's data */ From 035c7bcf7b5dddeead33000101bf092d43912bcc Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:30:55 +0100 Subject: [PATCH 232/455] boards: arm: v2m_musca_b1: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- boards/arm/v2m_musca_b1/v2m_musca_b1_musca_b1_ns.dts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/boards/arm/v2m_musca_b1/v2m_musca_b1_musca_b1_ns.dts b/boards/arm/v2m_musca_b1/v2m_musca_b1_musca_b1_ns.dts index c8555c54f6ae..73c4a21b5737 100644 --- a/boards/arm/v2m_musca_b1/v2m_musca_b1_musca_b1_ns.dts +++ b/boards/arm/v2m_musca_b1/v2m_musca_b1_musca_b1_ns.dts @@ -45,24 +45,27 @@ /* Embedded flash */ compatible = "soc-nv-flash"; reg = <0x0a000000 DT_SIZE_M(2)>; + ranges = <0x0 0x0a000000 DT_SIZE_M(2)>; erase-block-size = ; write-block-size = <4>; #address-cells = <1>; #size-cells = <1>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; /* Please see the memory layout in: * https://git.trustedfirmware.org/plugins/gitiles/TF-M/trusted-firmware-m.git/+/refs/heads/main/platform/ext/target/arm/musca_b1/partition/flash_layout.h */ slot0_partition: partition@20000 { + compatible = "zephyr,mapped-partition"; reg = <0x20000 DT_SIZE_K(384)>; }; slot0_ns_partition: partition@80000 { + compatible = "zephyr,mapped-partition"; reg = <0x80000 DT_SIZE_K(512)>; }; }; From 6f079b680948434ab69ae3b06452bc439af1dc32 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:31:54 +0100 Subject: [PATCH 233/455] samples: fs: fs_sample: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- .../fs/fs_sample/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay | 5 ++++- .../fs/fs_sample/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/samples/subsys/fs/fs_sample/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay b/samples/subsys/fs/fs_sample/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay index 6aac8a3b983d..5b036aa7196f 100644 --- a/samples/subsys/fs/fs_sample/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay +++ b/samples/subsys/fs/fs_sample/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay @@ -15,19 +15,22 @@ &cpuapp_rram { partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; reg = <0x00010000 DT_SIZE_K(300)>; }; slot1_partition: partition@5b000 { + compatible = "zephyr,mapped-partition"; reg = <0x0005b000 DT_SIZE_K(300)>; }; storage_partition: partition@a6000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x000a6000 DT_SIZE_K(128)>; }; diff --git a/samples/subsys/fs/fs_sample/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay b/samples/subsys/fs/fs_sample/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay index 6aac8a3b983d..5b036aa7196f 100644 --- a/samples/subsys/fs/fs_sample/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay +++ b/samples/subsys/fs/fs_sample/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay @@ -15,19 +15,22 @@ &cpuapp_rram { partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; reg = <0x00010000 DT_SIZE_K(300)>; }; slot1_partition: partition@5b000 { + compatible = "zephyr,mapped-partition"; reg = <0x0005b000 DT_SIZE_K(300)>; }; storage_partition: partition@a6000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x000a6000 DT_SIZE_K(128)>; }; From 933b533b5312f9ea88b6ac51951a8bf298279f26 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:32:08 +0100 Subject: [PATCH 234/455] tests: ipc: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- .../remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi | 2 +- .../remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/subsys/ipc/ipc_service_api/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi b/tests/subsys/ipc/ipc_service_api/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi index 4863fcc45aa3..10e1cb031fe2 100644 --- a/tests/subsys/ipc/ipc_service_api/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi +++ b/tests/subsys/ipc/ipc_service_api/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi @@ -14,12 +14,12 @@ ranges = <0x0 0x15d000 DT_SIZE_K(96)>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; ranges; cpuflpr_code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x0 DT_SIZE_K(96)>; }; diff --git a/tests/subsys/ipc/ipc_service_benchmark/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi b/tests/subsys/ipc/ipc_service_benchmark/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi index 4863fcc45aa3..10e1cb031fe2 100644 --- a/tests/subsys/ipc/ipc_service_benchmark/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi +++ b/tests/subsys/ipc/ipc_service_benchmark/remote/boards/nrf54l15dk_nrf54l15_cpuflpr_common.dtsi @@ -14,12 +14,12 @@ ranges = <0x0 0x15d000 DT_SIZE_K(96)>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; ranges; cpuflpr_code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x0 DT_SIZE_K(96)>; }; From 0944784fbd22bc5cb26dd0dfb86254d7a41aa139 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:32:20 +0100 Subject: [PATCH 235/455] tests: dfu: mcuboot_multi: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- tests/subsys/dfu/mcuboot_multi/native_sim.overlay | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/subsys/dfu/mcuboot_multi/native_sim.overlay b/tests/subsys/dfu/mcuboot_multi/native_sim.overlay index 791ea9e7354f..d0ce3b630952 100644 --- a/tests/subsys/dfu/mcuboot_multi/native_sim.overlay +++ b/tests/subsys/dfu/mcuboot_multi/native_sim.overlay @@ -9,31 +9,36 @@ &flash0 { partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "mcuboot"; reg = <0x00000000 0x0000c000>; }; slot0_partition: partition@c000 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x0000c000 0x00069000>; }; slot1_partition: partition@75000 { + compatible = "zephyr,mapped-partition"; label = "image-1"; reg = <0x00075000 0x00069000>; }; slot2_partition: partition@de000 { + compatible = "zephyr,mapped-partition"; label = "image-2"; reg = <0x000de000 0x00069000>; }; slot3_partition: partition@146000 { + compatible = "zephyr,mapped-partition"; label = "image-3"; reg = <0x00146000 0x00069000>; }; From 14b7c9f77b8fecc6bdb88899584f660059bcbbf9 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:32:30 +0100 Subject: [PATCH 236/455] tests: kvss: nvs: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- .../kvss/nvs/boards/native_sim_64kb_erase_block.overlay | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/subsys/kvss/nvs/boards/native_sim_64kb_erase_block.overlay b/tests/subsys/kvss/nvs/boards/native_sim_64kb_erase_block.overlay index a847f65104ef..03859701f1f8 100644 --- a/tests/subsys/kvss/nvs/boards/native_sim_64kb_erase_block.overlay +++ b/tests/subsys/kvss/nvs/boards/native_sim_64kb_erase_block.overlay @@ -14,31 +14,36 @@ /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "mcuboot"; reg = <0x00000000 0x00010000>; }; slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x00010000 0x00070000>; }; slot1_partition: partition@80000 { + compatible = "zephyr,mapped-partition"; label = "image-1"; reg = <0x00080000 0x00070000>; }; scratch_partition: partition@f0000 { + compatible = "zephyr,mapped-partition"; label = "image-scratch"; reg = <0x000f0000 0x00020000>; }; storage_partition: partition@110000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x00110000 0x00050000>; }; From 4b11588c6c1423d0f8d51d4b09031d9f809bba7c Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:32:39 +0100 Subject: [PATCH 237/455] tests: shell: shell_remote: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- .../remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/subsys/shell/shell_remote/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay b/tests/subsys/shell/shell_remote/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay index 65afab471527..3b034572f1bf 100644 --- a/tests/subsys/shell/shell_remote/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay +++ b/tests/subsys/shell/shell_remote/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay @@ -45,12 +45,12 @@ ranges = <0x0 0x15d000 DT_SIZE_K(96)>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; ranges; cpuflpr_code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x0 DT_SIZE_K(96)>; }; From f9ce01fe9364bee0ec16eb7edf9db8e4a156fda4 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:32:51 +0100 Subject: [PATCH 238/455] samples: shell: shell_module: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- .../remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/subsys/shell/shell_module/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay b/samples/subsys/shell/shell_module/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay index b594f33a2a8e..9d61b9eb3d59 100644 --- a/samples/subsys/shell/shell_module/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay +++ b/samples/subsys/shell/shell_module/remote/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay @@ -50,12 +50,12 @@ ranges = <0x0 0x15d000 DT_SIZE_K(96)>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; ranges; cpuflpr_code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x0 DT_SIZE_K(96)>; }; From 9de09aa3cfa9348ec917b6dfd03d2db0e2e40c1a Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Mon, 24 Aug 2026 10:33:03 +0100 Subject: [PATCH 239/455] tests: cmake: hwm: board_extend: Move to mapped partition binding Moves to the mapped partition binding as fixed partitions for bootable slots is being deprecated Signed-off-by: Jamie McCrae --- .../native_sim_extend/native_sim_native_one.dts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/cmake/hwm/board_extend/oot_root/boards/native/native_sim_extend/native_sim_native_one.dts b/tests/cmake/hwm/board_extend/oot_root/boards/native/native_sim_extend/native_sim_native_one.dts index 270d1bed384e..7442ebcad637 100644 --- a/tests/cmake/hwm/board_extend/oot_root/boards/native/native_sim_extend/native_sim_native_one.dts +++ b/tests/cmake/hwm/board_extend/oot_root/boards/native/native_sim_extend/native_sim_native_one.dts @@ -57,6 +57,7 @@ flashcontroller0: flash-controller@0 { compatible = "zephyr,sim-flash"; reg = <0x00000000 DT_SIZE_K(2048)>; + ranges; #address-cells = <1>; #size-cells = <1>; @@ -67,34 +68,42 @@ compatible = "soc-nv-flash"; erase-block-size = <4096>; write-block-size = <1>; + #address-cells = <1>; + #size-cells = <1>; reg = <0x00000000 DT_SIZE_K(2048)>; + ranges = <0x0 0x00000000 DT_SIZE_K(2048)>; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + ranges; boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "mcuboot"; reg = <0x00000000 0x0000c000>; }; slot0_partition: partition@c000 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x0000c000 0x00069000>; }; slot1_partition: partition@75000 { + compatible = "zephyr,mapped-partition"; label = "image-1"; reg = <0x00075000 0x00069000>; }; scratch_partition: partition@de000 { + compatible = "zephyr,mapped-partition"; label = "image-scratch"; reg = <0x000de000 0x0001e000>; }; storage_partition: partition@fc000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x000fc000 0x00004000>; }; From 38cca013688507df3a785747e3dd4f1344b0e966 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Mon, 24 Aug 2026 13:31:24 +0200 Subject: [PATCH 240/455] scripts: cmake: style: Load mixed-case commands from allow-list files Replace the hardcoded MIXED_CASE_COMMANDS set with an allow-list file shipped next to the script, and add a repeatable --mixed-case-file argument that appends extra files to it. This gives downstream projects a way to extend the lowercase-rule exceptions with their own mixed-case commands. Assisted-by: Claude Code:claude-fable-5 Signed-off-by: Pieter De Gendt --- scripts/cmake/cmake_style.py | 118 +++++++++++++++-------- scripts/cmake/cmake_style_mixed_case.txt | 30 ++++++ scripts/cmake/cmake_style_test.py | 57 ++++++++++- 3 files changed, 163 insertions(+), 42 deletions(-) create mode 100644 scripts/cmake/cmake_style_mixed_case.txt diff --git a/scripts/cmake/cmake_style.py b/scripts/cmake/cmake_style.py index a1f80100bff5..37a4b95e18fa 100755 --- a/scripts/cmake/cmake_style.py +++ b/scripts/cmake/cmake_style.py @@ -10,7 +10,11 @@ doc/contribute/style/cmake.rst: 1. Indentation: use 2 spaces per block level, never tabs. - 2. Commands: always use lowercase command names. + 2. Commands: always use lowercase command names. Commands whose canonical + names are mixed-case by convention (CMake module commands and their + Zephyr/sysbuild extensions) are listed in cmake_style_mixed_case.txt next + to this script; for those the canonical spelling is the only accepted + form. Downstream projects append their own names with --mixed-case-file. 3. No space between a command and its opening parenthesis ('if(' not 'if ('). 4. Cache/option variables (option(...) and set(... CACHE ...)) use UPPERCASE names. 5. Boolean values are not quoted, in the positions where a boolean is expected: @@ -41,6 +45,7 @@ """ import argparse +import re import sys import traceback from dataclasses import dataclass @@ -54,34 +59,12 @@ # CMake boolean constants that should not be quoted (rule 5). CMAKE_BOOLEANS = {"ON", "OFF", "TRUE", "FALSE"} -# Commands that are an exception to the lowercase rule (rule 2): CMake module -# commands, and Zephyr/sysbuild commands that extend a mixed-case command, use a -# mixed-case 'Module_Action' convention. For these the canonical mixed-case -# spelling is the only accepted form, so both an all-lowercase and any other -# casing are flagged and corrected to the literal below. Built-in CMake commands -# are not listed here, so an uppercase 'FILE(' or 'SET(' is still caught by the -# lowercase rule. -MIXED_CASE_COMMANDS = { - # ExternalProject module (https://cmake.org/cmake/help/latest/module/ExternalProject.html). - "ExternalProject_Add", - "ExternalProject_Add_Step", - "ExternalProject_Add_StepTargets", - "ExternalProject_Add_StepDependencies", - "ExternalProject_Get_Property", - # FetchContent module (https://cmake.org/cmake/help/latest/module/FetchContent.html). - "FetchContent_Declare", - "FetchContent_MakeAvailable", - "FetchContent_Populate", - "FetchContent_GetProperties", - "FetchContent_SetPopulated", - # Zephyr sysbuild extensions, modeled after ExternalProject_Add. - "ExternalZephyrProject_Add", - "ExternalZephyrVariantProject_Add", - "ExternalZephyrProject_Cmake", -} - -# Lookup from the lowercased command name to its canonical mixed-case spelling. -_MIXED_CASE_BY_LOWER = {name.lower(): name for name in MIXED_CASE_COMMANDS} +# Default allow-list of mixed-case commands (rule 2), shipped next to this +# script. Extra files given with --mixed-case-file append to it. +DEFAULT_MIXED_CASE_FILE = Path(__file__).parent / "cmake_style_mixed_case.txt" + +# CMake command names: an identifier, as matched by CMake's own grammar. +_COMMAND_NAME_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") EXIT_OK = 0 EXIT_ISSUES = 1 @@ -100,6 +83,39 @@ class Issue: message: str +def load_mixed_case(paths: list[Path]) -> dict[str, str]: + """ + Load the mixed-case command allow-list files at 'paths', returning a lookup + from the lowercased command name to its canonical mixed-case spelling. + + Each file holds one canonical name per line; blank lines and '#' comments + (whole-line or trailing) are ignored. Later files append to earlier ones. + Raises ValueError for an entry that is not a valid command name, is + all-lowercase (which would allow-list nothing), or spells a name already + loaded with a different case; raises OSError for an unreadable file. + """ + mixed_case: dict[str, str] = {} + for path in paths: + for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1): + name = line.split("#", 1)[0].strip() + if not name: + continue + if not _COMMAND_NAME_RE.fullmatch(name): + raise ValueError(f"{path}:{lineno}: '{name}' is not a valid command name") + if name == name.lower(): + raise ValueError( + f"{path}:{lineno}: '{name}' is all-lowercase; only mixed-case " + "command names belong in the allow-list" + ) + canonical = mixed_case.setdefault(name.lower(), name) + if canonical != name: + raise ValueError( + f"{path}:{lineno}: '{name}' conflicts with the earlier " + f"canonical spelling '{canonical}'" + ) + return mixed_case + + def _text(node: Node) -> str: """Decoded source text of a node ('' if the node carries no text).""" return node.text.decode("utf-8") if node.text is not None else "" @@ -117,7 +133,9 @@ def _tab_indent_issues(lines: list[str]) -> list[Issue]: return issues -def _check_command(node: Node, depth: int, lines: list[str], issues: list[Issue]) -> None: +def _check_command( + node: Node, depth: int, lines: list[str], mixed_case: dict[str, str], issues: list[Issue] +) -> None: name_node = node.named_children[0] if node.named_children else None if name_node is None: return @@ -125,10 +143,10 @@ def _check_command(node: Node, depth: int, lines: list[str], issues: list[Issue] name_line = name_node.start_point.row + 1 name_col = name_node.start_point.column - # Rule 2: lowercase command, except module/sysbuild commands whose canonical + # Rule 2: lowercase command, except allow-listed commands whose canonical # names are mixed-case by convention. For those the canonical spelling is the # only accepted form; everything else must be lowercase. - canonical = _MIXED_CASE_BY_LOWER.get(name.lower()) + canonical = mixed_case.get(name.lower()) if canonical is not None: if name != canonical: issues.append(Issue(name_line, name_col + 1, "command-case", f"use '{canonical}'")) @@ -247,14 +265,14 @@ def _check_quoted_bool(node: Node, command: str, issues: list[Issue]) -> None: ) -def _tree_issues(root: Node, lines: list[str]) -> list[Issue]: +def _tree_issues(root: Node, lines: list[str], mixed_case: dict[str, str]) -> list[Issue]: """Rules 1 (depth), 2, 3, 4 and 5, derived from the tree-sitter-cmake tree.""" issues: list[Issue] = [] def walk(node: Node, depth: int) -> None: node_type = node.type if node_type.endswith("_command"): - _check_command(node, depth, lines, issues) + _check_command(node, depth, lines, mixed_case, issues) child_depth = depth + 1 if node_type == "body" else depth for child in node.children: walk(child, child_depth) @@ -263,8 +281,12 @@ def walk(node: Node, depth: int) -> None: return issues -def check_text(text: str) -> list[Issue]: - """Return the list of style issues for the given CMake file contents.""" +def check_text(text: str, mixed_case: dict[str, str]) -> list[Issue]: + """ + Return the list of style issues for the given CMake file contents. + 'mixed_case' maps lowercased command names to their canonical mixed-case + spelling (see load_mixed_case()). + """ lines = text.split("\n") if lines and lines[-1] == "": lines = lines[:-1] @@ -273,14 +295,14 @@ def check_text(text: str) -> list[Issue]: # token columns). lines = [line[:-1] if line.endswith("\r") else line for line in lines] root = _PARSER.parse(text.encode("utf-8")).root_node - issues = _tab_indent_issues(lines) + _tree_issues(root, lines) + issues = _tab_indent_issues(lines) + _tree_issues(root, lines, mixed_case) issues.sort(key=lambda issue: (issue.line, issue.col)) return issues -def check_file(path: Path) -> list[Issue]: +def check_file(path: Path, mixed_case: dict[str, str]) -> list[Issue]: """Return the list of style issues for a single file.""" - return check_text(path.read_text(encoding="utf-8")) + return check_text(path.read_text(encoding="utf-8"), mixed_case) def _is_cmake_file(name: str) -> bool: @@ -316,17 +338,33 @@ def parse_args() -> argparse.Namespace: help="CMake files to check. Directories are searched recursively for " "CMakeLists.txt and *.cmake files.", ) + parser.add_argument( + "--mixed-case-file", + metavar="FILE", + type=Path, + action="append", + default=[], + help="extra mixed-case command allow-list file (one canonical name per " + f"line, '#' comments), appended to '{DEFAULT_MIXED_CASE_FILE.name}'. " + "May be given multiple times.", + ) return parser.parse_args() def main() -> int: args = parse_args() + try: + mixed_case = load_mixed_case([DEFAULT_MIXED_CASE_FILE, *args.mixed_case_file]) + except (OSError, ValueError) as err: + print(f"error: {err}", file=sys.stderr) + return EXIT_ERROR + total = 0 failed = False for path in expand_paths(args.paths): try: - issues = check_file(path) + issues = check_file(path, mixed_case) except Exception: # Never let a checker failure masquerade as "no issues" (exit 0) or # "issues found" (exit 1): report it and exit with EXIT_ERROR. diff --git a/scripts/cmake/cmake_style_mixed_case.txt b/scripts/cmake/cmake_style_mixed_case.txt new file mode 100644 index 000000000000..81a44365a6a8 --- /dev/null +++ b/scripts/cmake/cmake_style_mixed_case.txt @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Commands that are an exception to cmake_style.py's lowercase rule: CMake +# module commands, and Zephyr/sysbuild commands that extend a mixed-case +# command, use a mixed-case 'Module_Action' convention. For these the canonical +# mixed-case spelling below is the only accepted form, so both an all-lowercase +# and any other casing are flagged. +# +# One canonical name per line. Downstream projects append their own names by +# passing extra files like this one with --mixed-case-file. + +# ExternalProject module (https://cmake.org/cmake/help/latest/module/ExternalProject.html). +ExternalProject_Add +ExternalProject_Add_Step +ExternalProject_Add_StepTargets +ExternalProject_Add_StepDependencies +ExternalProject_Get_Property + +# FetchContent module (https://cmake.org/cmake/help/latest/module/FetchContent.html). +FetchContent_Declare +FetchContent_MakeAvailable +FetchContent_Populate +FetchContent_GetProperties +FetchContent_SetPopulated + +# Zephyr sysbuild extensions, modeled after ExternalProject_Add. +ExternalZephyrProject_Add +ExternalZephyrVariantProject_Add +ExternalZephyrProject_Cmake diff --git a/scripts/cmake/cmake_style_test.py b/scripts/cmake/cmake_style_test.py index 5f35b98bc3a7..f750c01d8574 100644 --- a/scripts/cmake/cmake_style_test.py +++ b/scripts/cmake/cmake_style_test.py @@ -10,10 +10,14 @@ # cmake_style needs tree-sitter; skip the whole module if it is not installed. cmake_style = pytest.importorskip("cmake_style") +# A fixture allow-list, so the rule tests don't depend on the contents of the +# shipped cmake_style_mixed_case.txt. +MIXED_CASE = {"externalproject_add": "ExternalProject_Add"} -def _rules(text): + +def _rules(text, mixed_case=MIXED_CASE): """The set of rule ids reported for 'text'.""" - return {issue.rule for issue in cmake_style.check_text(text)} + return {issue.rule for issue in cmake_style.check_text(text, mixed_case)} # Rule 1: indentation (2 spaces per block level, no tabs). @@ -69,6 +73,11 @@ def test_command_case_module_non_canonical_flagged(): assert _rules("externalproject_add(foo)\n") == {"command-case"} +def test_command_case_unlisted_mixed_case_flagged(): + # A mixed-case command not in the allow-list is held to the lowercase rule. + assert _rules("UpdateableImage_Get(foo)\n") == {"command-case"} + + # Rule 3: no space before the opening parenthesis. def test_paren_space_flagged(): assert _rules("""\ @@ -139,3 +148,47 @@ def test_clean_file_has_no_issues(): add_subdirectory(tests) endif() """) + + +# The mixed-case allow-list loader. +def test_load_mixed_case_appends_across_files(tmp_path): + extra = tmp_path / "extra.txt" + extra.write_text("""\ +# downstream extension +UpdateableImage_Get # trailing comment + +""") + mixed_case = cmake_style.load_mixed_case([cmake_style.DEFAULT_MIXED_CASE_FILE, extra]) + assert mixed_case["externalproject_add"] == "ExternalProject_Add" + assert mixed_case["updateableimage_get"] == "UpdateableImage_Get" + + +def test_load_mixed_case_rejects_lowercase_entry(tmp_path): + f = tmp_path / "bad.txt" + f.write_text("all_lowercase\n") + with pytest.raises(ValueError, match="all-lowercase"): + cmake_style.load_mixed_case([f]) + + +def test_load_mixed_case_rejects_invalid_name(tmp_path): + f = tmp_path / "bad.txt" + f.write_text("Not a command\n") + with pytest.raises(ValueError, match="not a valid command name"): + cmake_style.load_mixed_case([f]) + + +def test_load_mixed_case_rejects_conflicting_case(tmp_path): + f = tmp_path / "bad.txt" + f.write_text("Foo_Bar\nFOO_Bar\n") + with pytest.raises(ValueError, match="conflicts"): + cmake_style.load_mixed_case([f]) + + +def test_load_mixed_case_missing_file_raises(tmp_path): + with pytest.raises(OSError): + cmake_style.load_mixed_case([tmp_path / "nonexistent.txt"]) + + +def test_default_mixed_case_file_loads(): + # Guards the format of the shipped allow-list. + assert cmake_style.load_mixed_case([cmake_style.DEFAULT_MIXED_CASE_FILE]) From 05e17a4c06db2b3876e312549d4745770fd55867 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Mon, 24 Aug 2026 13:41:57 +0200 Subject: [PATCH 241/455] scripts: ci: check_compliance: Allow extending the CMake style allow-list Let downstream projects point the CMAKE_STYLE_MIXED_CASE_FILE environment variable at an extra mixed-case command allow-list file, forwarded to cmake_style.py as --mixed-case-file, mirroring the UNDEF_KCONFIG_OUTSIDE_ALLOWLIST_FILE mechanism. Assisted-by: Claude Code:claude-fable-5 Signed-off-by: Pieter De Gendt --- scripts/ci/check_compliance.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index a04ff9a23e1f..62c88f878e1d 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -510,10 +510,11 @@ def _changed_lines(self, file): changed.update((hunk.target_start, hunk.target_start + 1)) return changed - def _check_files(self, tool, file_filter): + def _check_files(self, tool, file_filter, extra_args=()): # Run 'tool' on each added/modified file matching 'file_filter' and # report issues on changed lines only. 'tool' is a Path; file_filter is a - # predicate on the file path string. + # predicate on the file path string; 'extra_args' are passed to the tool + # before the file argument. for file in get_files(filter="d"): if not file_filter(file): continue @@ -523,7 +524,7 @@ def _check_files(self, tool, file_filter): continue result = subprocess.run( - [sys.executable, str(tool), file], + [sys.executable, str(tool), *extra_args, file], cwd=GIT_TOP, capture_output=True, text=True, @@ -2096,6 +2097,10 @@ class CMakeStyle(StyleCheckMixin, ComplianceTest): Checks the CMake style of added/modified files against the Zephyr CMake style guidelines, using scripts/cmake/cmake_style.py. Only issues on lines touched by the change are reported, so pre-existing style is not flagged. + + Downstream projects can extend the mixed-case command allow-list by pointing + the CMAKE_STYLE_MIXED_CASE_FILE environment variable at an extra allow-list + file (same format as scripts/cmake/cmake_style_mixed_case.txt). """ name = "CMakeStyle" @@ -2110,9 +2115,16 @@ def run(self): "'pip install tree-sitter tree-sitter-cmake'" ) + # Load extensions to the mixed-case command allow-list + extra_args = [] + if path := os.environ.get("CMAKE_STYLE_MIXED_CASE_FILE", None): + logging.info(f"Loading extra mixed-case commands from {path}") + extra_args += ["--mixed-case-file", path] + self._check_files( ZEPHYR_BASE / "scripts" / "cmake" / "cmake_style.py", lambda file: file.endswith(".cmake") or Path(file).name == "CMakeLists.txt", + extra_args=extra_args, ) From 0d5e2d2bea98b6b538b252946e1ea2d54935fc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 19 Aug 2026 12:46:57 +0200 Subject: [PATCH 242/455] drivers: ethernet: nxp: s32: align devicetree with other snps,dwmac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit align devicetree with other snps,dwmac controllers. Signed-off-by: Fin Maaß --- drivers/ethernet/mdio/mdio_nxp_s32_gmac.c | 4 +--- dts/arm/nxp/s32/nxp_s32k344_m7.dtsi | 15 +++++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/ethernet/mdio/mdio_nxp_s32_gmac.c b/drivers/ethernet/mdio/mdio_nxp_s32_gmac.c index 3c41d9b00a54..34662ff42a1a 100644 --- a/drivers/ethernet/mdio/mdio_nxp_s32_gmac.c +++ b/drivers/ethernet/mdio/mdio_nxp_s32_gmac.c @@ -17,8 +17,6 @@ LOG_MODULE_REGISTER(nxp_s32_mdio, CONFIG_MDIO_LOG_LEVEL); #include -#define GMAC_MDIO_REG_OFFSET (0x200) - #define GMAC_STATUS_TO_ERRNO(x) \ ((x) == GMAC_STATUS_SUCCESS ? 0 : ((x) == GMAC_STATUS_TIMEOUT ? -ETIMEDOUT : -EIO)) @@ -149,7 +147,7 @@ static DEVICE_API(mdio, mdio_nxp_s32_driver_api) = { }; #define MDIO_NXP_S32_HW_INSTANCE_CHECK(i, n) \ - (((DT_INST_REG_ADDR(n) - GMAC_MDIO_REG_OFFSET) == IP_GMAC_##i##_BASE) ? i : 0) + ((DT_REG_ADDR(DT_INST_PARENT(n)) == IP_GMAC_##i##_BASE) ? i : 0) #define MDIO_NXP_S32_HW_INSTANCE(n) \ LISTIFY(__DEBRACKET FEATURE_GMAC_NUM_INSTANCES, \ diff --git a/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi b/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi index fc4e8b5d129b..5ed57c555279 100644 --- a/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi +++ b/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi @@ -656,15 +656,14 @@ snps,multicast-filter-bins = <64>; snps,perfect-filter-entries = <3>; status = "disabled"; - }; - mdio0: mdio@40480200 { - reg = <0x40480200 0x8>; - compatible = "snps,dwmac-mdio"; - clocks = <&clock NXP_S32_AIPS_PLAT_CLK>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + mdio0: mdio { + compatible = "snps,dwmac-mdio"; + clocks = <&clock NXP_S32_AIPS_PLAT_CLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; edma0: dma-controller@4020c000 { From a052dbde3dbcda5d844e39a7ab95e5657fb9f90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 19 Aug 2026 12:54:27 +0200 Subject: [PATCH 243/455] drivers: ethernet: nxp: s32: add ptp clock node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the reference manual the s32 also has the ptp clock, so add the node for it. As the devicetree just describes the hardware, independant of software and driver support, we can add it without having a working driver for it. Signed-off-by: Fin Maaß --- dts/arm/nxp/s32/nxp_s32k344_m7.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi b/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi index 5ed57c555279..eb39e69f4a58 100644 --- a/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi +++ b/dts/arm/nxp/s32/nxp_s32k344_m7.dtsi @@ -664,6 +664,11 @@ #size-cells = <0>; status = "disabled"; }; + + ptp_clock: ptp-clock { + compatible = "snps,dwmac-ptp-clock"; + status = "disabled"; + }; }; edma0: dma-controller@4020c000 { From eca1733db958ad6fbe69077cf9d6034e39a4c449 Mon Sep 17 00:00:00 2001 From: Jose Alberto Meza Date: Mon, 16 Mar 2026 15:07:25 -0700 Subject: [PATCH 244/455] boards: microchip: mec175x: Make MEC175x device revision B Select device revision B as default. Signed-off-by: Jose Alberto Meza --- boards/microchip/mec_assy6941/support/mec175x_spi_cfg.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/microchip/mec_assy6941/support/mec175x_spi_cfg.txt b/boards/microchip/mec_assy6941/support/mec175x_spi_cfg.txt index b7428492fc6b..a9cb3db1e12f 100644 --- a/boards/microchip/mec_assy6941/support/mec175x_spi_cfg.txt +++ b/boards/microchip/mec_assy6941/support/mec175x_spi_cfg.txt @@ -3,7 +3,7 @@ SPISizeMegabits = 128 [DEVICE] -DeviceSel = A +DeviceSel = B TagAddr0 = 0 TagAddr1 = 0 ; BoardID is used by a Boot-ROM feature named PlatformID. By default PlatformID From 3bb0a55fcb3073fdde994f232782fbbea0a31b04 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Thu, 20 Aug 2026 12:18:34 +0200 Subject: [PATCH 245/455] Bluetooth: BAP: UC: Unify callback order for sink and source The BAP stream callbacks does not mirror the ASCS states 1:1 so there are a few cases where the callbacks are called without matching the ASCS state. BAP stream ops are intended to be the same for sink and source ASEs, even though they have different state machine, but sink streams did stopped() disabled() qos_configured() when exiting the streaming state (without a release), and source streams did disabled() stopped() qos_configured() Refactor unicast_client_ep_notify_app a bit to have the same callbacks in the same order for sinks and sources: disabled() stopped() qos_configured() Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/bap_unicast_client.c | 46 ++++++++++--------- .../bsim/bluetooth/audio/src/bap_stream_tx.h | 3 +- .../audio/src/cap_initiator_unicast_test.c | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 88c18687e1e5..366da1286eec 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -1221,19 +1221,6 @@ static void unicast_client_ep_notify_app(struct bt_bap_stream *stream, bool stat return; } - /* Call the `stopped` callback if we leave the BT_BAP_EP_STATE_STREAMING state for any - * reason, except if the new state is BT_BAP_EP_STATE_IDLE as that indicates a disconnect - * that is handled by unicast_client_ep_set_status - */ - if (state_changed && new_state != BT_BAP_EP_STATE_IDLE && - old_state == BT_BAP_EP_STATE_STREAMING) { - if (ops->stopped != NULL) { - ops->stopped(stream, reason); - } else { - LOG_WRN("No callback for stopped set"); - } - } - switch (new_state) { case BT_BAP_EP_STATE_IDLE: if (ops->released != NULL) { @@ -1253,17 +1240,21 @@ static void unicast_client_ep_notify_app(struct bt_bap_stream *stream, bool stat break; case BT_BAP_EP_STATE_QOS_CONFIGURED: if (dir == BT_AUDIO_DIR_SINK) { - if (ops->disabled != NULL) { - /* If the old state was enabling or streaming, then the sink - * ASE has been disabled. Since the sink ASE does not have a - * disabling state, we can check if by comparing the old_state - */ - const bool disabled = old_state == BT_BAP_EP_STATE_ENABLING || - old_state == BT_BAP_EP_STATE_STREAMING; + /* If the old state was enabling or streaming, then the sink + * ASE has been disabled. Since the sink ASE does not have a + * disabling state, we can check if by comparing the old_state + */ + const bool disabled = old_state == BT_BAP_EP_STATE_ENABLING || + old_state == BT_BAP_EP_STATE_STREAMING; - if (disabled) { + if (disabled) { + if (ops->disabled != NULL) { ops->disabled(stream); } + + if (ops->stopped != NULL) { + ops->stopped(stream, reason); + } } } else if (dir == BT_AUDIO_DIR_SOURCE) { if (old_state == BT_BAP_EP_STATE_DISABLING && ops->stopped != NULL) { @@ -1323,7 +1314,18 @@ static void unicast_client_ep_notify_app(struct bt_bap_stream *stream, bool stat } break; case BT_BAP_EP_STATE_RELEASING: - /* no callback for releasing state */ + /* Call the `disable` `stopped` callback if we leave the BT_BAP_EP_STATE_STREAMING + * state for any reason + */ + if (state_changed && old_state == BT_BAP_EP_STATE_STREAMING) { + if (ops->disabled != NULL) { + ops->disabled(stream); + } + + if (ops->stopped != NULL) { + ops->stopped(stream, reason); + } + } break; default: LOG_WRN("Unexpected new_state: %d", new_state); diff --git a/tests/bsim/bluetooth/audio/src/bap_stream_tx.h b/tests/bsim/bluetooth/audio/src/bap_stream_tx.h index 41bced6732a1..64a1fe8ffbb2 100644 --- a/tests/bsim/bluetooth/audio/src/bap_stream_tx.h +++ b/tests/bsim/bluetooth/audio/src/bap_stream_tx.h @@ -46,8 +46,7 @@ int bap_stream_tx_register(struct bt_bap_stream *bap_stream); * * @retval 0 on success * @retval -EINVAL @p bap_stream is NULL - * @retval -EINVAL @p bap_stream is not configured for TX - * @retval -EALREADY @p bap_stream is currently not registered + * @retval -ENODATA @p bap_stream is currently not registered */ int bap_stream_tx_unregister(struct bt_bap_stream *bap_stream); diff --git a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c index 28087d74e440..14279f5a2541 100644 --- a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c @@ -190,7 +190,7 @@ static void unicast_stream_stopped(struct bt_bap_stream *stream, uint8_t reason) int err; err = bap_stream_tx_unregister(stream); - if (err != 0) { + if (err != 0 && err != -ENODATA) { FAIL("Failed to unregister stream %p for TX: %d\n", stream, err); return; } From e42ce27317854f070c62663485fcc8e2a88e8d33 Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Thu, 18 Dec 2025 13:10:23 +0300 Subject: [PATCH 246/455] drivers: i3c: max32: Defer IBI rules setup until ENEC succeeds Previously, `max32_i3c_ibi_enable()` updated IBI data and programmed the IBI rules before enabling target interrupts by sending CCC ENEC to the target. If ENEC failed, the driver ended up with IBI rules configured for a target that did not enable interrupts. Reorder the operations so that IBI rules are setup only if ENEC succeeds. Signed-off-by: Tahsin Mutlugun --- drivers/i3c/i3c_max32.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/i3c/i3c_max32.c b/drivers/i3c/i3c_max32.c index bd4f7cf42995..27c91d9f2d1d 100644 --- a/drivers/i3c/i3c_max32.c +++ b/drivers/i3c/i3c_max32.c @@ -1425,9 +1425,6 @@ int max32_i3c_ibi_enable(const struct device *dev, struct i3c_device_desc *targe } } - /* Disable controller interrupt while we configure IBI rules. */ - MXC_I3C_Controller_DisableInt(regs, MXC_F_I3C_CONT_INTCLR_TARG_START); - LOG_DBG("IBI enabling for 0x%02x (BCR 0x%02x)", target->dynamic_addr, target->bcr); msb = (target->dynamic_addr & BIT(6)) == BIT(6); @@ -1481,18 +1478,22 @@ int max32_i3c_ibi_enable(const struct device *dev, struct i3c_device_desc *targe idx = 0; } - data->ibi.addr[idx] = target->dynamic_addr; - data->ibi.num_addr += 1U; - - max32_i3c_ibi_rules_setup(data, regs); + /* Disable controller interrupt while we configure IBI rules. */ + MXC_I3C_Controller_DisableInt(regs, MXC_F_I3C_CONT_INTCLR_TARG_START); /* Tell target to enable IBI */ i3c_events.events = I3C_CCC_EVT_INTR; ret = i3c_ccc_do_events_set(target, true, &i3c_events); if (ret != 0) { LOG_ERR("Error sending IBI ENEC for 0x%02x (%d)", target->dynamic_addr, ret); + goto out; } + data->ibi.addr[idx] = target->dynamic_addr; + data->ibi.num_addr += 1U; + + max32_i3c_ibi_rules_setup(data, regs); + out: if (data->ibi.num_addr > 0U) { /* @@ -1534,18 +1535,20 @@ int max32_i3c_ibi_disable(const struct device *dev, struct i3c_device_desc *targ /* Disable controller interrupt while we configure IBI rules. */ MXC_I3C_Controller_DisableInt(regs, MXC_F_I3C_CONT_INTCLR_TARG_START); - data->ibi.addr[idx] = 0U; - data->ibi.num_addr -= 1U; - /* Tell target to disable IBI */ i3c_events.events = I3C_CCC_EVT_INTR; ret = i3c_ccc_do_events_set(target, false, &i3c_events); if (ret != 0) { LOG_ERR("Error sending IBI DISEC for 0x%02x (%d)", target->dynamic_addr, ret); + goto out; } + data->ibi.addr[idx] = 0U; + data->ibi.num_addr -= 1U; + max32_i3c_ibi_rules_setup(data, regs); +out: if (data->ibi.num_addr > 0U) { /* * Enable controller to raise interrupt when a target @@ -1553,7 +1556,6 @@ int max32_i3c_ibi_disable(const struct device *dev, struct i3c_device_desc *targ */ MXC_I3C_Controller_EnableInt(regs, MXC_F_I3C_CONT_INTCLR_TARG_START); } -out: return ret; } From 3b99a1be28bf4cd53a534e820ff99f25db4f4a4e Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Mon, 9 Feb 2026 13:01:01 +0300 Subject: [PATCH 247/455] drivers: i3c: max32: Add device power management support Implements power management interface for MAX32 I3C driver. To restore the peripheral after resuming from a suspend-to-ram state, use the controller-mode enable bit to detect whether the I3C controller has lost its state. Signed-off-by: Tahsin Mutlugun --- drivers/i3c/i3c_max32.c | 110 ++++++++++++++++++++++++- dts/arm/adi/max32/max32657_common.dtsi | 1 + 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/i3c_max32.c b/drivers/i3c/i3c_max32.c index 27c91d9f2d1d..6f19d8625c94 100644 --- a/drivers/i3c/i3c_max32.c +++ b/drivers/i3c/i3c_max32.c @@ -20,6 +20,9 @@ #include #include +#include +#include + #include #include @@ -648,6 +651,8 @@ static int max32_i3c_recover_bus(const struct device *dev) int ret = 0; uint8_t ibi_type; + pm_policy_device_power_lock_get(dev); + /* Return to IDLE if in SDR message mode */ if (max32_i3c_state_get(regs) == MXC_V_I3C_CONT_STATUS_STATE_SDR_NORM) { max32_i3c_request_emit_stop(dev->data, regs); @@ -686,6 +691,8 @@ static int max32_i3c_recover_bus(const struct device *dev) ret = -EBUSY; } + pm_policy_device_power_lock_put(dev); + return ret; } @@ -880,6 +887,8 @@ static int max32_i3c_transfer(const struct device *dev, struct i3c_device_desc * k_mutex_lock(&data->lock, K_FOREVER); + pm_policy_device_power_lock_get(dev); + max32_i3c_wait_idle(data, regs); max32_i3c_xfer_reset(regs); @@ -950,6 +959,9 @@ static int max32_i3c_transfer(const struct device *dev, struct i3c_device_desc * max32_i3c_request_emit_stop(data, regs); max32_i3c_errwarn_clear_all_nowait(regs); max32_i3c_status_clear_all(regs); + + pm_policy_device_power_lock_put(dev); + k_mutex_unlock(&data->lock); return ret; @@ -977,6 +989,8 @@ static int max32_i3c_do_daa(const struct device *dev) k_mutex_lock(&data->lock, K_FOREVER); + pm_policy_device_power_lock_get(dev); + ret = max32_i3c_state_wait_timeout(regs, MXC_V_I3C_CONT_STATUS_STATE_IDLE, 100, 100000); if (ret == -ETIMEDOUT) { goto out_daa_unlock; @@ -1110,6 +1124,8 @@ static int max32_i3c_do_daa(const struct device *dev) max32_i3c_interrupt_enable(regs, intmask); out_daa_unlock: + pm_policy_device_power_lock_put(dev); + k_mutex_unlock(&data->lock); return ret; @@ -1138,6 +1154,8 @@ static int max32_i3c_do_ccc(const struct device *dev, struct i3c_ccc_payload *pa k_mutex_lock(&data->lock, K_FOREVER); + pm_policy_device_power_lock_get(dev); + max32_i3c_xfer_reset(regs); LOG_DBG("CCC[0x%02x]", payload->ccc.id); @@ -1220,6 +1238,8 @@ static int max32_i3c_do_ccc(const struct device *dev, struct i3c_ccc_payload *pa ret = 0; } + pm_policy_device_power_lock_put(dev); + k_mutex_unlock(&data->lock); return ret; @@ -1494,6 +1514,8 @@ int max32_i3c_ibi_enable(const struct device *dev, struct i3c_device_desc *targe max32_i3c_ibi_rules_setup(data, regs); + pm_policy_device_power_lock_get(dev); + out: if (data->ibi.num_addr > 0U) { /* @@ -1548,6 +1570,8 @@ int max32_i3c_ibi_disable(const struct device *dev, struct i3c_device_desc *targ max32_i3c_ibi_rules_setup(data, regs); + pm_policy_device_power_lock_put(dev); + out: if (data->ibi.num_addr > 0U) { /* @@ -1685,6 +1709,82 @@ static int max32_i3c_config_get(const struct device *dev, enum i3c_config_type t return 0; } +static int max32_i3c_pm_resume(const struct device *dev) +{ + const struct max32_i3c_config *cfg = dev->config; +#ifdef CONFIG_PM_S2RAM + const struct max32_i3c_data *data = dev->data; + struct i3c_config_controller ctrl_config; +#endif /* CONFIG_PM_S2RAM */ + int ret; + + ret = clock_control_on(cfg->clock, (clock_control_subsys_t)&cfg->perclk); + if (ret) { + return ret; + } + + ret = pinctrl_apply_state(cfg->pctrl, PINCTRL_STATE_DEFAULT); + if ((ret < 0) && (ret != -ENOENT)) { + return ret; + } + +#ifdef CONFIG_PM_S2RAM + if (!(cfg->regs->cont_ctrl0 & MXC_S_I3C_CONT_CTRL0_EN_ON)) { + /* Source and destination should not overlap so copy configuration first */ + memcpy(&ctrl_config, &data->common.ctrl_config, sizeof(ctrl_config)); + ret = max32_i3c_configure(dev, I3C_CONFIG_CONTROLLER, &ctrl_config); + if (ret) { + return ret; + } + } + +#endif /* CONFIG_PM_S2RAM */ + + return 0; +} + +static int max32_i3c_pm_suspend(const struct device *dev) +{ + const struct max32_i3c_config *cfg = dev->config; + int ret; + + ret = pinctrl_apply_state(cfg->pctrl, PINCTRL_STATE_SLEEP); + if ((ret < 0) && (ret != -ENOENT)) { + return ret; + } + + ret = clock_control_off(cfg->clock, (clock_control_subsys_t)&cfg->perclk); + if (ret) { + return ret; + } + + return 0; +} + +static int max32_i3c_pm_action(const struct device *dev, enum pm_device_action action) +{ + int ret = 0; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + ret = max32_i3c_pm_resume(dev); + if (ret) { + return ret; + } + break; + case PM_DEVICE_ACTION_SUSPEND: + ret = max32_i3c_pm_suspend(dev); + if (ret) { + return ret; + } + break; + default: + return -ENOTSUP; + } + + return 0; +} + /** * @brief Initialize the hardware. * @@ -1748,7 +1848,7 @@ static int max32_i3c_init(const struct device *dev) } } - return 0; + return pm_device_driver_init(dev, max32_i3c_pm_action); } static int max32_i3c_i2c_api_configure(const struct device *dev, uint32_t dev_config) @@ -1769,6 +1869,8 @@ static int max32_i3c_i2c_api_transfer(const struct device *dev, struct i2c_msg * k_mutex_lock(&data->lock, K_FOREVER); + pm_policy_device_power_lock_get(dev); + max32_i3c_wait_idle(data, regs); max32_i3c_xfer_reset(regs); @@ -1839,6 +1941,9 @@ static int max32_i3c_i2c_api_transfer(const struct device *dev, struct i2c_msg * max32_i3c_request_emit_stop(data, regs); max32_i3c_errwarn_clear_all_nowait(regs); max32_i3c_status_clear_all(regs); + + pm_policy_device_power_lock_put(dev); + k_mutex_unlock(&data->lock); return ret; @@ -1900,7 +2005,8 @@ static DEVICE_API(i3c, max32_i3c_driver_api) = { .common.ctrl_config.scl.i3c = DT_INST_PROP_OR(id, i3c_scl_hz, 0), \ .common.ctrl_config.scl.i2c = DT_INST_PROP_OR(id, i2c_scl_hz, 0), \ }; \ - DEVICE_DT_INST_DEFINE(id, max32_i3c_init, NULL, &max32_i3c_data_##id, \ + PM_DEVICE_DT_INST_DEFINE(id, max32_i3c_pm_action); \ + DEVICE_DT_INST_DEFINE(id, max32_i3c_init, PM_DEVICE_DT_INST_GET(id), &max32_i3c_data_##id, \ &max32_i3c_config_##id, POST_KERNEL, \ CONFIG_I3C_CONTROLLER_INIT_PRIORITY, &max32_i3c_driver_api); \ static void max32_i3c_config_func_##id(const struct device *dev) \ diff --git a/dts/arm/adi/max32/max32657_common.dtsi b/dts/arm/adi/max32/max32657_common.dtsi index dbe5593020c1..68ce8a1cac0f 100644 --- a/dts/arm/adi/max32/max32657_common.dtsi +++ b/dts/arm/adi/max32/max32657_common.dtsi @@ -176,6 +176,7 @@ clocks = <&gcr ADI_MAX32_CLOCK_BUS0 13>; interrupts = <10 0>; status = "disabled"; + zephyr,disabling-power-states = <&standby &backup>; }; uart0: serial@42000 { From 8838eb418db9c6de04db0446f2328478b97e829f Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 22 Aug 2026 11:13:16 +1000 Subject: [PATCH 248/455] pm: document `PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ` Add help text for the `PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ` symbol. Signed-off-by: Jordan Yates --- subsys/pm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/subsys/pm/Kconfig b/subsys/pm/Kconfig index e7158571084d..b0ac7ffa6014 100644 --- a/subsys/pm/Kconfig +++ b/subsys/pm/Kconfig @@ -168,6 +168,10 @@ if PM_DEVICE_RUNTIME config PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ bool + help + Use this option to signal that the driver performs blocking + operations during suspend, and therefore the system workqueue cannot + be used. config PM_DEVICE_RUNTIME_ASYNC bool "Asynchronous device runtime power management" From fe2be458a4ab4d3f6f1feca606061bac082ba43d Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 22 Aug 2026 11:10:10 +1000 Subject: [PATCH 249/455] modem: cellular: select `PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ` The `PM_DEVICE_ACTION_SUSPEND` implementation can block for excessive durations, see: ``` ret = k_sem_take(&data->suspended_sem, K_SECONDS(30)); ```` Blocking the system workqueue for this long is a very bad idea even if it did work, and it doesn't, because the modem libraries also depend on the system workqueue to operate correctly. This is the exact situation the dedicated workqueue was added for, use it. Signed-off-by: Jordan Yates --- drivers/modem/Kconfig.cellular | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/modem/Kconfig.cellular b/drivers/modem/Kconfig.cellular index 1f850d40a987..99ebf27677b9 100644 --- a/drivers/modem/Kconfig.cellular +++ b/drivers/modem/Kconfig.cellular @@ -12,6 +12,7 @@ config MODEM_CELLULAR select MODEM_PIPELINK select MODEM_BACKEND_UART select UART_USE_RUNTIME_CONFIGURE + select PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ if PM_DEVICE_RUNTIME select RING_BUFFER select NET_L2_PPP_OPTION_MRU select NET_L2_PPP_PAP From 136f4bc66eee62dd2eede749475e330012df35ce Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 10:32:25 +1000 Subject: [PATCH 250/455] modem: cellular: reject `SUSPEND` from system workqueue Reject attempts to suspend modem cellular drivers from the system workqueue. Suspending from the system workqueue cannot work for several reasons: * `modem_cellular_delegate_event` handling runs on the workqueue * `modem_chat` relies on workqueue for executing any shutdown commands Output the error to make it clear to users what is happening, and how to avoid it (don't call `pm_device_runtime_put` from the system workqueue). Signed-off-by: Jordan Yates --- drivers/modem/modem_cellular.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/modem/modem_cellular.c b/drivers/modem/modem_cellular.c index 44b6bdc86a41..bdfaca8f6174 100644 --- a/drivers/modem/modem_cellular.c +++ b/drivers/modem/modem_cellular.c @@ -2723,6 +2723,7 @@ DEVICE_API(cellular, modem_cellular_api) = { int modem_cellular_pm_action(const struct device *dev, enum pm_device_action action) { struct modem_cellular_data *data = (struct modem_cellular_data *)dev->data; + k_tid_t current_thread = k_current_get(); int ret; switch (action) { @@ -2732,6 +2733,17 @@ int modem_cellular_pm_action(const struct device *dev, enum pm_device_action act break; case PM_DEVICE_ACTION_SUSPEND: + if (current_thread == k_work_queue_thread_get(&k_sys_work_q)) { + /* Suspending from the system workqueue cannot work for several reasons: + * `modem_cellular_delegate_event` handling runs on the workqueue + * `modem_chat` relies on workqueue for executing any shutdown commands + * Output the error here to make it clear to users what is happening, and + * how to avoid it (don't call `net_if_down` or `conn_mgr_all_if_down` from + * the system workqueue). + */ + LOG_ERR("Cannot suspend from system workqueue"); + return -EDEADLK; + } modem_cellular_delegate_event(data, MODEM_CELLULAR_EVENT_SUSPEND); ret = k_sem_take(&data->suspended_sem, K_SECONDS(30)); break; From 9596f509fa765dcd1cc2c23149bda945d581dfe8 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 22 Aug 2026 11:22:04 +1000 Subject: [PATCH 251/455] samples: net: cellular_modem: fix PM usage The PPP networking interface has been linked to the underlying modem device since PR #96579. As such, the sample can operate purely through `net_if_up` and `net_if_down` interfaces, which is also how end applications should be controlling it (or a higher level like `conn_mgr`). The sample should also definitely not be calling `pm_device_action_run` directly, but that is moot with the above change. The sample should also be enabling runtime PM. Signed-off-by: Jordan Yates --- samples/net/cellular_modem/prj.conf | 1 + samples/net/cellular_modem/src/main.c | 31 +++++++++++---------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/samples/net/cellular_modem/prj.conf b/samples/net/cellular_modem/prj.conf index 5030b0aa07ff..f77e8b7b6bf2 100644 --- a/samples/net/cellular_modem/prj.conf +++ b/samples/net/cellular_modem/prj.conf @@ -23,6 +23,7 @@ CONFIG_NET_CONNECTION_MANAGER=y # Modem driver CONFIG_MODEM=y CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y CONFIG_MODEM_CELLULAR=y # Dynamic APN configuration (uncomment if not needed) diff --git a/samples/net/cellular_modem/src/main.c b/samples/net/cellular_modem/src/main.c index c86c804a6d56..7dc9cd5528b8 100644 --- a/samples/net/cellular_modem/src/main.c +++ b/samples/net/cellular_modem/src/main.c @@ -468,9 +468,6 @@ int main(void) ppp_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(PPP)); - printk("Powering on modem\n"); - pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME); - printk("Bring up network interface\n"); ret = net_if_up(ppp_iface); if (ret < 0) { @@ -544,15 +541,19 @@ int main(void) } power_cycle: - printk("Shutting down modem\n"); - ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND); - if (ret != 0) { - printk("Failed to power down modem\n"); + printk("Taking interface down\n"); + ret = net_if_down(ppp_iface); + if (ret < 0) { + printk("Failed to take down network interface\n"); return -1; } - printk("Restarting modem\n"); - pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME); + printk("Requesting interface up\n"); + ret = net_if_up(ppp_iface); + if (ret < 0) { + printk("Failed to request network interface back up\n"); + return -1; + } printk("Waiting for L4 connected\n"); ret = k_event_wait(&l4_event, L4_CONNECTED, false, K_SECONDS(120)); @@ -562,7 +563,7 @@ int main(void) } printk("L4 connected\n"); - /* Wait a bit to avoid (unsuccessfully) trying to send the first echo packet too quickly. */ + /* Wait a bit to avoid (unsuccessfully) trying to send the first echo packet too quickly */ k_sleep(K_SECONDS(5)); if (valid_dns) { @@ -576,16 +577,10 @@ int main(void) return -1; } + printk("Final interface down\n"); ret = net_if_down(ppp_iface); if (ret < 0) { - printk("Failed to bring down network interface\n"); - return -1; - } - - printk("Powering down modem\n"); - ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND); - if (ret != 0) { - printk("Failed to power down modem\n"); + printk("Failed to take down network interface\n"); return -1; } From bd9a32ab40ded33adda2078bc649ff07d5233e3d Mon Sep 17 00:00:00 2001 From: Pisit Sawangvonganan Date: Wed, 26 Aug 2026 13:13:48 +0700 Subject: [PATCH 252/455] net: tcp: fix typo in test callback registration Use the correct `NET_SOCK_STREAM` constant when registering TCP test callbacks for TTCN-3 based TCP sanity checks. Signed-off-by: Pisit Sawangvonganan --- subsys/net/ip/tcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 462a3b8af37e..ad6e22e6416c 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -5030,9 +5030,9 @@ void net_tcp_init(void) int rto; #if defined(CONFIG_NET_TEST_PROTOCOL) /* Register inputs for TTCN-3 based TCP sanity check */ - test_cb_register(NET_AF_INET, NET_NET_SOCK_STREAM, NET_IPPROTO_TCP, + test_cb_register(NET_AF_INET, NET_SOCK_STREAM, NET_IPPROTO_TCP, 4242, 4242, tcp_input); - test_cb_register(NET_AF_INET6, NET_NET_SOCK_STREAM, NET_IPPROTO_TCP, + test_cb_register(NET_AF_INET6, NET_SOCK_STREAM, NET_IPPROTO_TCP, 4242, 4242, tcp_input); test_cb_register(NET_AF_INET, NET_SOCK_DGRAM, NET_IPPROTO_UDP, 4242, 4242, tp_input); From a31e549c997f47703091f0458e8f0cfa8ca8d8d4 Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Mon, 24 Aug 2026 14:45:18 +0300 Subject: [PATCH 253/455] tests: flash: common: Migrate MAX32 boards to memory-mapped partitions Migrate ADI MAX32 boards' flash partitions from `fixed-partitions` to `zephyr,mapped-partition` and move the flash partition configuration from board overlays into the board DTS files. Also enable `USE_DT_CODE_PARTITION` to link the application into the `zephyr,code-partition`. Signed-off-by: Tahsin Mutlugun --- boards/adi/max32662evkit/max32662evkit.dts | 23 +++++++++++++- .../adi/max32662evkit/max32662evkit_defconfig | 4 ++- .../max32666evkit_max32666_cpu0.dts | 23 +++++++++++++- .../max32666evkit_max32666_cpu0_defconfig | 4 ++- .../max32666evkit_max32666_cpu1.dts | 23 +++++++++++++- .../max32666evkit_max32666_cpu1_defconfig | 4 ++- .../max32666fthr_max32666_cpu0.dts | 21 +++++++++++++ .../max32666fthr_max32666_cpu0_defconfig | 4 ++- boards/adi/max32670evkit/max32670evkit.dts | 23 +++++++++++++- .../adi/max32670evkit/max32670evkit_defconfig | 4 ++- boards/adi/max32672evkit/max32672evkit.dts | 23 +++++++++++++- .../adi/max32672evkit/max32672evkit_defconfig | 4 ++- boards/adi/max32672fthr/max32672fthr.dts | 23 +++++++++++++- .../adi/max32672fthr/max32672fthr_defconfig | 4 ++- boards/adi/max32675evkit/max32675evkit.dts | 23 +++++++++++++- .../adi/max32675evkit/max32675evkit_defconfig | 4 ++- .../max32680evkit_max32680_m4.dts | 23 +++++++++++++- .../max32680evkit_max32680_m4_defconfig | 4 ++- .../max32690fthr/max32690fthr_max32690_m4.dts | 23 +++++++++++++- .../max32690fthr_max32690_m4_defconfig | 2 ++ .../max78000evkit_max78000_m4.dts | 23 +++++++++++++- .../max78000evkit_max78000_m4_defconfig | 4 ++- .../flash/common/boards/max32662evkit.overlay | 23 -------------- .../boards/max32666evkit_max32666_cpu0.conf | 1 - .../max32666evkit_max32666_cpu0.overlay | 31 ------------------- .../boards/max32666evkit_max32666_cpu1.conf | 1 - .../max32666evkit_max32666_cpu1.overlay | 31 ------------------- .../boards/max32666fthr_max32666_cpu0.overlay | 23 -------------- .../flash/common/boards/max32670evkit.overlay | 23 -------------- .../flash/common/boards/max32672evkit.overlay | 23 -------------- .../flash/common/boards/max32672fthr.overlay | 23 -------------- .../flash/common/boards/max32675evkit.overlay | 23 -------------- .../boards/max32680evkit_max32680_m4.overlay | 23 -------------- .../max32690_flash1_storage_partition.overlay | 17 ++-------- .../boards/max32690fthr_max32690_m4.overlay | 23 -------------- 35 files changed, 275 insertions(+), 283 deletions(-) delete mode 100644 tests/drivers/flash/common/boards/max32662evkit.overlay delete mode 100644 tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.conf delete mode 100644 tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.overlay delete mode 100644 tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.conf delete mode 100644 tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.overlay delete mode 100644 tests/drivers/flash/common/boards/max32666fthr_max32666_cpu0.overlay delete mode 100644 tests/drivers/flash/common/boards/max32670evkit.overlay delete mode 100644 tests/drivers/flash/common/boards/max32672evkit.overlay delete mode 100644 tests/drivers/flash/common/boards/max32672fthr.overlay delete mode 100644 tests/drivers/flash/common/boards/max32675evkit.overlay delete mode 100644 tests/drivers/flash/common/boards/max32680evkit_max32680_m4.overlay delete mode 100644 tests/drivers/flash/common/boards/max32690fthr_max32690_m4.overlay diff --git a/boards/adi/max32662evkit/max32662evkit.dts b/boards/adi/max32662evkit/max32662evkit.dts index 44300b625b0e..761f298f19ff 100644 --- a/boards/adi/max32662evkit/max32662evkit.dts +++ b/boards/adi/max32662evkit/max32662evkit.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ compatible = "adi,max32662evkit"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram2; @@ -159,3 +160,23 @@ pinctrl-0 = <&can0b_tx_p0_16 &can0b_rx_p0_15>; pinctrl-names = "default"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(192)>; + read-only; + }; + + storage_partition: partition@30000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x30000 DT_SIZE_K(64)>; + }; + }; +}; diff --git a/boards/adi/max32662evkit/max32662evkit_defconfig b/boards/adi/max32662evkit/max32662evkit_defconfig index 38ffe5e0e7a2..98be802336b3 100644 --- a/boards/adi/max32662evkit/max32662evkit_defconfig +++ b/boards/adi/max32662evkit/max32662evkit_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts b/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts index 918c1718e07a..c678adf32ec9 100644 --- a/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts +++ b/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ / { chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart1; zephyr,shell-uart = &uart1; zephyr,sram = &sram4; @@ -90,6 +91,26 @@ zephyr_udc0: &usbhs { status = "okay"; }; +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(256)>; + read-only; + }; + + storage_partition: partition@40000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x40000 DT_SIZE_K(256)>; + }; + }; +}; + /* Ensure we don't attempt to access flash instance used by CPU1 */ &flash1 { status = "disabled"; diff --git a/boards/adi/max32666evkit/max32666evkit_max32666_cpu0_defconfig b/boards/adi/max32666evkit/max32666evkit_max32666_cpu0_defconfig index a97eac06f2a7..b52f14c25f77 100644 --- a/boards/adi/max32666evkit/max32666evkit_max32666_cpu0_defconfig +++ b/boards/adi/max32666evkit/max32666evkit_max32666_cpu0_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # enable uart driver CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts b/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts index 88f19f08e662..d31659cc6778 100644 --- a/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts +++ b/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ }; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram5; @@ -40,3 +41,23 @@ parity = "none"; status = "okay"; }; + +&flash1 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(128)>; + read-only; + }; + + storage_partition: partition@20000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x20000 DT_SIZE_K(128)>; + }; + }; +}; diff --git a/boards/adi/max32666evkit/max32666evkit_max32666_cpu1_defconfig b/boards/adi/max32666evkit/max32666evkit_max32666_cpu1_defconfig index 1a3a3add9049..7ee6c95d38a7 100644 --- a/boards/adi/max32666evkit/max32666evkit_max32666_cpu1_defconfig +++ b/boards/adi/max32666evkit/max32666evkit_max32666_cpu1_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART driver CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts b/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts index 5e2ae221badc..af785a31fa5d 100644 --- a/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts +++ b/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts @@ -16,6 +16,7 @@ compatible = "adi,max32666fthr"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart1; zephyr,shell-uart = &uart1; zephyr,sram = &sram4; @@ -201,3 +202,23 @@ feather_i2c: &i2c0 { zephyr_udc0: &usbhs { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(256)>; + read-only; + }; + + storage_partition: partition@40000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x40000 DT_SIZE_K(256)>; + }; + }; +}; diff --git a/boards/adi/max32666fthr/max32666fthr_max32666_cpu0_defconfig b/boards/adi/max32666fthr/max32666fthr_max32666_cpu0_defconfig index b118723e8403..cc4d15283ed1 100644 --- a/boards/adi/max32666fthr/max32666fthr_max32666_cpu0_defconfig +++ b/boards/adi/max32666fthr/max32666fthr_max32666_cpu0_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024 Analog Devices, Inc. +# Copyright (c) 2023-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # enable uart driver CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32670evkit/max32670evkit.dts b/boards/adi/max32670evkit/max32670evkit.dts index e6fa83a721bc..79f894ab938c 100644 --- a/boards/adi/max32670evkit/max32670evkit.dts +++ b/boards/adi/max32670evkit/max32670evkit.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ compatible = "adi,max32670evkit"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram3; @@ -110,3 +111,23 @@ &rtc_counter { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(256)>; + read-only; + }; + + storage_partition: partition@20000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x20000 DT_SIZE_K(128)>; + }; + }; +}; diff --git a/boards/adi/max32670evkit/max32670evkit_defconfig b/boards/adi/max32670evkit/max32670evkit_defconfig index 38ffe5e0e7a2..98be802336b3 100644 --- a/boards/adi/max32670evkit/max32670evkit_defconfig +++ b/boards/adi/max32670evkit/max32670evkit_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32672evkit/max32672evkit.dts b/boards/adi/max32672evkit/max32672evkit.dts index 36a464bc5364..8d852de07385 100644 --- a/boards/adi/max32672evkit/max32672evkit.dts +++ b/boards/adi/max32672evkit/max32672evkit.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ compatible = "adi,max32672evkit"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram3; @@ -163,3 +164,23 @@ &rtc_counter { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(256)>; + read-only; + }; + + storage_partition: partition@20000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x20000 DT_SIZE_K(128)>; + }; + }; +}; diff --git a/boards/adi/max32672evkit/max32672evkit_defconfig b/boards/adi/max32672evkit/max32672evkit_defconfig index 38ffe5e0e7a2..98be802336b3 100644 --- a/boards/adi/max32672evkit/max32672evkit_defconfig +++ b/boards/adi/max32672evkit/max32672evkit_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32672fthr/max32672fthr.dts b/boards/adi/max32672fthr/max32672fthr.dts index 0ed07af00a13..8f3a1b6aec6b 100644 --- a/boards/adi/max32672fthr/max32672fthr.dts +++ b/boards/adi/max32672fthr/max32672fthr.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ compatible = "adi,max32672fthr"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram3; @@ -163,3 +164,23 @@ &rtc_counter { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(256)>; + read-only; + }; + + storage_partition: partition@20000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x20000 DT_SIZE_K(128)>; + }; + }; +}; diff --git a/boards/adi/max32672fthr/max32672fthr_defconfig b/boards/adi/max32672fthr/max32672fthr_defconfig index 38ffe5e0e7a2..98be802336b3 100644 --- a/boards/adi/max32672fthr/max32672fthr_defconfig +++ b/boards/adi/max32672fthr/max32672fthr_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32675evkit/max32675evkit.dts b/boards/adi/max32675evkit/max32675evkit.dts index c22b3cce2ea7..037f755738fc 100644 --- a/boards/adi/max32675evkit/max32675evkit.dts +++ b/boards/adi/max32675evkit/max32675evkit.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ compatible = "adi,max32675evkit"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram3; @@ -108,3 +109,23 @@ &wdt0 { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(256)>; + read-only; + }; + + storage_partition: partition@40000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x40000 DT_SIZE_K(128)>; + }; + }; +}; diff --git a/boards/adi/max32675evkit/max32675evkit_defconfig b/boards/adi/max32675evkit/max32675evkit_defconfig index 38ffe5e0e7a2..98be802336b3 100644 --- a/boards/adi/max32675evkit/max32675evkit_defconfig +++ b/boards/adi/max32675evkit/max32675evkit_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts b/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts index 7214a8f90ff2..c30ddf526d4c 100644 --- a/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts +++ b/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ compatible = "adi,max32680evkit"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart1; zephyr,shell-uart = &uart1; zephyr,sram = &sram2; @@ -185,3 +186,23 @@ &rtc_counter { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + reg = <0x0 DT_SIZE_K(384)>; + read-only; + }; + + storage_partition: partition@60000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x60000 DT_SIZE_K(128)>; + }; + }; +}; diff --git a/boards/adi/max32680evkit/max32680evkit_max32680_m4_defconfig b/boards/adi/max32680evkit/max32680evkit_max32680_m4_defconfig index 38ffe5e0e7a2..98be802336b3 100644 --- a/boards/adi/max32680evkit/max32680evkit_max32680_m4_defconfig +++ b/boards/adi/max32680evkit/max32680evkit_max32680_m4_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2024-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts b/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts index 3a49a149ec64..d1c078fd4595 100644 --- a/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts +++ b/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Analog Devices, Inc. + * Copyright (c) 2023-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ compatible = "adi,max32690fthr"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram0; @@ -149,3 +150,23 @@ feather_spi: &spi0 { zephyr_udc0: &usbhs { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + label = "image-m4"; + reg = <0x0 DT_SIZE_M(2)>; + }; + + storage_partition: partition@200000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x200000 DT_SIZE_M(1)>; + }; + }; +}; diff --git a/boards/adi/max32690fthr/max32690fthr_max32690_m4_defconfig b/boards/adi/max32690fthr/max32690fthr_max32690_m4_defconfig index 516300c7db0d..3ccf4b0d3762 100644 --- a/boards/adi/max32690fthr/max32690fthr_max32690_m4_defconfig +++ b/boards/adi/max32690fthr/max32690fthr_max32690_m4_defconfig @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/boards/adi/max78000evkit/max78000evkit_max78000_m4.dts b/boards/adi/max78000evkit/max78000evkit_max78000_m4.dts index 8f8ec6f463d6..748fd925d727 100644 --- a/boards/adi/max78000evkit/max78000evkit_max78000_m4.dts +++ b/boards/adi/max78000evkit/max78000evkit_max78000_m4.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Analog Devices, Inc. + * Copyright (c) 2025-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ compatible = "adi,max78000evkit"; chosen { + zephyr,code-partition = &code_partition; zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,sram = &sram2; @@ -134,3 +135,23 @@ &rtc_counter { status = "okay"; }; + +&flash0 { + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + label = "image-m4"; + reg = <0x0 DT_SIZE_K(448)>; + }; + + storage_partition: partition@70000 { + compatible = "zephyr,mapped-partition"; + label = "storage-m4"; + reg = <0x70000 DT_SIZE_K(64)>; + }; + }; +}; diff --git a/boards/adi/max78000evkit/max78000evkit_max78000_m4_defconfig b/boards/adi/max78000evkit/max78000evkit_max78000_m4_defconfig index 9428e5334a08..c7bb5bebcf09 100644 --- a/boards/adi/max78000evkit/max78000evkit_max78000_m4_defconfig +++ b/boards/adi/max78000evkit/max78000evkit_max78000_m4_defconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2025 Analog Devices, Inc. +# Copyright (c) 2025-2026 Analog Devices, Inc. # SPDX-License-Identifier: Apache-2.0 # Enable MPU @@ -14,3 +14,5 @@ CONFIG_UART_CONSOLE=y # Enable UART CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y + +CONFIG_USE_DT_CODE_PARTITION=y diff --git a/tests/drivers/flash/common/boards/max32662evkit.overlay b/tests/drivers/flash/common/boards/max32662evkit.overlay deleted file mode 100644 index 01d2c6b0bfda..000000000000 --- a/tests/drivers/flash/common/boards/max32662evkit.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(192)>; - read-only; - }; - - storage_partition: partition@30000 { - label = "storage"; - reg = <0x30000 DT_SIZE_K(64)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.conf b/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.conf deleted file mode 100644 index 3d5bd27bd6a0..000000000000 --- a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.conf +++ /dev/null @@ -1 +0,0 @@ -CONFIG_USE_DT_CODE_PARTITION=y diff --git a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.overlay b/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.overlay deleted file mode 100644 index 819d462f142c..000000000000 --- a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu0.overlay +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/ { - chosen { - zephyr,code-partition = &code_partition; - }; -}; - -&flash0 { - partitions { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - code_partition: partition@0 { - compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; - read-only; - }; - - storage_partition: partition@40000 { - compatible = "zephyr,mapped-partition"; - label = "storage"; - reg = <0x40000 DT_SIZE_K(256)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.conf b/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.conf deleted file mode 100644 index 3d5bd27bd6a0..000000000000 --- a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.conf +++ /dev/null @@ -1 +0,0 @@ -CONFIG_USE_DT_CODE_PARTITION=y diff --git a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.overlay b/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.overlay deleted file mode 100644 index 6ec90dd3838a..000000000000 --- a/tests/drivers/flash/common/boards/max32666evkit_max32666_cpu1.overlay +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/ { - chosen { - zephyr,code-partition = &code_partition; - }; -}; - -&flash1 { - partitions { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - code_partition: partition@0 { - compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(128)>; - read-only; - }; - - storage_partition: partition@20000 { - compatible = "zephyr,mapped-partition"; - label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32666fthr_max32666_cpu0.overlay b/tests/drivers/flash/common/boards/max32666fthr_max32666_cpu0.overlay deleted file mode 100644 index 4e008dfde665..000000000000 --- a/tests/drivers/flash/common/boards/max32666fthr_max32666_cpu0.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(256)>; - read-only; - }; - - storage_partition: partition@40000 { - label = "storage"; - reg = <0x40000 DT_SIZE_K(256)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32670evkit.overlay b/tests/drivers/flash/common/boards/max32670evkit.overlay deleted file mode 100644 index e22ab31610aa..000000000000 --- a/tests/drivers/flash/common/boards/max32670evkit.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(256)>; - read-only; - }; - - storage_partition: partition@20000 { - label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32672evkit.overlay b/tests/drivers/flash/common/boards/max32672evkit.overlay deleted file mode 100644 index e22ab31610aa..000000000000 --- a/tests/drivers/flash/common/boards/max32672evkit.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(256)>; - read-only; - }; - - storage_partition: partition@20000 { - label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32672fthr.overlay b/tests/drivers/flash/common/boards/max32672fthr.overlay deleted file mode 100644 index e22ab31610aa..000000000000 --- a/tests/drivers/flash/common/boards/max32672fthr.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(256)>; - read-only; - }; - - storage_partition: partition@20000 { - label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32675evkit.overlay b/tests/drivers/flash/common/boards/max32675evkit.overlay deleted file mode 100644 index 967c67228f17..000000000000 --- a/tests/drivers/flash/common/boards/max32675evkit.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(256)>; - read-only; - }; - - storage_partition: partition@40000 { - label = "storage"; - reg = <0x40000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32680evkit_max32680_m4.overlay b/tests/drivers/flash/common/boards/max32680evkit_max32680_m4.overlay deleted file mode 100644 index 9f957f022268..000000000000 --- a/tests/drivers/flash/common/boards/max32680evkit_max32680_m4.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_K(384)>; - read-only; - }; - - storage_partition: partition@60000 { - label = "storage"; - reg = <0x60000 DT_SIZE_K(128)>; - }; - }; -}; diff --git a/tests/drivers/flash/common/boards/max32690_flash1_storage_partition.overlay b/tests/drivers/flash/common/boards/max32690_flash1_storage_partition.overlay index aa8a0ae725c0..9280fd35c407 100644 --- a/tests/drivers/flash/common/boards/max32690_flash1_storage_partition.overlay +++ b/tests/drivers/flash/common/boards/max32690_flash1_storage_partition.overlay @@ -1,22 +1,9 @@ /* - * Copyright (c) 2024 Analog Devices, Inc. + * Copyright (c) 2024-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ /delete-node/ &storage_partition; -&flash1 { - status = "okay"; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - storage_partition: partition@0 { - label = "storage"; - reg = <0x0 DT_SIZE_K(256)>; - }; - }; -}; +storage_partition: &rv32_storage_partition {}; diff --git a/tests/drivers/flash/common/boards/max32690fthr_max32690_m4.overlay b/tests/drivers/flash/common/boards/max32690fthr_max32690_m4.overlay deleted file mode 100644 index dee6fb376908..000000000000 --- a/tests/drivers/flash/common/boards/max32690fthr_max32690_m4.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Analog Devices, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - reg = <0x0 DT_SIZE_M(2)>; - read-only; - }; - - storage_partition: partition@200000 { - label = "storage"; - reg = <0x200000 DT_SIZE_M(1)>; - }; - }; -}; From 9a52c264598fce30eba3f36aef081ba9a32f09e8 Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Mon, 24 Aug 2026 20:32:47 +0300 Subject: [PATCH 254/455] board: adi: Set storage partition size to 64KiB Reallocate flash partitions on MAX32 boards to reduce the default storage partition size to 64 KiB. Signed-off-by: Tahsin Mutlugun --- boards/adi/max32655evkit/max32655evkit.dtsi | 6 +++--- boards/adi/max32655fthr/max32655fthr.dtsi | 8 ++++---- .../max32666evkit/max32666evkit_max32666_cpu0.dts | 6 +++--- .../max32666evkit/max32666evkit_max32666_cpu1.dts | 6 +++--- .../max32666fthr/max32666fthr_max32666_cpu0.dts | 6 +++--- boards/adi/max32670evkit/max32670evkit.dts | 6 +++--- boards/adi/max32672evkit/max32672evkit.dts | 6 +++--- boards/adi/max32672fthr/max32672fthr.dts | 6 +++--- boards/adi/max32675evkit/max32675evkit.dts | 6 +++--- .../max32680evkit/max32680evkit_max32680_m4.dts | 6 +++--- boards/adi/max32690evkit/max32690evkit.dtsi | 6 +++--- .../adi/max32690fthr/max32690fthr_max32690_m4.dts | 6 +++--- boards/adi/max78000fthr/max78000fthr.dtsi | 14 +++++++------- boards/adi/max78002evkit/max78002evkit.dtsi | 12 ++++++------ 14 files changed, 50 insertions(+), 50 deletions(-) diff --git a/boards/adi/max32655evkit/max32655evkit.dtsi b/boards/adi/max32655evkit/max32655evkit.dtsi index 56f9c215e4ea..2c6a4bc7c0de 100644 --- a/boards/adi/max32655evkit/max32655evkit.dtsi +++ b/boards/adi/max32655evkit/max32655evkit.dtsi @@ -91,13 +91,13 @@ m4_partition: partition@0 { compatible = "zephyr,mapped-partition"; label = "image-m4"; - reg = <0x0 DT_SIZE_K(352)>; + reg = <0x0 DT_SIZE_K(320)>; }; - storage_partition: partition@58000 { + storage_partition: partition@50000 { compatible = "zephyr,mapped-partition"; label = "storage-m4"; - reg = <0x58000 DT_SIZE_K(32)>; + reg = <0x50000 DT_SIZE_K(64)>; }; rv32_partition: partition@60000 { diff --git a/boards/adi/max32655fthr/max32655fthr.dtsi b/boards/adi/max32655fthr/max32655fthr.dtsi index 914b8648bfac..f11ab9d9560b 100644 --- a/boards/adi/max32655fthr/max32655fthr.dtsi +++ b/boards/adi/max32655fthr/max32655fthr.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025 Analog Devices, Inc. + * Copyright (c) 2023-2026 Analog Devices, Inc. * * SPDX-License-Identifier: Apache-2.0 */ @@ -215,13 +215,13 @@ feather_spi: &spi1 { m4_partition: partition@0 { compatible = "zephyr,mapped-partition"; label = "image-m4"; - reg = <0x0 DT_SIZE_K(352)>; + reg = <0x0 DT_SIZE_K(320)>; }; - storage_partition: partition@58000 { + storage_partition: partition@50000 { compatible = "zephyr,mapped-partition"; label = "storage-m4"; - reg = <0x58000 DT_SIZE_K(32)>; + reg = <0x50000 DT_SIZE_K(64)>; }; rv32_partition: partition@60000 { diff --git a/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts b/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts index c678adf32ec9..cc1fc71e3787 100644 --- a/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts +++ b/boards/adi/max32666evkit/max32666evkit_max32666_cpu0.dts @@ -99,14 +99,14 @@ zephyr_udc0: &usbhs { code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; + reg = <0x0 DT_SIZE_K(448)>; read-only; }; - storage_partition: partition@40000 { + storage_partition: partition@70000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x40000 DT_SIZE_K(256)>; + reg = <0x70000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts b/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts index d31659cc6778..e56019303dee 100644 --- a/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts +++ b/boards/adi/max32666evkit/max32666evkit_max32666_cpu1.dts @@ -50,14 +50,14 @@ code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(128)>; + reg = <0x0 DT_SIZE_K(448)>; read-only; }; - storage_partition: partition@20000 { + storage_partition: partition@70000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; + reg = <0x70000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts b/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts index af785a31fa5d..ac694b407d74 100644 --- a/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts +++ b/boards/adi/max32666fthr/max32666fthr_max32666_cpu0.dts @@ -211,14 +211,14 @@ zephyr_udc0: &usbhs { code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; + reg = <0x0 DT_SIZE_K(448)>; read-only; }; - storage_partition: partition@40000 { + storage_partition: partition@70000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x40000 DT_SIZE_K(256)>; + reg = <0x70000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32670evkit/max32670evkit.dts b/boards/adi/max32670evkit/max32670evkit.dts index 79f894ab938c..28dabe601615 100644 --- a/boards/adi/max32670evkit/max32670evkit.dts +++ b/boards/adi/max32670evkit/max32670evkit.dts @@ -120,14 +120,14 @@ code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; + reg = <0x0 DT_SIZE_K(320)>; read-only; }; - storage_partition: partition@20000 { + storage_partition: partition@50000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; + reg = <0x50000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32672evkit/max32672evkit.dts b/boards/adi/max32672evkit/max32672evkit.dts index 8d852de07385..bd79a3da5ddb 100644 --- a/boards/adi/max32672evkit/max32672evkit.dts +++ b/boards/adi/max32672evkit/max32672evkit.dts @@ -173,14 +173,14 @@ code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; + reg = <0x0 DT_SIZE_K(960)>; read-only; }; - storage_partition: partition@20000 { + storage_partition: partition@f0000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; + reg = <0xf0000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32672fthr/max32672fthr.dts b/boards/adi/max32672fthr/max32672fthr.dts index 8f3a1b6aec6b..9137b42eca42 100644 --- a/boards/adi/max32672fthr/max32672fthr.dts +++ b/boards/adi/max32672fthr/max32672fthr.dts @@ -173,14 +173,14 @@ code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; + reg = <0x0 DT_SIZE_K(960)>; read-only; }; - storage_partition: partition@20000 { + storage_partition: partition@f0000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x20000 DT_SIZE_K(128)>; + reg = <0xf0000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32675evkit/max32675evkit.dts b/boards/adi/max32675evkit/max32675evkit.dts index 037f755738fc..b1f29f70426b 100644 --- a/boards/adi/max32675evkit/max32675evkit.dts +++ b/boards/adi/max32675evkit/max32675evkit.dts @@ -118,14 +118,14 @@ code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(256)>; + reg = <0x0 DT_SIZE_K(320)>; read-only; }; - storage_partition: partition@40000 { + storage_partition: partition@50000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x40000 DT_SIZE_K(128)>; + reg = <0x50000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts b/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts index c30ddf526d4c..257f73ec5e8c 100644 --- a/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts +++ b/boards/adi/max32680evkit/max32680evkit_max32680_m4.dts @@ -195,14 +195,14 @@ code_partition: partition@0 { compatible = "zephyr,mapped-partition"; - reg = <0x0 DT_SIZE_K(384)>; + reg = <0x0 DT_SIZE_K(448)>; read-only; }; - storage_partition: partition@60000 { + storage_partition: partition@70000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x60000 DT_SIZE_K(128)>; + reg = <0x70000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32690evkit/max32690evkit.dtsi b/boards/adi/max32690evkit/max32690evkit.dtsi index cf0d3c8e59c5..07684d17a248 100644 --- a/boards/adi/max32690evkit/max32690evkit.dtsi +++ b/boards/adi/max32690evkit/max32690evkit.dtsi @@ -103,13 +103,13 @@ m4_partition: partition@0 { compatible = "zephyr,mapped-partition"; label = "image-m4"; - reg = <0x0 DT_SIZE_M(1)>; + reg = <0x0 DT_SIZE_K(3008)>; }; - storage_partition: partition@100000 { + storage_partition: partition@2f0000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x100000 DT_SIZE_K(64)>; + reg = <0x2f0000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts b/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts index d1c078fd4595..0389e47b490a 100644 --- a/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts +++ b/boards/adi/max32690fthr/max32690fthr_max32690_m4.dts @@ -160,13 +160,13 @@ zephyr_udc0: &usbhs { code_partition: partition@0 { compatible = "zephyr,mapped-partition"; label = "image-m4"; - reg = <0x0 DT_SIZE_M(2)>; + reg = <0x0 DT_SIZE_K(3008)>; }; - storage_partition: partition@200000 { + storage_partition: partition@2f0000 { compatible = "zephyr,mapped-partition"; label = "storage"; - reg = <0x200000 DT_SIZE_M(1)>; + reg = <0x2f0000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max78000fthr/max78000fthr.dtsi b/boards/adi/max78000fthr/max78000fthr.dtsi index 55d08900ef99..73802b83cd85 100644 --- a/boards/adi/max78000fthr/max78000fthr.dtsi +++ b/boards/adi/max78000fthr/max78000fthr.dtsi @@ -139,25 +139,25 @@ feather_i2c: &i2c1 { m4_partition: partition@0 { compatible = "zephyr,mapped-partition"; label = "image-m4"; - reg = <0x0 DT_SIZE_K(224)>; + reg = <0x0 DT_SIZE_K(256)>; }; - m4_storage_partition: partition@38000 { + m4_storage_partition: partition@40000 { compatible = "zephyr,mapped-partition"; label = "storage-m4"; - reg = <0x38000 DT_SIZE_K(32)>; + reg = <0x40000 DT_SIZE_K(64)>; }; - rv32_partition: partition@40000 { + rv32_partition: partition@50000 { compatible = "zephyr,mapped-partition"; label = "image-rv32"; - reg = <0x40000 DT_SIZE_K(64)>; + reg = <0x50000 DT_SIZE_K(128)>; }; - rv32_storage_partition: partition@50000 { + rv32_storage_partition: partition@70000 { compatible = "zephyr,mapped-partition"; label = "storage-rv32"; - reg = <0x50000 DT_SIZE_K(32)>; + reg = <0x70000 DT_SIZE_K(64)>; }; }; }; diff --git a/boards/adi/max78002evkit/max78002evkit.dtsi b/boards/adi/max78002evkit/max78002evkit.dtsi index 548e6bde174c..42cf00759a9d 100644 --- a/boards/adi/max78002evkit/max78002evkit.dtsi +++ b/boards/adi/max78002evkit/max78002evkit.dtsi @@ -153,25 +153,25 @@ m4_uart: &uart0 {}; m4_partition: partition@0 { compatible = "zephyr,mapped-partition"; label = "image-m4"; - reg = <0x0 DT_SIZE_M(1)>; + reg = <0x0 DT_SIZE_K(1216)>; }; - m4_storage_partition: partition@100000 { + m4_storage_partition: partition@130000 { compatible = "zephyr,mapped-partition"; label = "storage-m4"; - reg = <0x100000 DT_SIZE_K(256)>; + reg = <0x130000 DT_SIZE_K(64)>; }; rv32_partition: partition@140000 { compatible = "zephyr,mapped-partition"; label = "image-rv32"; - reg = <0x140000 DT_SIZE_M(1)>; + reg = <0x140000 DT_SIZE_K(1216)>; }; - rv32_storage_partition: partition@240000 { + rv32_storage_partition: partition@270000 { compatible = "zephyr,mapped-partition"; label = "storage-rv32"; - reg = <0x240000 DT_SIZE_K(256)>; + reg = <0x270000 DT_SIZE_K(64)>; }; }; }; From 191c8516a08c7e3b02e73fb15610523d91036a69 Mon Sep 17 00:00:00 2001 From: Guotao Zhang Date: Tue, 25 Aug 2026 11:44:14 +0200 Subject: [PATCH 255/455] Bluetooth: Host: Return -EAGAIN from bt_disable() during async init When bt_enable() is called with a ready callback, bt_init() runs asynchronously on the system workqueue. If bt_disable() is invoked before that work item completes, it races with bt_init() over bt_dev.sent_cmd, which can be freed twice and trigger a buf.c:472 assert. Fix this by checking k_work_busy_get(&bt_dev.init) at the top of bt_disable(). If init is still queued or running, return -EAGAIN so the caller can retry once the ready callback fires. Note: the same race can occur with a synchronous bt_enable(NULL) called concurrently from another thread; that case will be covered by BT_DEV_ENABLING/BT_DEV_DISABLING transition flags introduced in a follow-up change. Document the new -EAGAIN return code in bt_disable()'s Doxygen. Signed-off-by: Guotao Zhang --- include/zephyr/bluetooth/bluetooth.h | 9 ++++++++- subsys/bluetooth/host/hci_core.c | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/zephyr/bluetooth/bluetooth.h b/include/zephyr/bluetooth/bluetooth.h index f51d42cf9de8..f89138e2eef5 100644 --- a/include/zephyr/bluetooth/bluetooth.h +++ b/include/zephyr/bluetooth/bluetooth.h @@ -337,6 +337,10 @@ int bt_enable(bt_ready_cb_t cb); * * Disable Bluetooth. Can't be called before bt_enable has completed. * + * When bt_enable() was called with a ready callback the initialization runs + * asynchronously. If bt_disable() is called before the ready callback fires, + * it returns -EAGAIN. The caller should retry after the ready callback. + * * This API will clear all configured identity addresses and keys that are not persistently * stored with @kconfig{CONFIG_BT_SETTINGS}. These can be restored * with @ref settings_load before reenabling the stack. @@ -349,7 +353,10 @@ int bt_enable(bt_ready_cb_t cb); * * Close and release HCI resources. Result is architecture dependent. * - * @return Zero on success or (negative) error code otherwise. + * @retval 0 Success. + * @retval -EAGAIN bt_enable() with a ready callback has not completed yet; + * retry after the ready callback fires. + * @retval -EALREADY bt_disable() has already been called. */ int bt_disable(void); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index e9a03a06c09e..c8546c6721f6 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -4859,6 +4859,16 @@ int bt_disable(void) struct net_buf *buf; int err; + /* When bt_enable() was called with a ready callback, bt_init() runs + * asynchronously in the init_work item. If bt_disable() is called + * before init has finished, reject it so that bt_init() can complete + * without racing against HCI_Reset. The caller should retry once + * bt_enable() has signalled its ready callback. + */ + if (k_work_busy_get(&bt_dev.init) != 0) { + return -EAGAIN; + } + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_DISABLE)) { return -EALREADY; } From 42956029310b0e8bc7f44b183b84cfb464c54694 Mon Sep 17 00:00:00 2001 From: Guotao Zhang Date: Tue, 25 Aug 2026 11:44:29 +0200 Subject: [PATCH 256/455] Bluetooth: Shell: Report error from bt disable command Report the error code when bt_disable() fails, including the new -EAGAIN case where initialization has not yet completed. Signed-off-by: Guotao Zhang --- subsys/bluetooth/host/shell/bt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/subsys/bluetooth/host/shell/bt.c b/subsys/bluetooth/host/shell/bt.c index 99c58654ee30..555c6c735d9c 100644 --- a/subsys/bluetooth/host/shell/bt.c +++ b/subsys/bluetooth/host/shell/bt.c @@ -1335,7 +1335,15 @@ static int cmd_init(const struct shell *sh, size_t argc, char *argv[]) static int cmd_disable(const struct shell *sh, size_t argc, char *argv[]) { - return bt_disable(); + int err; + + err = bt_disable(); + if (err != 0) { + shell_error(sh, "Bluetooth disable failed (err %d)", err); + return -ENOEXEC; + } + + return 0; } #ifdef CONFIG_SETTINGS From 820ec9af2e5e4885460152799b3da1acd5549f8f Mon Sep 17 00:00:00 2001 From: Guotao Zhang Date: Tue, 25 Aug 2026 11:44:49 +0200 Subject: [PATCH 257/455] Tests: Bluetooth: Add disable_async_init test Add a bsim test and CI script that verifies bt_disable() returns -EAGAIN when called before the bt_enable() ready callback fires. Signed-off-by: Guotao Zhang --- .../host/misc/disable/src/main_disable.c | 55 +++++++++++++++++++ .../tests_scripts/disable_async_init.sh | 20 +++++++ 2 files changed, 75 insertions(+) create mode 100755 tests/bsim/bluetooth/host/misc/disable/tests_scripts/disable_async_init.sh diff --git a/tests/bsim/bluetooth/host/misc/disable/src/main_disable.c b/tests/bsim/bluetooth/host/misc/disable/src/main_disable.c index 781b435aea14..e60ce1af373f 100644 --- a/tests/bsim/bluetooth/host/misc/disable/src/main_disable.c +++ b/tests/bsim/bluetooth/host/misc/disable/src/main_disable.c @@ -90,6 +90,56 @@ static void test_disable_set_default_id(void) TEST_PASS("Disable set default ID test passed"); } +K_SEM_DEFINE(ready_sem, 0, 1); + +static void ready_cb(int err) +{ + if (err != 0) { + TEST_FAIL("bt_enable ready callback error %d", err); + } else { + k_sem_give(&ready_sem); + } +} + +static void test_disable_async_init(void) +{ + /* Call bt_enable() with a ready callback so bt_init() runs + * asynchronously on the system workqueue. Immediately call + * bt_disable(); it must return -EAGAIN because init is still + * in progress. + * + * k_sched_lock() prevents the workqueue from running until + * k_sched_unlock() is called, so bt_disable() is guaranteed to + * observe the init work item as queued regardless of + * controller behavior. + */ + int err; + + k_sched_lock(); + + err = bt_enable(ready_cb); + if (err != 0) { + k_sched_unlock(); + TEST_FAIL("bt_enable failed (err %d)", err); + } + + err = bt_disable(); + k_sched_unlock(); + if (err != -EAGAIN) { + TEST_FAIL("Expected -EAGAIN from bt_disable before init complete, got %d", err); + } + + /* Wait for init to finish, then disable properly. */ + k_sem_take(&ready_sem, K_FOREVER); + + err = bt_disable(); + if (err != 0) { + TEST_FAIL("bt_disable after init failed (err %d)", err); + } + + TEST_PASS("disable async init test passed"); +} + static const struct bst_test_instance test_def[] = { { .test_id = "disable", @@ -101,6 +151,11 @@ static const struct bst_test_instance test_def[] = { .test_descr = "disable_test where each iteration sets the default ID", .test_main_f = test_disable_set_default_id }, + { + .test_id = "disable_async_init", + .test_descr = "bt_disable during async bt_init returns -EAGAIN", + .test_main_f = test_disable_async_init + }, BSTEST_END_MARKER }; diff --git a/tests/bsim/bluetooth/host/misc/disable/tests_scripts/disable_async_init.sh b/tests/bsim/bluetooth/host/misc/disable/tests_scripts/disable_async_init.sh new file mode 100755 index 000000000000..51a6f38c9fd2 --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/disable/tests_scripts/disable_async_init.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Copyright (c) 2026 NXP +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +# Disable test: bt_disable() called immediately after bt_enable(cb) must +# return -EAGAIN while async init is still in progress. +simulation_id="${BOARD_TS}_disable_async_init" +verbosity_level=2 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_host_misc_disable_prj_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=disable_async_init + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=1 -sim_length=10e6 $@ + +wait_for_background_jobs From 5fde24c35bc223aa6310b9b792640e11feea3b47 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 10:44:44 +1000 Subject: [PATCH 258/455] modem: cellular: `CELLULAR_EVENT_MODEM_SUSPENDED` Add a modem event that is output when the modem transitions back to the idle state. This can be used by higher level application code to monitor unexpected transitions from the `modem_cellular` state machine. Signed-off-by: Jordan Yates --- drivers/modem/modem_cellular.c | 1 + include/zephyr/drivers/cellular.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/modem/modem_cellular.c b/drivers/modem/modem_cellular.c index bdfaca8f6174..b14cf2f74318 100644 --- a/drivers/modem/modem_cellular.c +++ b/drivers/modem/modem_cellular.c @@ -848,6 +848,7 @@ static int modem_cellular_on_idle_state_enter(struct modem_cellular_data *data) modem_cmux_release(&data->cmux); modem_pipe_close_async(data->uart_pipe); k_sem_give(&data->suspended_sem); + modem_cellular_emit_event(data, CELLULAR_EVENT_MODEM_SUSPENDED, NULL); return 0; } diff --git a/include/zephyr/drivers/cellular.h b/include/zephyr/drivers/cellular.h index bd7edbbf1969..08f271833083 100644 --- a/include/zephyr/drivers/cellular.h +++ b/include/zephyr/drivers/cellular.h @@ -149,6 +149,8 @@ enum cellular_event { CELLULAR_EVENT_MODEM_COMMS_CHECK_RESULT = BIT(2), /** Cellular network status changed */ CELLULAR_EVENT_NETWORK_STATUS_CHANGED = BIT(3), + /** Cellular modem suspension callback */ + CELLULAR_EVENT_MODEM_SUSPENDED = BIT(4), }; /* Opaque bit-mask large enough for all current & future events */ From 0b5c9fa191d35d7a569d13841333a0205eb7b693 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 10:54:47 +1000 Subject: [PATCH 259/455] samples: net: cellular_modem: common event handling Always subscribe to modem events in the application, instead of only when `CONFIG_SAMPLE_CELLULAR_MODEM_AUTO_APN=y`. This will allow handling additional events with minimal further changes. Signed-off-by: Jordan Yates --- samples/net/cellular_modem/src/main.c | 36 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/samples/net/cellular_modem/src/main.c b/samples/net/cellular_modem/src/main.c index 7dc9cd5528b8..100eab20dd12 100644 --- a/samples/net/cellular_modem/src/main.c +++ b/samples/net/cellular_modem/src/main.c @@ -168,17 +168,9 @@ static int modem_cellular_find_apn(char *dst, size_t dst_sz, const char *key) return -ENOENT; } -static void modem_event_cb(const struct device *dev, enum cellular_event evt, const void *payload, - void *user_data) +static void auto_apn_modem_info_cb(const struct device *dev, + const struct cellular_evt_modem_info *mi) { - ARG_UNUSED(user_data); - - if (evt != CELLULAR_EVENT_MODEM_INFO_CHANGED) { - return; - } - - const struct cellular_evt_modem_info *mi = payload; - if (!mi || mi->field != CELLULAR_MODEM_INFO_SIM_IMSI) { return; /* not the IMSI notification */ } @@ -228,6 +220,21 @@ static void modem_event_cb(const struct device *dev, enum cellular_event evt, co #endif +static void modem_event_cb(const struct device *dev, enum cellular_event evt, const void *payload, + void *user_data) +{ + switch (evt) { + case CELLULAR_EVENT_MODEM_INFO_CHANGED: +#ifdef CONFIG_SAMPLE_CELLULAR_MODEM_AUTO_APN + auto_apn_modem_info_cb(dev, payload); +#endif + break; + default: + printk("Unhandled event: %d\n", evt); + break; + } +} + static void sample_dns_request_result(enum dns_resolve_status status, struct dns_addrinfo *info, void *user_data) { @@ -459,10 +466,11 @@ int main(void) uint16_t *port; int ret; -#ifdef CONFIG_SAMPLE_CELLULAR_MODEM_AUTO_APN - /* subscribe before powering the modem so we catch the IMSI event */ - cellular_set_callback(modem, CELLULAR_EVENT_MODEM_INFO_CHANGED, modem_event_cb, NULL); -#endif + /* Subscribe before powering the modem so we catch all events */ + ret = cellular_set_callback(modem, CELLULAR_EVENT_MODEM_INFO_CHANGED, modem_event_cb, NULL); + if (ret < 0) { + printk("Failed to subscribe to modem events (%d)\n", ret); + } init_sample_test_packet(); From 0325e9cf6198fbcdfb4ec5d3acbd6ab350e326a3 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Aug 2026 11:05:18 +1000 Subject: [PATCH 260/455] samples: net: cellular_modem: handle all events Handle all events in the sample to demonstrate what can be monitored at the application level. Signed-off-by: Jordan Yates --- samples/net/cellular_modem/src/main.c | 52 ++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/samples/net/cellular_modem/src/main.c b/samples/net/cellular_modem/src/main.c index 100eab20dd12..30439b1bf939 100644 --- a/samples/net/cellular_modem/src/main.c +++ b/samples/net/cellular_modem/src/main.c @@ -220,15 +220,61 @@ static void auto_apn_modem_info_cb(const struct device *dev, #endif +static void modem_registration_changed(const struct device *dev, + const struct cellular_evt_registration_status *rs) +{ + ARG_UNUSED(dev); + + printk("Registration status: %d\n", rs->status); +} + +static void comms_check_result(const struct device *dev, + const struct cellular_evt_modem_comms_check_result *ccr) +{ + ARG_UNUSED(dev); + + printk("Comms check %s\n", ccr->success ? "succeeded" : "failed"); +} + +static void network_status_changed(const struct device *dev, + const struct cellular_evt_network_status *ns) +{ + ARG_UNUSED(dev); + ARG_UNUSED(ns); + + printk("Network status changed\n"); +} + +static void modem_suspended(const struct device *dev) +{ + ARG_UNUSED(dev); + + printk("Modem suspended\n"); +} + static void modem_event_cb(const struct device *dev, enum cellular_event evt, const void *payload, void *user_data) { + ARG_UNUSED(user_data); + switch (evt) { case CELLULAR_EVENT_MODEM_INFO_CHANGED: #ifdef CONFIG_SAMPLE_CELLULAR_MODEM_AUTO_APN auto_apn_modem_info_cb(dev, payload); #endif break; + case CELLULAR_EVENT_REGISTRATION_STATUS_CHANGED: + modem_registration_changed(dev, payload); + break; + case CELLULAR_EVENT_MODEM_COMMS_CHECK_RESULT: + comms_check_result(dev, payload); + break; + case CELLULAR_EVENT_NETWORK_STATUS_CHANGED: + network_status_changed(dev, payload); + break; + case CELLULAR_EVENT_MODEM_SUSPENDED: + modem_suspended(dev); + break; default: printk("Unhandled event: %d\n", evt); break; @@ -462,12 +508,16 @@ NET_MGMT_REGISTER_EVENT_HANDLER(l4_events, L4_EVENT_MASK, l4_event_handler, NULL int main(void) { + const cellular_event_mask_t all_events = + CELLULAR_EVENT_MODEM_INFO_CHANGED | CELLULAR_EVENT_REGISTRATION_STATUS_CHANGED | + CELLULAR_EVENT_MODEM_COMMS_CHECK_RESULT | CELLULAR_EVENT_NETWORK_STATUS_CHANGED | + CELLULAR_EVENT_MODEM_SUSPENDED; bool valid_dns = false; uint16_t *port; int ret; /* Subscribe before powering the modem so we catch all events */ - ret = cellular_set_callback(modem, CELLULAR_EVENT_MODEM_INFO_CHANGED, modem_event_cb, NULL); + ret = cellular_set_callback(modem, all_events, modem_event_cb, NULL); if (ret < 0) { printk("Failed to subscribe to modem events (%d)\n", ret); } From 5cc0e888c730ae2b2031e7bc3d3d9fa7f077faea Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Fri, 21 Aug 2026 12:20:05 -0500 Subject: [PATCH 261/455] soc: adi: max32: compute NUM_IRQS from devicetree Every MAX32 SoC carried a hardcoded CONFIG_NUM_IRQS sized to the full hardware IRQ line count: 18 values spread over 14 Kconfig files in three different syntactic styles. Each one had to be maintained by hand, was easy to get wrong when a peripheral was added, and forced applications to carry an ISR table sized for peripherals they never enable. Replace them with a single family-level default derived from the devicetree via dt_highest_controller_irq_number(). The helper only considers nodes with status = "okay", so the table is now sized to what the application actually uses. Both cores are covered: Cortex-M4 and Cortex-M33 through the NVIC node, RV32 through the MAX32 RISC-V core interrupt controller. Only one of those controllers is ever present in a given build's devicetree, so the default for the other core is inert. The default is declared with configdefault and placed after the per-SoC rsource statements, so a SoC or a board can still pin an explicit value. Measured with samples/hello_world, the derived values are at or below the previous constants on all 32 buildable MAX32 board targets, for example max78002/m4 105 -> 57, max32657 54 -> 26 and max32655/rv32 64 -> 28. This mirrors the equivalent change made for STM32. Assisted-by: Claude:claude-opus-5 Signed-off-by: Maureen Helm --- soc/adi/max32/Kconfig.defconfig | 20 ++++++++++++++++++++ soc/adi/max32/Kconfig.defconfig.max32650 | 3 --- soc/adi/max32/Kconfig.defconfig.max32651 | 3 --- soc/adi/max32/Kconfig.defconfig.max32655 | 6 ------ soc/adi/max32/Kconfig.defconfig.max32657 | 3 --- soc/adi/max32/Kconfig.defconfig.max32660 | 3 --- soc/adi/max32/Kconfig.defconfig.max32662 | 3 --- soc/adi/max32/Kconfig.defconfig.max32666 | 3 --- soc/adi/max32/Kconfig.defconfig.max32670 | 3 --- soc/adi/max32/Kconfig.defconfig.max32672 | 3 --- soc/adi/max32/Kconfig.defconfig.max32675 | 3 --- soc/adi/max32/Kconfig.defconfig.max32680 | 4 ---- soc/adi/max32/Kconfig.defconfig.max32690 | 6 ------ soc/adi/max32/Kconfig.defconfig.max78000 | 6 ------ soc/adi/max32/Kconfig.defconfig.max78002 | 6 ------ 15 files changed, 20 insertions(+), 55 deletions(-) diff --git a/soc/adi/max32/Kconfig.defconfig b/soc/adi/max32/Kconfig.defconfig index 04a3470844dc..c7ecc449ec3a 100644 --- a/soc/adi/max32/Kconfig.defconfig +++ b/soc/adi/max32/Kconfig.defconfig @@ -5,6 +5,8 @@ if SOC_FAMILY_MAX32 +# Source the per-SoC Kconfig files first, so that individual SoCs can override +# the family-level defaults given below. rsource "Kconfig.defconfig.max*" rsource "sbt/Kconfig.defconfig" @@ -13,6 +15,24 @@ choice PM_PREWAKEUP_CONV_MODE endchoice # PM_PREWAKEUP_CONV_MODE +# Compute the IRQ table size automatically using DT. +# dt_highest_controller_irq_number() returns a zero-based IRQn, increment it to +# obtain the table size (= max IRQn + 1). Both cores are covered here: only one +# of the two interrupt controllers is ever present in a given build's DT, so the +# default for the other core is inert. + +# Cortex-M4 (ARMv7-M) and Cortex-M33 (ARMv8-M) cores: NVIC. +DT_NVIC_PATH := /soc/interrupt-controller@e000e100 +DT_NVIC_MAX_IRQN := $(dt_highest_controller_irq_number,$(DT_NVIC_PATH),irq) + +# RV32 cores: MAX32 RISC-V core interrupt controller. +DT_RV32_INTC_PATH := /soc/intc@e5070000 +DT_RV32_INTC_MAX_IRQN := $(dt_highest_controller_irq_number,$(DT_RV32_INTC_PATH),irq) + +configdefault NUM_IRQS + default $(inc,$(DT_NVIC_MAX_IRQN)) if $(dt_path_enabled,$(DT_NVIC_PATH)) + default $(inc,$(DT_RV32_INTC_MAX_IRQN)) if $(dt_path_enabled,$(DT_RV32_INTC_PATH)) + if SOC_FAMILY_MAX32_RV32 config RISCV_TRAP_HANDLER_ALIGNMENT diff --git a/soc/adi/max32/Kconfig.defconfig.max32650 b/soc/adi/max32/Kconfig.defconfig.max32650 index 9c8608b1b775..fc82b8e7331d 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32650 +++ b/soc/adi/max32/Kconfig.defconfig.max32650 @@ -8,7 +8,4 @@ if SOC_MAX32650 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 80 - endif # SOC_MAX32650 diff --git a/soc/adi/max32/Kconfig.defconfig.max32651 b/soc/adi/max32/Kconfig.defconfig.max32651 index 20680b3e8437..38b37c6a6cf3 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32651 +++ b/soc/adi/max32/Kconfig.defconfig.max32651 @@ -8,9 +8,6 @@ if SOC_MAX32651 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 80 - choice SOC_FAMILY_MAX32_SECURE_SOC_SIGNING_ALGO default SOC_FAMILY_MAX32_SECURE_SOC_SIGNING_ALGO_RSA2048 endchoice diff --git a/soc/adi/max32/Kconfig.defconfig.max32655 b/soc/adi/max32/Kconfig.defconfig.max32655 index 3b3fe974fd47..fb22f170ff63 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32655 +++ b/soc/adi/max32/Kconfig.defconfig.max32655 @@ -10,9 +10,6 @@ if SOC_MAX32655_M4 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 110 - endif if SOC_MAX32655_RV32 @@ -20,9 +17,6 @@ if SOC_MAX32655_RV32 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_iso,clock-frequency) -config NUM_IRQS - default 64 - config ISR_STACK_SIZE default 1024 diff --git a/soc/adi/max32/Kconfig.defconfig.max32657 b/soc/adi/max32/Kconfig.defconfig.max32657 index cd84ea8cb8ba..7faa35eda297 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32657 +++ b/soc/adi/max32/Kconfig.defconfig.max32657 @@ -9,9 +9,6 @@ config SYS_CLOCK_HW_CYCLES_PER_SEC default 32768 if MAX32_WUT_TIMER default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 54 - # if PM_S2RAM selected, HAS_PM_S2RAM_CUSTOM_MARKING must be selected config PM_S2RAM select HAS_PM_S2RAM_CUSTOM_MARKING diff --git a/soc/adi/max32/Kconfig.defconfig.max32660 b/soc/adi/max32/Kconfig.defconfig.max32660 index 29848d44d1dc..20aeb244be54 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32660 +++ b/soc/adi/max32/Kconfig.defconfig.max32660 @@ -8,7 +8,4 @@ if SOC_MAX32660 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 55 - endif # SOC_MAX32660 diff --git a/soc/adi/max32/Kconfig.defconfig.max32662 b/soc/adi/max32/Kconfig.defconfig.max32662 index 997c0f2515ac..f72a99ae4cf0 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32662 +++ b/soc/adi/max32/Kconfig.defconfig.max32662 @@ -8,7 +8,4 @@ if SOC_MAX32662 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 108 - endif # SOC_MAX32662 diff --git a/soc/adi/max32/Kconfig.defconfig.max32666 b/soc/adi/max32/Kconfig.defconfig.max32666 index 25213269dbc8..76810b8274b7 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32666 +++ b/soc/adi/max32/Kconfig.defconfig.max32666 @@ -8,7 +8,4 @@ if SOC_MAX32666 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 95 - endif # SOC_MAX32666 diff --git a/soc/adi/max32/Kconfig.defconfig.max32670 b/soc/adi/max32/Kconfig.defconfig.max32670 index a351d2706862..52c8eb008a81 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32670 +++ b/soc/adi/max32/Kconfig.defconfig.max32670 @@ -8,7 +8,4 @@ if SOC_MAX32670 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 100 - endif # SOC_MAX32670 diff --git a/soc/adi/max32/Kconfig.defconfig.max32672 b/soc/adi/max32/Kconfig.defconfig.max32672 index 34fe3230e601..bae48c2bceb6 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32672 +++ b/soc/adi/max32/Kconfig.defconfig.max32672 @@ -8,9 +8,6 @@ if SOC_MAX32672 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 108 - choice SOC_FAMILY_MAX32_SECURE_SOC_SIGNING_ALGO default SOC_FAMILY_MAX32_SECURE_SOC_SIGNING_ALGO_ECDSA256 endchoice diff --git a/soc/adi/max32/Kconfig.defconfig.max32675 b/soc/adi/max32/Kconfig.defconfig.max32675 index 3718cef95493..a6319ab22132 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32675 +++ b/soc/adi/max32/Kconfig.defconfig.max32675 @@ -8,7 +8,4 @@ if SOC_MAX32675 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 100 - endif # SOC_MAX32675 diff --git a/soc/adi/max32/Kconfig.defconfig.max32680 b/soc/adi/max32/Kconfig.defconfig.max32680 index 4a996967c8f4..9e41af35562e 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32680 +++ b/soc/adi/max32/Kconfig.defconfig.max32680 @@ -8,8 +8,4 @@ if SOC_MAX32680 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 104 if SOC_MAX32680_M4 - default 60 if SOC_MAX32680_RV32 - endif # SOC_MAX32680 diff --git a/soc/adi/max32/Kconfig.defconfig.max32690 b/soc/adi/max32/Kconfig.defconfig.max32690 index 849eacafcc97..6a1c01f6b67a 100644 --- a/soc/adi/max32/Kconfig.defconfig.max32690 +++ b/soc/adi/max32/Kconfig.defconfig.max32690 @@ -10,9 +10,6 @@ if SOC_MAX32690_M4 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 112 - endif if SOC_MAX32690_RV32 @@ -20,9 +17,6 @@ if SOC_MAX32690_RV32 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_iso,clock-frequency) -config NUM_IRQS - default 64 - endif endif # SOC_MAX32690 diff --git a/soc/adi/max32/Kconfig.defconfig.max78000 b/soc/adi/max32/Kconfig.defconfig.max78000 index 1a0e71cbda16..465745f37a78 100644 --- a/soc/adi/max32/Kconfig.defconfig.max78000 +++ b/soc/adi/max32/Kconfig.defconfig.max78000 @@ -10,9 +10,6 @@ if SOC_MAX78000_M4 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 119 - endif # SOC_MAX78000_M4 if SOC_MAX78000_RV32 @@ -20,9 +17,6 @@ if SOC_MAX78000_RV32 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_iso,clock-frequency) -config NUM_IRQS - default 60 - endif # SOC_MAX78000_RV32 diff --git a/soc/adi/max32/Kconfig.defconfig.max78002 b/soc/adi/max32/Kconfig.defconfig.max78002 index 1530df2ad5ba..000e265516d7 100644 --- a/soc/adi/max32/Kconfig.defconfig.max78002 +++ b/soc/adi/max32/Kconfig.defconfig.max78002 @@ -10,9 +10,6 @@ if SOC_MAX78002_M4 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_ipo,clock-frequency) -config NUM_IRQS - default 105 - endif # SOC_MAX78002_M4 if SOC_MAX78002_RV32 @@ -20,9 +17,6 @@ if SOC_MAX78002_RV32 config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_node_int_prop_int,/clocks/clk_iso,clock-frequency) -config NUM_IRQS - default 60 - endif # SOC_MAX78002_RV32 endif # SOC_MAX78002 From 53e6d7138ee77c653dc29d23b7eccc3fb7bed543 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Fri, 21 Aug 2026 12:20:12 -0500 Subject: [PATCH 262/455] doc: migration: 4.5: add section about CONFIG_NUM_IRQS on MAX32 CONFIG_NUM_IRQS is no longer a hardcoded per-SoC value on MAX32, so applications that register an ISR for an IRQ line with no corresponding enabled devicetree node may now fail to build, or index past the ISR table when installing a handler dynamically. Document the failure mode and the fix. Assisted-by: Claude:claude-opus-5 Signed-off-by: Maureen Helm --- doc/releases/migration-guide-4.5.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index ac8ccdec4d43..d901b52106d4 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -269,6 +269,26 @@ ADC condition. In-tree boards no longer enable it explicitly in their defconfigs since the default already covers them. +Analog Devices +============== + +* :kconfig:option:`CONFIG_NUM_IRQS` is now computed automatically for all MAX32 SoCs from the + devicetree, based on active (``status = "okay";``) devices, using the + ``dt_highest_controller_irq_number`` Kconfig preprocessor function. The hardcoded per-SoC values + have been removed, and the resulting IRQ table is typically considerably smaller than before. + Applications which register custom ISRs (using :c:macro:`IRQ_CONNECT()`) may encounter build + failures such as the following due to :kconfig:option:`CONFIG_NUM_IRQS` having a lower value: + + .. code-block:: + + gen_isr_tables.py: error: IRQ 88 (offset=0) exceeds the maximum of 54 + + Explicitly set :kconfig:option:`CONFIG_NUM_IRQS` to an appropriate value to solve these issues. + (:ref:`The following documentation page ` explains how to do it) + + Applications that install ISRs at runtime with :c:func:`irq_connect_dynamic` are not covered by + this build-time check and must be reviewed manually. + Audio Codec =========== From 3ab898ddcfe2d00af3376a53fe3dc374c7f2e990 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 23 Aug 2026 11:20:35 +1000 Subject: [PATCH 263/455] boards: nordic: nrf93m1dk: add missing `supported` tags Add missing tags from the board for twister. `netif:modem` matches the nRF91 DK boards, and the nRF54L15 SoC supports Bluetooth. Signed-off-by: Jordan Yates --- boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp.yaml | 5 ++++- boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp_ns.yaml | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp.yaml b/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp.yaml index ec7115e8a686..4f04dffc916c 100644 --- a/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp.yaml +++ b/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp.yaml @@ -8,7 +8,6 @@ arch: arm toolchain: - gnuarmemb - zephyr -sysbuild: true ram: 256 flash: 712 supported: @@ -22,3 +21,7 @@ supported: - spi - watchdog - i2s + - netif:modem + - ble +vendor: nordic +sysbuild: true diff --git a/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp_ns.yaml b/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp_ns.yaml index e0e7008b8468..0a300c260bef 100644 --- a/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp_ns.yaml +++ b/boards/nordic/nrf93m1dk/nrf93m1dk_nrf54l15_cpuapp_ns.yaml @@ -19,5 +19,7 @@ supported: - watchdog - adc - i2s + - netif:modem + - ble vendor: nordic sysbuild: true From fb999ffaaec5b4bd58824e6420f626710417d179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 14 Aug 2026 09:39:47 +0000 Subject: [PATCH 264/455] drivers: i2c: numaker: use inclusive terminology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update comments, log messages, Kconfig prose, and driver-local identifiers to use the controller/target terminology ratified by coding guideline A.2 and already used by the Zephyr I2C API. Identifiers mirroring the Nuvoton NuMaker BSP (e.g. I2C_SetSlaveAddr, M_* status macros, S_* status macros) are kept unchanged. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/i2c/i2c_numaker.c | 342 +++++++++++++++++++------------------- 1 file changed, 173 insertions(+), 169 deletions(-) diff --git a/drivers/i2c/i2c_numaker.c b/drivers/i2c/i2c_numaker.c index af62b8dec85b..7901cc789d79 100644 --- a/drivers/i2c/i2c_numaker.c +++ b/drivers/i2c/i2c_numaker.c @@ -19,30 +19,30 @@ LOG_MODULE_REGISTER(i2c_numaker, CONFIG_I2C_LOG_LEVEL); #include #include -/* i2c Master Mode Status */ +/* i2c Controller Mode Status */ #define M_START 0x08 /* Start */ -#define M_REPEAT_START 0x10 /* Master Repeat Start */ -#define M_TRAN_ADDR_ACK 0x18 /* Master Transmit Address ACK */ -#define M_TRAN_ADDR_NACK 0x20 /* Master Transmit Address NACK */ -#define M_TRAN_DATA_ACK 0x28 /* Master Transmit Data ACK */ -#define M_TRAN_DATA_NACK 0x30 /* Master Transmit Data NACK */ -#define M_ARB_LOST 0x38 /* Master Arbitration Los */ -#define M_RECE_ADDR_ACK 0x40 /* Master Receive Address ACK */ -#define M_RECE_ADDR_NACK 0x48 /* Master Receive Address NACK */ -#define M_RECE_DATA_ACK 0x50 /* Master Receive Data ACK */ -#define M_RECE_DATA_NACK 0x58 /* Master Receive Data NACK */ +#define M_REPEAT_START 0x10 /* Controller Repeat Start */ +#define M_TRAN_ADDR_ACK 0x18 /* Controller Transmit Address ACK */ +#define M_TRAN_ADDR_NACK 0x20 /* Controller Transmit Address NACK */ +#define M_TRAN_DATA_ACK 0x28 /* Controller Transmit Data ACK */ +#define M_TRAN_DATA_NACK 0x30 /* Controller Transmit Data NACK */ +#define M_ARB_LOST 0x38 /* Controller Arbitration Los */ +#define M_RECE_ADDR_ACK 0x40 /* Controller Receive Address ACK */ +#define M_RECE_ADDR_NACK 0x48 /* Controller Receive Address NACK */ +#define M_RECE_DATA_ACK 0x50 /* Controller Receive Data ACK */ +#define M_RECE_DATA_NACK 0x58 /* Controller Receive Data NACK */ #define BUS_ERROR 0x00 /* Bus error */ -/* i2c Slave Mode Status */ -#define S_REPEAT_START_STOP 0xA0 /* Slave Transmit Repeat Start or Stop */ -#define S_TRAN_ADDR_ACK 0xA8 /* Slave Transmit Address ACK */ -#define S_TRAN_DATA_ACK 0xB8 /* Slave Transmit Data ACK */ -#define S_TRAN_DATA_NACK 0xC0 /* Slave Transmit Data NACK */ -#define S_TRAN_LAST_DATA_ACK 0xC8 /* Slave Transmit Last Data ACK */ -#define S_RECE_ADDR_ACK 0x60 /* Slave Receive Address ACK */ -#define S_RECE_ARB_LOST 0x68 /* Slave Receive Arbitration Lost */ -#define S_RECE_DATA_ACK 0x80 /* Slave Receive Data ACK */ -#define S_RECE_DATA_NACK 0x88 /* Slave Receive Data NACK */ +/* i2c Target Mode Status */ +#define S_REPEAT_START_STOP 0xA0 /* Target Transmit Repeat Start or Stop */ +#define S_TRAN_ADDR_ACK 0xA8 /* Target Transmit Address ACK */ +#define S_TRAN_DATA_ACK 0xB8 /* Target Transmit Data ACK */ +#define S_TRAN_DATA_NACK 0xC0 /* Target Transmit Data NACK */ +#define S_TRAN_LAST_DATA_ACK 0xC8 /* Target Transmit Last Data ACK */ +#define S_RECE_ADDR_ACK 0x60 /* Target Receive Address ACK */ +#define S_RECE_ARB_LOST 0x68 /* Target Receive Arbitration Lost */ +#define S_RECE_DATA_ACK 0x80 /* Target Receive Data ACK */ +#define S_RECE_DATA_NACK 0x88 /* Target Receive Data NACK */ /* i2c GC Mode Status */ #define GC_ADDR_ACK 0x70 /* GC mode Address ACK */ @@ -70,7 +70,7 @@ struct i2c_numaker_config { struct i2c_numaker_data { struct k_sem lock; uint32_t dev_config; - /* Master transfer context */ + /* Controller transfer context */ struct { struct k_sem xfer_sync; uint16_t addr; @@ -80,41 +80,41 @@ struct i2c_numaker_data { uint8_t *buf_beg; uint8_t *buf_pos; uint8_t *buf_end; - } master_xfer; + } controller_xfer; #ifdef CONFIG_I2C_TARGET - /* Slave transfer context */ + /* Target transfer context */ struct { - struct i2c_target_config *slave_config; - bool slave_addressed; - } slave_xfer; + struct i2c_target_config *target_config; + bool target_addressed; + } target_xfer; #endif }; /* ACK/NACK last data byte, dependent on whether or not message merge is allowed */ -static void m_numaker_i2c_master_xfer_msg_read_last_byte(const struct device *dev) +static void m_numaker_i2c_controller_xfer_msg_read_last_byte(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; /* Shouldn't invoke with message pointer OOB */ - __ASSERT_NO_MSG(data->master_xfer.msgs_pos < data->master_xfer.msgs_end); + __ASSERT_NO_MSG(data->controller_xfer.msgs_pos < data->controller_xfer.msgs_end); /* Should invoke with exactly one data byte remaining for read */ - __ASSERT_NO_MSG((data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ); - __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 1); + __ASSERT_NO_MSG((data->controller_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ); + __ASSERT_NO_MSG((data->controller_xfer.buf_end - data->controller_xfer.buf_pos) == 1); /* Flags of previous message */ - bool do_stop_prev = data->master_xfer.msgs_pos->flags & I2C_MSG_STOP; + bool do_stop_prev = data->controller_xfer.msgs_pos->flags & I2C_MSG_STOP; /* Advance to next messages temporarily */ - data->master_xfer.msgs_pos++; + data->controller_xfer.msgs_pos++; /* Has next message? */ - if (data->master_xfer.msgs_pos < data->master_xfer.msgs_end) { + if (data->controller_xfer.msgs_pos < data->controller_xfer.msgs_end) { /* Flags of next message */ - struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos; + struct i2c_msg *msgs_pos = data->controller_xfer.msgs_pos; bool is_read_next = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; - bool do_restart_next = data->master_xfer.msgs_pos->flags & I2C_MSG_RESTART; + bool do_restart_next = data->controller_xfer.msgs_pos->flags & I2C_MSG_RESTART; /* * Different R/W bit so message merge is disallowed. @@ -127,23 +127,23 @@ static void m_numaker_i2c_master_xfer_msg_read_last_byte(const struct device *de } if (do_stop_prev || do_restart_next) { - /* NACK last data byte (required for Master Receiver) */ + /* NACK last data byte (required for Controller Receiver) */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } else { /* ACK last data byte, so to merge adjacent messages into one transaction */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } } else { - /* NACK last data byte (required for Master Receiver) */ + /* NACK last data byte (required for Controller Receiver) */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } /* Roll back message pointer */ - data->master_xfer.msgs_pos--; + data->controller_xfer.msgs_pos--; } /* End the transfer, involving I2C Stop and signal to thread */ -static void m_numaker_i2c_master_xfer_end(const struct device *dev, bool do_stop) +static void m_numaker_i2c_controller_xfer_end(const struct device *dev, bool do_stop) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; @@ -154,26 +154,26 @@ static void m_numaker_i2c_master_xfer_end(const struct device *dev, bool do_stop I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk); } - /* Signal master transfer end */ - k_sem_give(&data->master_xfer.xfer_sync); + /* Signal controller transfer end */ + k_sem_give(&data->controller_xfer.xfer_sync); } -static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev); +static void m_numaker_i2c_controller_xfer_msg_end(const struct device *dev); /* Read next data byte, involving ACK/NACK last data byte and message merge */ -static void m_numaker_i2c_master_xfer_msg_read_next_byte(const struct device *dev) +static void m_numaker_i2c_controller_xfer_msg_read_next_byte(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; - switch (data->master_xfer.buf_end - data->master_xfer.buf_pos) { + switch (data->controller_xfer.buf_end - data->controller_xfer.buf_pos) { case 0: /* Last data byte ACKed, we'll do message merge */ - m_numaker_i2c_master_xfer_msg_end(dev); + m_numaker_i2c_controller_xfer_msg_end(dev); break; case 1: /* Read last data byte for this message */ - m_numaker_i2c_master_xfer_msg_read_last_byte(dev); + m_numaker_i2c_controller_xfer_msg_read_last_byte(dev); break; default: /* ACK non-last data byte */ @@ -182,30 +182,31 @@ static void m_numaker_i2c_master_xfer_msg_read_next_byte(const struct device *de } /* End one message transfer, involving message merge and transfer end */ -static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev) +static void m_numaker_i2c_controller_xfer_msg_end(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; /* Shouldn't invoke with message pointer OOB */ - __ASSERT_NO_MSG(data->master_xfer.msgs_pos < data->master_xfer.msgs_end); + __ASSERT_NO_MSG(data->controller_xfer.msgs_pos < data->controller_xfer.msgs_end); /* Should have transferred up */ - __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 0); + __ASSERT_NO_MSG((data->controller_xfer.buf_end - data->controller_xfer.buf_pos) == 0); /* Flags of previous message */ - bool is_read_prev = (data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; - bool do_stop_prev = data->master_xfer.msgs_pos->flags & I2C_MSG_STOP; + bool is_read_prev = + (data->controller_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; + bool do_stop_prev = data->controller_xfer.msgs_pos->flags & I2C_MSG_STOP; /* Advance to next messages */ - data->master_xfer.msgs_pos++; + data->controller_xfer.msgs_pos++; /* Has next message? */ - if (data->master_xfer.msgs_pos < data->master_xfer.msgs_end) { + if (data->controller_xfer.msgs_pos < data->controller_xfer.msgs_end) { /* Flags of next message */ - struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos; + struct i2c_msg *msgs_pos = data->controller_xfer.msgs_pos; bool is_read_next = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; - bool do_restart_next = data->master_xfer.msgs_pos->flags & I2C_MSG_RESTART; + bool do_restart_next = data->controller_xfer.msgs_pos->flags & I2C_MSG_RESTART; /* * Different R/W bit so message merge is disallowed. @@ -229,13 +230,13 @@ static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev) /* Merge into the same transaction */ /* Prepare buffer for current message */ - data->master_xfer.buf_beg = data->master_xfer.msgs_pos->buf; - data->master_xfer.buf_pos = data->master_xfer.msgs_pos->buf; - data->master_xfer.buf_end = data->master_xfer.msgs_pos->buf + - data->master_xfer.msgs_pos->len; + data->controller_xfer.buf_beg = data->controller_xfer.msgs_pos->buf; + data->controller_xfer.buf_pos = data->controller_xfer.msgs_pos->buf; + data->controller_xfer.buf_end = data->controller_xfer.msgs_pos->buf + + data->controller_xfer.msgs_pos->len; if (is_read_prev) { - m_numaker_i2c_master_xfer_msg_read_next_byte(dev); + m_numaker_i2c_controller_xfer_msg_read_next_byte(dev); } else { /* * Interrupt flag not cleared, expect to re-enter ISR with @@ -248,7 +249,7 @@ static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev) LOG_WRN("Last message not marked I2C Stop"); } - m_numaker_i2c_master_xfer_end(dev, do_stop_prev); + m_numaker_i2c_controller_xfer_end(dev, do_stop_prev); } } @@ -286,8 +287,8 @@ static int i2c_numaker_configure(const struct device *dev, uint32_t dev_config) irq_disable(config->irq_n); #ifdef CONFIG_I2C_TARGET - if (data->slave_xfer.slave_addressed) { - LOG_ERR("Reconfigure with slave being busy"); + if (data->target_xfer.target_addressed) { + LOG_ERR("Reconfigure with target being busy"); err = -EBUSY; goto done; } @@ -323,11 +324,11 @@ static int i2c_numaker_get_config(const struct device *dev, uint32_t *dev_config } /* - * Master active transfer: + * Controller active transfer: * 1. Do I2C Start to start the transfer (thread) * 2. I2C FSM (ISR) * 3. Force I2C Stop to end the transfer (thread) - * Slave passive transfer: + * Target passive transfer: * 1. Prepare callback (thread) * 2. Do data transfer via above callback (ISR) */ @@ -343,40 +344,41 @@ static int i2c_numaker_transfer(const struct device *dev, struct i2c_msg *msgs, irq_disable(config->irq_n); #ifdef CONFIG_I2C_TARGET - if (data->slave_xfer.slave_addressed) { - LOG_ERR("Master transfer with slave being busy"); + if (data->target_xfer.target_addressed) { + LOG_ERR("Controller transfer with target being busy"); err = -EBUSY; goto cleanup; } #endif /* Prepare to start transfer */ - data->master_xfer.addr = addr; - data->master_xfer.msgs_beg = msgs; - data->master_xfer.msgs_pos = msgs; - data->master_xfer.msgs_end = msgs + num_msgs; + data->controller_xfer.addr = addr; + data->controller_xfer.msgs_beg = msgs; + data->controller_xfer.msgs_pos = msgs; + data->controller_xfer.msgs_end = msgs + num_msgs; /* Do I2C Start to start the transfer */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk | I2C_CTL0_SI_Msk); irq_enable(config->irq_n); - k_sem_take(&data->master_xfer.xfer_sync, K_FOREVER); + k_sem_take(&data->controller_xfer.xfer_sync, K_FOREVER); irq_disable(config->irq_n); /* Check transfer result */ - if (data->master_xfer.msgs_pos != data->master_xfer.msgs_end) { + if (data->controller_xfer.msgs_pos != data->controller_xfer.msgs_end) { bool is_read; bool is_10bit; - is_read = (data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; - is_10bit = data->master_xfer.msgs_pos->flags & I2C_MSG_ADDR_10_BITS; + is_read = (data->controller_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; + is_10bit = data->controller_xfer.msgs_pos->flags & I2C_MSG_ADDR_10_BITS; LOG_ERR("Failed message:"); - LOG_ERR("MSG IDX: %d", data->master_xfer.msgs_pos - data->master_xfer.msgs_beg); + LOG_ERR("MSG IDX: %d", + data->controller_xfer.msgs_pos - data->controller_xfer.msgs_beg); LOG_ERR("ADDR (%d-bit): 0x%04X", is_10bit ? 10 : 7, addr); LOG_ERR("DIR: %s", is_read ? "R" : "W"); LOG_ERR("Expected %d bytes transferred, but actual %d", - data->master_xfer.msgs_pos->len, - data->master_xfer.buf_pos - data->master_xfer.buf_beg); + data->controller_xfer.msgs_pos->len, + data->controller_xfer.buf_pos - data->controller_xfer.buf_beg); err = -EIO; goto i2c_stop; } @@ -387,8 +389,8 @@ static int i2c_numaker_transfer(const struct device *dev, struct i2c_msg *msgs, I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk); #ifdef CONFIG_I2C_TARGET - /* Enable slave mode if one slave is registered */ - if (data->slave_xfer.slave_config) { + /* Enable target mode if one target is registered */ + if (data->target_xfer.target_config) { I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } @@ -402,14 +404,14 @@ static int i2c_numaker_transfer(const struct device *dev, struct i2c_msg *msgs, } #ifdef CONFIG_I2C_TARGET -static int i2c_numaker_slave_register(const struct device *dev, - struct i2c_target_config *slave_config) +static int i2c_numaker_target_register(const struct device *dev, + struct i2c_target_config *target_config) { - if (!slave_config || !slave_config->callbacks) { + if (!target_config || !target_config->callbacks) { return -EINVAL; } - if (slave_config->flags & I2C_ADDR_10_BITS) { + if (target_config->flags & I2C_ADDR_10_BITS) { LOG_ERR("10-bits address not supported"); return -ENOTSUP; } @@ -422,19 +424,19 @@ static int i2c_numaker_slave_register(const struct device *dev, k_sem_take(&data->lock, K_FOREVER); irq_disable(config->irq_n); - if (data->slave_xfer.slave_config) { + if (data->target_xfer.target_config) { err = -EBUSY; goto cleanup; } - data->slave_xfer.slave_config = slave_config; - /* Slave address */ - I2C_SetSlaveAddr(i2c_base, 0, slave_config->address, I2C_GCMODE_DISABLE); + data->target_xfer.target_config = target_config; + /* Target address */ + I2C_SetSlaveAddr(i2c_base, 0, target_config->address, I2C_GCMODE_DISABLE); - /* Slave address state */ - data->slave_xfer.slave_addressed = false; + /* Target address state */ + data->target_xfer.target_addressed = false; - /* Enable slave mode */ + /* Enable target mode */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); cleanup: @@ -445,41 +447,41 @@ static int i2c_numaker_slave_register(const struct device *dev, return err; } -static int i2c_numaker_slave_unregister(const struct device *dev, - struct i2c_target_config *slave_config) +static int i2c_numaker_target_unregister(const struct device *dev, + struct i2c_target_config *target_config) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; int err = 0; - if (!slave_config) { + if (!target_config) { return -EINVAL; } k_sem_take(&data->lock, K_FOREVER); irq_disable(config->irq_n); - if (data->slave_xfer.slave_config != slave_config) { + if (data->target_xfer.target_config != target_config) { err = -EINVAL; goto cleanup; } - if (data->slave_xfer.slave_addressed) { - LOG_ERR("Unregister slave driver with slave being busy"); + if (data->target_xfer.target_addressed) { + LOG_ERR("Unregister target driver with target being busy"); err = -EBUSY; goto cleanup; } - /* Slave address: Zero */ + /* Target address: Zero */ I2C_SetSlaveAddr(i2c_base, 0, 0, I2C_GCMODE_DISABLE); - /* Slave address state */ - data->slave_xfer.slave_addressed = false; + /* Target address state */ + data->target_xfer.target_addressed = false; - /* Disable slave mode */ + /* Disable target mode */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); - data->slave_xfer.slave_config = NULL; + data->target_xfer.target_config = NULL; cleanup: @@ -510,9 +512,9 @@ static void i2c_numaker_isr(const struct device *dev) struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; #ifdef CONFIG_I2C_TARGET - struct i2c_target_config *slave_config = data->slave_xfer.slave_config; - const struct i2c_target_callbacks *slave_callbacks = slave_config ? slave_config->callbacks - : NULL; + struct i2c_target_config *target_config = data->target_xfer.target_config; + const struct i2c_target_callbacks *target_callbacks = + target_config ? target_config->callbacks : NULL; uint8_t data_byte; #endif uint32_t status; @@ -526,71 +528,73 @@ static void i2c_numaker_isr(const struct device *dev) switch (status) { case M_START: /* Start */ - case M_REPEAT_START: /* Master Repeat Start */ + case M_REPEAT_START: /* Controller Repeat Start */ /* Prepare buffer for current message */ - data->master_xfer.buf_beg = data->master_xfer.msgs_pos->buf; - data->master_xfer.buf_pos = data->master_xfer.msgs_pos->buf; - data->master_xfer.buf_end = data->master_xfer.msgs_pos->buf + - data->master_xfer.msgs_pos->len; + data->controller_xfer.buf_beg = data->controller_xfer.msgs_pos->buf; + data->controller_xfer.buf_pos = data->controller_xfer.msgs_pos->buf; + data->controller_xfer.buf_end = data->controller_xfer.msgs_pos->buf + + data->controller_xfer.msgs_pos->len; /* Write I2C address */ - struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos; + struct i2c_msg *msgs_pos = data->controller_xfer.msgs_pos; bool is_read = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; - uint16_t addr = data->master_xfer.addr; + uint16_t addr = data->controller_xfer.addr; int addr_rw = is_read ? ((addr << 1) | 1) : (addr << 1); I2C_SET_DATA(i2c_base, (uint8_t)(addr_rw & 0xFF)); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); break; - case M_TRAN_ADDR_ACK: /* Master Transmit Address ACK */ - case M_TRAN_DATA_ACK: /* Master Transmit Data ACK */ - __ASSERT_NO_MSG(data->master_xfer.buf_pos); - if (data->master_xfer.buf_pos < data->master_xfer.buf_end) { - I2C_SET_DATA(i2c_base, *data->master_xfer.buf_pos++); + case M_TRAN_ADDR_ACK: /* Controller Transmit Address ACK */ + case M_TRAN_DATA_ACK: /* Controller Transmit Data ACK */ + __ASSERT_NO_MSG(data->controller_xfer.buf_pos); + if (data->controller_xfer.buf_pos < data->controller_xfer.buf_end) { + I2C_SET_DATA(i2c_base, *data->controller_xfer.buf_pos++); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { /* End this message */ - m_numaker_i2c_master_xfer_msg_end(dev); + m_numaker_i2c_controller_xfer_msg_end(dev); } break; - case M_TRAN_ADDR_NACK: /* Master Transmit Address NACK */ - case M_TRAN_DATA_NACK: /* Master Transmit Data NACK */ - case M_RECE_ADDR_NACK: /* Master Receive Address NACK */ - case M_ARB_LOST: /* Master Arbitration Lost */ - m_numaker_i2c_master_xfer_end(dev, true); + case M_TRAN_ADDR_NACK: /* Controller Transmit Address NACK */ + case M_TRAN_DATA_NACK: /* Controller Transmit Data NACK */ + case M_RECE_ADDR_NACK: /* Controller Receive Address NACK */ + case M_ARB_LOST: /* Controller Arbitration Lost */ + m_numaker_i2c_controller_xfer_end(dev, true); break; - case M_RECE_ADDR_ACK: /* Master Receive Address ACK */ - case M_RECE_DATA_ACK: /* Master Receive Data ACK */ - __ASSERT_NO_MSG(data->master_xfer.buf_pos); + case M_RECE_ADDR_ACK: /* Controller Receive Address ACK */ + case M_RECE_DATA_ACK: /* Controller Receive Data ACK */ + __ASSERT_NO_MSG(data->controller_xfer.buf_pos); if (status == M_RECE_ADDR_ACK) { - __ASSERT_NO_MSG(data->master_xfer.buf_pos < data->master_xfer.buf_end); + __ASSERT_NO_MSG(data->controller_xfer.buf_pos < + data->controller_xfer.buf_end); } else if (status == M_RECE_DATA_ACK) { - __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) >= - 1); - *data->master_xfer.buf_pos++ = I2C_GET_DATA(i2c_base); + __ASSERT_NO_MSG((data->controller_xfer.buf_end - + data->controller_xfer.buf_pos) >= 1); + *data->controller_xfer.buf_pos++ = I2C_GET_DATA(i2c_base); } - m_numaker_i2c_master_xfer_msg_read_next_byte(dev); + m_numaker_i2c_controller_xfer_msg_read_next_byte(dev); break; - case M_RECE_DATA_NACK: /* Master Receive Data NACK */ - __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 1); - *data->master_xfer.buf_pos++ = I2C_GET_DATA(i2c_base); + case M_RECE_DATA_NACK: /* Controller Receive Data NACK */ + __ASSERT_NO_MSG((data->controller_xfer.buf_end - data->controller_xfer.buf_pos) == + 1); + *data->controller_xfer.buf_pos++ = I2C_GET_DATA(i2c_base); /* End this message */ - m_numaker_i2c_master_xfer_msg_end(dev); + m_numaker_i2c_controller_xfer_msg_end(dev); break; case BUS_ERROR: /* Bus error */ - m_numaker_i2c_master_xfer_end(dev, true); + m_numaker_i2c_controller_xfer_end(dev, true); break; #ifdef CONFIG_I2C_TARGET - /* NOTE: Don't disable interrupt here because slave mode relies on */ + /* NOTE: Don't disable interrupt here because target mode relies on */ /* for passive transfer in ISR. */ - /* Slave Transmit */ - case S_TRAN_ADDR_ACK: /* Slave Transmit Address ACK */ - case ADDR_TRAN_ARB_LOST: /* Slave Transmit Arbitration Lost */ - data->slave_xfer.slave_addressed = true; - if (slave_callbacks->read_requested(slave_config, &data_byte) == 0) { + /* Target Transmit */ + case S_TRAN_ADDR_ACK: /* Target Transmit Address ACK */ + case ADDR_TRAN_ARB_LOST: /* Target Transmit Arbitration Lost */ + data->target_xfer.target_addressed = true; + if (target_callbacks->read_requested(target_config, &data_byte) == 0) { /* Non-last data byte */ I2C_SET_DATA(i2c_base, data_byte); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); @@ -600,8 +604,8 @@ static void i2c_numaker_isr(const struct device *dev) I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; - case S_TRAN_DATA_ACK: /* Slave Transmit Data ACK */ - if (slave_callbacks->read_processed(slave_config, &data_byte) == 0) { + case S_TRAN_DATA_ACK: /* Target Transmit Data ACK */ + if (target_callbacks->read_processed(target_config, &data_byte) == 0) { /* Non-last data byte */ I2C_SET_DATA(i2c_base, data_byte); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); @@ -611,17 +615,17 @@ static void i2c_numaker_isr(const struct device *dev) I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; - case S_TRAN_DATA_NACK: /* Slave Transmit Data NACK */ - case S_TRAN_LAST_DATA_ACK: /* Slave Transmit Last Data ACK */ - /* Go slave end */ - data->slave_xfer.slave_addressed = false; - slave_callbacks->stop(slave_config); + case S_TRAN_DATA_NACK: /* Target Transmit Data NACK */ + case S_TRAN_LAST_DATA_ACK: /* Target Transmit Last Data ACK */ + /* Go target end */ + data->target_xfer.target_addressed = false; + target_callbacks->stop(target_config); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); break; - /* Slave Receive */ - case S_RECE_DATA_ACK: /* Slave Receive Data ACK */ + /* Target Receive */ + case S_RECE_DATA_ACK: /* Target Receive Data ACK */ data_byte = I2C_GET_DATA(i2c_base); - if (slave_callbacks->write_received(slave_config, data_byte) == 0) { + if (target_callbacks->write_received(target_config, data_byte) == 0) { /* Write OK, ACK next data byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { @@ -629,16 +633,16 @@ static void i2c_numaker_isr(const struct device *dev) I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; - case S_RECE_DATA_NACK: /* Slave Receive Data NACK */ - /* Go slave end */ - data->slave_xfer.slave_addressed = false; - slave_callbacks->stop(slave_config); + case S_RECE_DATA_NACK: /* Target Receive Data NACK */ + /* Go target end */ + data->target_xfer.target_addressed = false; + target_callbacks->stop(target_config); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); break; - case S_RECE_ADDR_ACK: /* Slave Receive Address ACK */ - case S_RECE_ARB_LOST: /* Slave Receive Arbitration Lost */ - data->slave_xfer.slave_addressed = true; - if (slave_callbacks->write_requested(slave_config) == 0) { + case S_RECE_ADDR_ACK: /* Target Receive Address ACK */ + case S_RECE_ARB_LOST: /* Target Receive Arbitration Lost */ + data->target_xfer.target_addressed = true; + if (target_callbacks->write_requested(target_config) == 0) { /* Write ready, ACK next byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { @@ -646,10 +650,10 @@ static void i2c_numaker_isr(const struct device *dev) I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; - case S_REPEAT_START_STOP: /* Slave Transmit/Receive Repeat Start or Stop */ - /* Go slave end */ - data->slave_xfer.slave_addressed = false; - slave_callbacks->stop(slave_config); + case S_REPEAT_START_STOP: /* Target Transmit/Receive Repeat Start or Stop */ + /* Go target end */ + data->target_xfer.target_addressed = false; + target_callbacks->stop(target_config); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); break; #endif /* CONFIG_I2C_TARGET */ @@ -659,7 +663,7 @@ static void i2c_numaker_isr(const struct device *dev) break; default: __ASSERT(false, "Uncaught I2C FSM state"); - m_numaker_i2c_master_xfer_end(dev, true); + m_numaker_i2c_controller_xfer_end(dev, true); } } @@ -680,7 +684,7 @@ static int i2c_numaker_init(const struct device *dev) memset(data, 0x00, sizeof(*data)); k_sem_init(&data->lock, 1, 1); - k_sem_init(&data->master_xfer.xfer_sync, 0, 1); + k_sem_init(&data->controller_xfer.xfer_sync, 0, 1); SYS_UnlockReg(); @@ -729,8 +733,8 @@ static DEVICE_API(i2c, i2c_numaker_driver_api) = { .get_config = i2c_numaker_get_config, .transfer = i2c_numaker_transfer, #ifdef CONFIG_I2C_TARGET - .target_register = i2c_numaker_slave_register, - .target_unregister = i2c_numaker_slave_unregister, + .target_register = i2c_numaker_target_register, + .target_unregister = i2c_numaker_target_unregister, #endif #ifdef CONFIG_I2C_RTIO .iodev_submit = i2c_iodev_submit_fallback, From cb755c828ac36f6db7364f2d163221fdc4c41184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 13 Jul 2026 13:59:18 +0200 Subject: [PATCH 265/455] doc: css: fix rendering of code-identifier section titles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Section titles wrapped in inline code (e.g. ``zephyr_*``) rendered as a small code chip in both the body and the navigation sidebar, making them look like a label rather than a heading and hiding the heading level. Let the code inherit the surrounding size, weight and color so the hierarchy and nav entries stay legible while keeping the monospace font. Also scope the link-colored inline-code rules to .rst-content so they no longer tint the sidebar entries, which are section titles rather than cross-references. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Benjamin Cabé --- doc/_static/css/custom.css | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index 8c68e3e1accd..218a2183db44 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -279,12 +279,14 @@ code, border-bottom: 1px solid var(--code-background-color) !important; } -/* Code literals */ -a.internal code.literal { +/* Code literals that are links in body content take the link color. Scoped to .rst-content so the + navigation sidebar (where code identifiers are section titles, not cross-references) is unaffected + and keeps the regular nav text color. */ +.rst-content a.internal code.literal { color: var(--link-color); } -a.internal:visited code.literal { +.rst-content a.internal:visited code.literal { color: var(--link-color-visited); } @@ -559,6 +561,24 @@ kbd, .kbd, font-weight: 100; } +/* Section titles that are code identifiers would otherwise render as a small inline-code chip, both + in the body and the sidebar. Let the code inherit the surrounding size, weight and color so the + heading hierarchy and nav entries stay legible. */ +.rst-content section > h1 code, +.rst-content section > h2 code, +.rst-content section > h3 code, +.rst-content section > h4 code, +.rst-content section > h5 code, +.rst-content section > h6 code, +.wy-menu-vertical a code.literal { + font-size: inherit; + font-weight: inherit; + background: transparent; + border: 0; + padding: 0; + color: inherit; +} + /* Definition lists */ .rst-content dl { border-left: 4px solid var(--admonition-note-background-color); From 04f80170474456fb7d8b379ff9a0819aa1a20e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 13 Jul 2026 13:32:51 +0200 Subject: [PATCH 266/455] doc: css: change color of definition list side bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use gray to not confuse it with the heading border color Signed-off-by: Benjamin Cabé --- doc/_static/css/custom.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index 218a2183db44..a5182c6ac08e 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -581,7 +581,9 @@ kbd, .kbd, /* Definition lists */ .rst-content dl { - border-left: 4px solid var(--admonition-note-background-color); + /* Neutral gray, distinct from the accent color the headings use for their left border, so a + definition list is not mistaken for content nested under the preceding heading. */ + border-left: 4px solid var(--code-border-color); } .rst-content dl:not(.field-list) > dt { From bbe4af76af1629956641a9854ddcd2c3b73eebfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 14 Jan 2026 23:42:18 +0100 Subject: [PATCH 267/455] doc: add moderncmakedomain for CMake support in Sphinx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is from CMake's repo https://gitlab.kitware.com/cmake/cmake/-/tree/master/Utilities/Sphinx?ref_type=heads This contains some minor tweaks that I'll try to get accepted upstream. Signed-off-by: Benjamin Cabé --- .ruff-excludes.toml | 17 + doc/_extensions/moderncmakedomain/REUSE.toml | 8 + doc/_extensions/moderncmakedomain/__init__.py | 5 + doc/_extensions/moderncmakedomain/cmake.py | 774 ++++++++++++++++++ doc/_extensions/moderncmakedomain/colors.py | 29 + doc/conf.py | 1 + 6 files changed, 834 insertions(+) create mode 100644 doc/_extensions/moderncmakedomain/REUSE.toml create mode 100644 doc/_extensions/moderncmakedomain/__init__.py create mode 100644 doc/_extensions/moderncmakedomain/cmake.py create mode 100644 doc/_extensions/moderncmakedomain/colors.py diff --git a/.ruff-excludes.toml b/.ruff-excludes.toml index 0d9ed427516f..e6035e3b6bea 100644 --- a/.ruff-excludes.toml +++ b/.ruff-excludes.toml @@ -14,6 +14,21 @@ "./boards/microchip/mec172xevb_assy6906/support/mec172x_remote_flasher.py" = [ "I001", # https://docs.astral.sh/ruff/rules/unsorted-imports ] +"./doc/_extensions/moderncmakedomain/cmake.py" = [ + "B904", # https://docs.astral.sh/ruff/rules/raise-without-from-inside-except + "B905", # https://docs.astral.sh/ruff/rules/zip-without-explicit-strict + "I001", # https://docs.astral.sh/ruff/rules/unsorted-imports + "SIM115", # https://docs.astral.sh/ruff/rules/open-file-with-context-handler + "SIM201", # https://docs.astral.sh/ruff/rules/negate-equal-op + "UP006", # https://docs.astral.sh/ruff/rules/non-pep585-annotation + "UP015", # https://docs.astral.sh/ruff/rules/redundant-open-modes + "UP024", # https://docs.astral.sh/ruff/rules/os-error-alias + "UP035", # https://docs.astral.sh/ruff/rules/deprecated-import +] +"./doc/_extensions/moderncmakedomain/colors.py" = [ + "I001", # https://docs.astral.sh/ruff/rules/unsorted-imports + "UP009", # https://docs.astral.sh/ruff/rules/utf8-encoding-declaration +] "./doc/_scripts/redirects.py" = [ "E501", # https://docs.astral.sh/ruff/rules/line-too-long ] @@ -719,6 +734,8 @@ exclude = [ "./arch/x86/zefi/zefi.py", "./boards/microchip/mec172xevb_assy6906/support/mec172x_remote_flasher.py", + "./doc/_extensions/moderncmakedomain/cmake.py", + "./doc/_extensions/moderncmakedomain/colors.py", "./doc/_scripts/gen_devicetree_rest.py", "./doc/_scripts/redirects.py", "./doc/conf.py", diff --git a/doc/_extensions/moderncmakedomain/REUSE.toml b/doc/_extensions/moderncmakedomain/REUSE.toml new file mode 100644 index 000000000000..b9c97b680127 --- /dev/null +++ b/doc/_extensions/moderncmakedomain/REUSE.toml @@ -0,0 +1,8 @@ +version = 1 + +[[annotations]] +path = [ + "*", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileCopyrightText = "Copyright 2000-2026 Kitware, Inc. and Contributors" diff --git a/doc/_extensions/moderncmakedomain/__init__.py b/doc/_extensions/moderncmakedomain/__init__.py new file mode 100644 index 000000000000..c139a70cfc62 --- /dev/null +++ b/doc/_extensions/moderncmakedomain/__init__.py @@ -0,0 +1,5 @@ +from .cmake import setup + +__version__ = "3.29.0" + +__all__ = ["__version__", "setup"] diff --git a/doc/_extensions/moderncmakedomain/cmake.py b/doc/_extensions/moderncmakedomain/cmake.py new file mode 100644 index 000000000000..720d841fbf84 --- /dev/null +++ b/doc/_extensions/moderncmakedomain/cmake.py @@ -0,0 +1,774 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# BEGIN imports + +import os +import re +from dataclasses import dataclass +from typing import Any, List, Tuple, Type, cast + +import sphinx + +# The following imports may fail if we don't have Sphinx 2.x or later. +if sphinx.version_info >= (2,): + from docutils import io, nodes + from docutils.nodes import Element, Node, TextElement, system_message + from docutils.parsers.rst import Directive, directives + from docutils.transforms import Transform + from docutils.utils.code_analyzer import Lexer, LexerError + + from sphinx import addnodes + from sphinx.directives import ObjectDescription, nl_escape_re + from sphinx.domains import Domain, ObjType + from sphinx.roles import XRefRole + from sphinx.util import logging, ws_re + from sphinx.util.docutils import ReferenceRole + from sphinx.util.nodes import make_refnode +else: + # Sphinx 2.x is required. + assert sphinx.version_info >= (2,) + +# END imports + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +# BEGIN pygments tweaks + +# Override much of pygments' CMakeLexer. +# We need to parse CMake syntax definitions, not CMake code. + +# For hard test cases that use much of the syntax below, see +# - module/FindPkgConfig.html +# (with "glib-2.0>=2.10 gtk+-2.0" and similar) +# - module/ExternalProject.html +# (with http:// https:// git@; also has command options -E --build) +# - manual/cmake-buildsystem.7.html +# (with nested $<..>; relative and absolute paths, "::") + +from pygments.lexer import bygroups # noqa I100 +from pygments.lexers import CMakeLexer # pylint: disable=no-name-in-module +from pygments.token import (Comment, Name, Number, Operator, Punctuation, + String, Text, Whitespace) + +# Notes on regular expressions below: +# - [\.\+-] are needed for string constants like gtk+-2.0 +# - Unix paths are recognized by '/'; support for Windows paths may be added +# if needed +# - (\\.) allows for \-escapes (used in manual/cmake-language.7) +# - $<..$<..$>..> nested occurrence in cmake-buildsystem +# - Nested variable evaluations are only supported in a limited capacity. +# Only one level of nesting is supported and at most one nested variable can +# be present. + +CMakeLexer.tokens["root"] = [ + # fctn( + (r'\b(\w+)([ \t]*)(\()', + bygroups(Name.Function, Text, Name.Function), '#push'), + (r'\(', Name.Function, '#push'), + (r'\)', Name.Function, '#pop'), + (r'\[', Punctuation, '#push'), + (r'\]', Punctuation, '#pop'), + (r'[|;,.=*\-]', Punctuation), + # used in commands/source_group + (r'\\\\', Punctuation), + (r'[:]', Operator), + # used in FindPkgConfig.cmake + (r'[<>]=', Punctuation), + # $<...> + (r'\$<', Operator, '#push'), + # + (r'<[^<|]+?>(\w*\.\.\.)?', Name.Variable), + # ${..} $ENV{..}, possibly nested + (r'(\$\w*\{)([^\}\$]*)?(?:(\$\w*\{)([^\}]+?)(\}))?([^\}]*?)(\})', + bygroups(Operator, Name.Tag, Operator, Name.Tag, Operator, Name.Tag, + Operator)), + # DATA{ ...} + (r'([A-Z]+\{)(.+?)(\})', bygroups(Operator, Name.Tag, Operator)), + # URL, git@, ... + (r'[a-z]+(@|(://))((\\.)|[\w.+-:/\\])+', Name.Attribute), + # absolute path + (r'/\w[\w\.\+-/\\]*', Name.Attribute), + (r'/', Name.Attribute), + # relative path + (r'\w[\w\.\+-]*/[\w.+-/\\]*', Name.Attribute), + # initial A-Z, contains a-z + (r'[A-Z]((\\.)|[\w.+-])*[a-z]((\\.)|[\w.+-])*', Name.Builtin), + (r'@?[A-Z][A-Z0-9_]*', Name.Constant), + (r'[a-z_]((\\;)|(\\ )|[\w.+-])*', Name.Builtin), + (r'[0-9][0-9\.]*', Number), + # "string" + (r'(?s)"(\\"|[^"])*"', String), + (r'\.\.\.', Name.Variable), + # <..|..> is different from + (r'<', Operator, '#push'), + (r'>', Operator, '#pop'), + (r'\n', Whitespace), + (r'[ \t]+', Whitespace), + (r'#.*\n', Comment), + # fallback, for debugging only + # (r'[^<>\])\}\|$"# \t\n]+', Name.Exception), +] + +# END pygments tweaks + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +logger = logging.getLogger(__name__) + +# RE to split multiple command signatures. +sig_end_re = re.compile(r'(?<=[)])\n') + + +@dataclass +class ObjectEntry: + docname: str + objtype: str + node_id: str + name: str + + +class CMakeModule(Directive): + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'encoding': directives.encoding} + + def __init__(self, *args, **keys): + self.re_start = re.compile(r'^#\[(?P=*)\[\.rst:$') + Directive.__init__(self, *args, **keys) + + def run(self): + settings = self.state.document.settings + if not settings.file_insertion_enabled: + raise self.warning(f'{self.name!r} directive disabled.') + + env = self.state.document.settings.env + _, path = env.relfn2path(self.arguments[0]) + path = os.path.normpath(path) + encoding = self.options.get('encoding', settings.input_encoding) + e_handler = settings.input_encoding_error_handler + try: + settings.record_dependencies.add(path) + f = io.FileInput(source_path=path, encoding=encoding, + error_handler=e_handler) + except UnicodeEncodeError: + msg = (f'Problems with {self.name!r} directive path:\n' + f'Cannot encode input file path {path!r} (wrong locale?).') + raise self.severe(msg) + except IOError as error: + msg = f'Problems with {self.name!r} directive path:\n{error}.' + raise self.severe(msg) + raw_lines = f.read().splitlines() + f.close() + rst = None + lines = [] + for line in raw_lines: + if rst is not None and rst != '#': + # Bracket mode: check for end bracket + pos = line.find(rst) + if pos >= 0: + if line[0] == '#': + line = '' + else: + line = line[0:pos] + rst = None + else: + # Line mode: check for .rst start (bracket or line) + m = self.re_start.match(line) + if m: + rst = f']{m.group("eq")}]' + line = '' + elif line == '#.rst:': + rst = '#' + line = '' + elif rst == '#': + if line == '#' or line[:2] == '# ': + line = line[2:] + else: + rst = None + line = '' + elif rst is None: + line = '' + lines.append(line) + if rst is not None and rst != '#': + raise self.warning(f'{self.name!r} found unclosed bracket ' + f'"#[{rst[1:-1]}[.rst:" in {path!r}') + self.state_machine.insert_input(lines, path) + return [] + + +class _cmake_index_entry: + def __init__(self, desc): + self.desc = desc + + def __call__(self, title, targetid, main='main'): + return ('pair', f'{self.desc} ; {title}', targetid, main, None) + + +_cmake_index_objs = { + 'command': _cmake_index_entry('command'), + 'cpack_gen': _cmake_index_entry('cpack generator'), + 'envvar': _cmake_index_entry('envvar'), + 'generator': _cmake_index_entry('generator'), + 'genex': _cmake_index_entry('genex'), + 'guide': _cmake_index_entry('guide'), + 'manual': _cmake_index_entry('manual'), + 'module': _cmake_index_entry('module'), + 'policy': _cmake_index_entry('policy'), + 'prop_cache': _cmake_index_entry('cache property'), + 'prop_dir': _cmake_index_entry('directory property'), + 'prop_gbl': _cmake_index_entry('global property'), + 'prop_inst': _cmake_index_entry('installed file property'), + 'prop_sf': _cmake_index_entry('source file property'), + 'prop_test': _cmake_index_entry('test property'), + 'prop_tgt': _cmake_index_entry('target property'), + 'variable': _cmake_index_entry('variable'), + } + + +class CMakeTransform(Transform): + + # Run this transform early since we insert nodes we want + # treated as if they were written in the documents. + default_priority = 210 + + def __init__(self, document, startnode): + Transform.__init__(self, document, startnode) + self.titles = {} + + def parse_title(self, docname): + """Parse a document title as the first line starting in [A-Za-z0-9<$] + or fall back to the document basename if no such line exists. + The cmake --help-*-list commands also depend on this convention. + Return the title or False if the document file does not exist. + """ + settings = self.document.settings + env = settings.env + title = self.titles.get(docname) + if title is None: + fname = os.path.join(env.srcdir, docname+'.rst') + try: + f = open(fname, 'r', encoding=settings.input_encoding) + except IOError: + title = False + else: + for line in f: + if len(line) > 0 and (line[0].isalnum() or + line[0] == '<' or line[0] == '$'): + title = line.rstrip() + break + f.close() + if title is None: + title = os.path.basename(docname) + self.titles[docname] = title + return title + + def apply(self): + env = self.document.settings.env + + # Identify CMake domain objects from parent name + parts = env.docname.split('/') + objtype = None + tail = None + if len(parts) > 1 and parts[-2] in _cmake_index_objs: + objtype = parts[-2] + tail = parts[-1] + + make_index_entry = _cmake_index_objs.get(objtype) + if make_index_entry: + title = self.parse_title(env.docname) + # Insert the object link target. + if objtype == 'command': + targetname = title.lower() + elif objtype == 'guide' and not tail.endswith('/index'): + targetname = tail + else: + if objtype == 'genex': + m = CMakeXRefRole._re_genex.match(title) + if m: + title = m.group(1) + targetname = title + targetid = f'{objtype}:{targetname}' + targetnode = nodes.target('', '', ids=[targetid]) + self.document.note_explicit_target(targetnode) + self.document.insert(0, targetnode) + # Insert the object index entry. + indexnode = addnodes.index() + indexnode['entries'] = [make_index_entry(title, targetid)] + self.document.insert(0, indexnode) + + # Add to cmake domain object inventory + domain = cast(CMakeDomain, env.get_domain('cmake')) + domain.note_object(objtype, targetname, targetid, targetid) + + +class CMakeObject(ObjectDescription): + def __init__(self, *args, **kwargs): + self.targetname = None + super().__init__(*args, **kwargs) + + def handle_signature(self, sig, signode): + # called from sphinx.directives.ObjectDescription.run() + signode += addnodes.desc_name(sig, sig) + return sig + + def add_target_and_index(self, name, sig, signode): + if self.objtype == 'command': + targetname = name.lower() + elif self.targetname: + targetname = self.targetname + else: + targetname = name + targetid = f'{self.objtype}:{targetname}' + if targetid not in self.state.document.ids: + signode['names'].append(targetid) + signode['ids'].append(targetid) + signode['first'] = not self.names + self.state.document.note_explicit_target(signode) + + domain = cast(CMakeDomain, self.env.get_domain('cmake')) + domain.note_object(self.objtype, targetname, targetid, targetid, + location=signode) + + make_index_entry = _cmake_index_objs.get(self.objtype) + if make_index_entry: + self.indexnode['entries'].append(make_index_entry(name, targetid)) + + +class CMakeGenexObject(CMakeObject): + option_spec = { + 'target': directives.unchanged, + } + + def handle_signature(self, sig, signode): + name = super().handle_signature(sig, signode) + + m = CMakeXRefRole._re_genex.match(sig) + if m: + name = m.group(1) + + return name + + def run(self): + target = self.options.get('target') + if target is not None: + self.targetname = target + + return super().run() + + +class CMakeSignatureObject(CMakeObject): + object_type = 'signature' + + BREAK_ALL = 'all' + BREAK_SMART = 'smart' + BREAK_VERBATIM = 'verbatim' + + BREAK_CHOICES = {BREAK_ALL, BREAK_SMART, BREAK_VERBATIM} + + @staticmethod + def break_option(argument): + return directives.choice(argument, CMakeSignatureObject.BREAK_CHOICES) + + option_spec = { + 'target': directives.unchanged, + 'break': break_option, + } + + @staticmethod + def _break_signature_all(sig: str) -> str: + return ws_re.sub(' ', sig) + + @staticmethod + def _break_signature_verbatim(sig: str) -> str: + lines = [ws_re.sub('\xa0', line.strip()) for line in sig.split('\n')] + return ' '.join(lines) + + @staticmethod + def _break_signature_smart(sig: str) -> str: + tokens = [] + for line in sig.split('\n'): + token = '' + delim = '' + + for c in line.strip(): + if not delim and ws_re.match(c): + if token: + tokens.append(ws_re.sub('\xa0', token)) + token = '' + else: + if c == '[': + delim += ']' + elif c == '<': + delim += '>' + elif delim and c == delim[-1]: + delim = delim[:-1] + token += c + + if token: + tokens.append(ws_re.sub('\xa0', token)) + + return ' '.join(tokens) + + def __init__(self, *args, **kwargs): + self.targetnames = {} + self.break_style = CMakeSignatureObject.BREAK_SMART + super().__init__(*args, **kwargs) + + def get_signatures(self) -> List[str]: + content = nl_escape_re.sub('', self.arguments[0]) + lines = sig_end_re.split(content) + + if self.break_style == CMakeSignatureObject.BREAK_VERBATIM: + fixup = CMakeSignatureObject._break_signature_verbatim + elif self.break_style == CMakeSignatureObject.BREAK_SMART: + fixup = CMakeSignatureObject._break_signature_smart + else: + fixup = CMakeSignatureObject._break_signature_all + + return [fixup(line.strip()) for line in lines] + + def handle_signature(self, sig, signode): + language = 'cmake' + classes = ['code', 'cmake', 'highlight'] + + node = addnodes.desc_name(sig, '', classes=classes) + + try: + tokens = Lexer(sig, language, 'short') + except LexerError as error: + if self.state.document.settings.report_level > 2: + # Silently insert without syntax highlighting. + tokens = Lexer(sig, language, 'none') + else: + raise self.warning(error) + + for classes, value in tokens: + if value == '\xa0': + node += nodes.inline(value, value, classes=['nbsp']) + elif classes: + node += nodes.inline(value, value, classes=classes) + else: + node += nodes.Text(value) + + signode.clear() + signode += node + + return sig + + def add_target_and_index(self, name, sig, signode): + sig = sig.replace('\xa0', ' ') + if sig in self.targetnames: + sigargs = self.targetnames[sig] + else: + def extract_keywords(params): + for p in params: + if p[0].isalpha(): + yield p + else: + return + + keywords = extract_keywords(sig.split('(')[1].split()) + sigargs = ' '.join(keywords) + command = sig.split('(')[0].lower() + targetname = sigargs.lower() + # Anchor each signature on its command name, qualified by the keyword + # arguments that distinguish overloaded signatures. The command name is + # unique, so this keeps keyword-less signatures from collapsing onto the + # same id and stops two commands that share a leading keyword from + # colliding. The id is kept in the 'command:' namespace (as the + # cmake:command directive does) so it cannot clash with section ids. + anchor = ' '.join(filter(None, [command, targetname])) + targetid = 'command:' + nodes.make_id(anchor) + + if targetid not in self.state.document.ids: + signode['names'].append(anchor) + signode['ids'].append(targetid) + signode['first'] = not self.names + self.state.document.note_explicit_target(signode) + + domain = cast(CMakeDomain, self.env.get_domain('cmake')) + + # Register the keyword-qualified signature as a command object. + if sigargs: + refname = f'{command}({sigargs})' + refid = f'command:{command}({targetname})' + domain.note_object('command', name=refname, target_id=refid, + node_id=targetid, location=signode) + + # Also register the bare command name so it can be referenced as + # :cmake:command:``, unless a dedicated cmake:command + # directive (or an earlier signature) already registered it. + bareid = f'command:{command}' + if bareid not in domain.data['objects']: + domain.note_object('command', name=command, target_id=bareid, + node_id=targetid, location=signode) + + def run(self): + self.break_style = CMakeSignatureObject.BREAK_ALL + + targets = self.options.get('target') + if targets is not None: + signatures = self.get_signatures() + targets = [t.strip() for t in targets.split('\n')] + for signature, target in zip(signatures, targets): + self.targetnames[signature] = target + + self.break_style = ( + self.options.get('break', CMakeSignatureObject.BREAK_SMART)) + + return super().run() + + +class CMakeReferenceRole: + # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. + _re = re.compile(r'^(.+?)(\s*)(?$', re.DOTALL) + + @staticmethod + def _escape_angle_brackets(text: str) -> str: + # CMake cross-reference targets frequently contain '<' so escape + # any explicit `` with '<' not preceded by whitespace. + while True: + m = CMakeReferenceRole._re.match(text) + if m and len(m.group(2)) == 0: + text = f'{m.group(1)}\x00<{m.group(3)}>' + else: + break + return text + + def __class_getitem__(cls, parent: Any): + class Class(parent): + def __call__(self, name: str, rawtext: str, text: str, + *args, **kwargs + ) -> Tuple[List[Node], List[system_message]]: + text = CMakeReferenceRole._escape_angle_brackets(text) + return super().__call__(name, rawtext, text, *args, **kwargs) + return Class + + +class CMakeCRefRole(CMakeReferenceRole[ReferenceRole]): + nodeclass: Type[Element] = nodes.reference + innernodeclass: Type[TextElement] = nodes.literal + classes: List[str] = ['cmake', 'literal'] + + def run(self) -> Tuple[List[Node], List[system_message]]: + refnode = self.nodeclass(self.rawtext) + self.set_source_info(refnode) + + refnode['refid'] = nodes.make_id(self.target) + refnode += self.innernodeclass(self.rawtext, self.title, + classes=self.classes) + + return [refnode], [] + + +class CMakeXRefRole(CMakeReferenceRole[XRefRole]): + + _re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL) + _re_genex = re.compile(r'^\$<([^<>:]+)(:[^<>]+)?>$', re.DOTALL) + _re_guide = re.compile(r'^([^<>/]+)/([^<>]*)$', re.DOTALL) + + def __call__(self, typ, rawtext, text, *args, **kwargs): + if typ == 'cmake:command': + # Translate a CMake command cross-reference of the form: + # `command_name(SUB_COMMAND)` + # to be its own explicit target: + # `command_name(SUB_COMMAND) ` + # so the XRefRole `fix_parens` option does not add more `()`. + m = CMakeXRefRole._re_sub.match(text) + if m: + text = f'{text} <{text}>' + elif typ == 'cmake:genex': + m = CMakeXRefRole._re_genex.match(text) + if m: + text = f'{text} <{m.group(1)}>' + elif typ == 'cmake:guide': + m = CMakeXRefRole._re_guide.match(text) + if m: + text = f'{m.group(2)} <{text}>' + return super().__call__(typ, rawtext, text, *args, **kwargs) + + # We cannot insert index nodes using the result_nodes method + # because CMakeXRefRole is processed before substitution_reference + # nodes are evaluated so target nodes (with 'ids' fields) would be + # duplicated in each evaluated substitution replacement. The + # docutils substitution transform does not allow this. Instead we + # use our own CMakeXRefTransform below to add index entries after + # substitutions are completed. + # + # def result_nodes(self, document, env, node, is_ref): + # pass + + +class CMakeXRefTransform(Transform): + + # Run this transform early since we insert nodes we want + # treated as if they were written in the documents, but + # after the sphinx (210) and docutils (220) substitutions. + default_priority = 221 + + # This helper supports docutils < 0.18, which is missing 'findall', + # and docutils == 0.18.0, which is missing 'traverse'. + def _document_findall_as_list(self, condition): + if hasattr(self.document, 'findall'): + # Fully iterate into a list so the caller can grow 'self.document' + # while iterating. + return list(self.document.findall(condition)) + + # Fallback to 'traverse' on old docutils, which returns a list. + return self.document.traverse(condition) + + def apply(self): + env = self.document.settings.env + + # Find CMake cross-reference nodes and add index and target + # nodes for them. + for ref in self._document_findall_as_list(addnodes.pending_xref): + if not ref['refdomain'] == 'cmake': + continue + + objtype = ref['reftype'] + make_index_entry = _cmake_index_objs.get(objtype) + if not make_index_entry: + continue + + objname = ref['reftarget'] + if objtype == 'guide' and CMakeXRefRole._re_guide.match(objname): + # Do not index cross-references to guide sections. + continue + + if objtype == 'command': + # Index signature references to their parent command. + objname = objname.split('(')[0].lower() + + targetnum = env.new_serialno(f'index-{objtype}:{objname}') + + targetid = f'index-{targetnum}-{objtype}:{objname}' + targetnode = nodes.target('', '', ids=[targetid]) + self.document.note_explicit_target(targetnode) + + indexnode = addnodes.index() + indexnode['entries'] = [make_index_entry(objname, targetid, '')] + ref.replace_self([indexnode, targetnode, ref]) + + +class CMakeDomain(Domain): + """CMake domain.""" + name = 'cmake' + label = 'CMake' + object_types = { + 'command': ObjType('command', 'command'), + 'cpack_gen': ObjType('cpack_gen', 'cpack_gen'), + 'envvar': ObjType('envvar', 'envvar'), + 'generator': ObjType('generator', 'generator'), + 'genex': ObjType('genex', 'genex'), + 'guide': ObjType('guide', 'guide'), + 'variable': ObjType('variable', 'variable'), + 'module': ObjType('module', 'module'), + 'policy': ObjType('policy', 'policy'), + 'prop_cache': ObjType('prop_cache', 'prop_cache'), + 'prop_dir': ObjType('prop_dir', 'prop_dir'), + 'prop_gbl': ObjType('prop_gbl', 'prop_gbl'), + 'prop_inst': ObjType('prop_inst', 'prop_inst'), + 'prop_sf': ObjType('prop_sf', 'prop_sf'), + 'prop_test': ObjType('prop_test', 'prop_test'), + 'prop_tgt': ObjType('prop_tgt', 'prop_tgt'), + 'manual': ObjType('manual', 'manual'), + } + directives = { + 'command': CMakeObject, + 'envvar': CMakeObject, + 'genex': CMakeGenexObject, + 'signature': CMakeSignatureObject, + 'variable': CMakeObject, + # Other `object_types` cannot be created except by the `CMakeTransform` + } + roles = { + 'cref': CMakeCRefRole(), + 'command': CMakeXRefRole(fix_parens=True, lowercase=True), + 'cpack_gen': CMakeXRefRole(), + 'envvar': CMakeXRefRole(), + 'generator': CMakeXRefRole(), + 'genex': CMakeXRefRole(), + 'guide': CMakeXRefRole(), + 'variable': CMakeXRefRole(), + 'module': CMakeXRefRole(), + 'policy': CMakeXRefRole(), + 'prop_cache': CMakeXRefRole(), + 'prop_dir': CMakeXRefRole(), + 'prop_gbl': CMakeXRefRole(), + 'prop_inst': CMakeXRefRole(), + 'prop_sf': CMakeXRefRole(), + 'prop_test': CMakeXRefRole(), + 'prop_tgt': CMakeXRefRole(), + 'manual': CMakeXRefRole(), + } + initial_data = { + 'objects': {}, # fullname -> ObjectEntry + } + + def clear_doc(self, docname): + to_clear = set() + for fullname, obj in self.data['objects'].items(): + if obj.docname == docname: + to_clear.add(fullname) + for fullname in to_clear: + del self.data['objects'][fullname] + + def merge_domaindata(self, docnames, otherdata): + """Merge domaindata from the workers/chunks when they return. + + Called once per parallelization chunk. + Only used when sphinx is run in parallel mode. + + :param docnames: a Set of the docnames that are part of the current + chunk to merge + :param otherdata: the partial data calculated by the current chunk + """ + for refname, obj in otherdata['objects'].items(): + if obj.docname in docnames: + self.data['objects'][refname] = obj + + def resolve_xref(self, env, fromdocname, builder, + typ, target, node, contnode): + targetid = f'{typ}:{target}' + obj = self.data['objects'].get(targetid) + + if obj is None and typ == 'command': + # If 'command(args)' wasn't found, try just 'command'. + # TODO: remove this fallback? warn? + # logger.warning(f'no match for {targetid}') + command = target.split('(')[0] + targetid = f'{typ}:{command}' + obj = self.data['objects'].get(targetid) + + if obj is None: + # TODO: warn somehow? + return None + + return make_refnode(builder, fromdocname, obj.docname, obj.node_id, + contnode, target) + + def note_object(self, objtype: str, name: str, target_id: str, + node_id: str, location: Any = None): + if target_id in self.data['objects']: + other = self.data['objects'][target_id].docname + logger.warning( + f'CMake object {target_id!r} also described in {other!r}', + location=location) + + self.data['objects'][target_id] = ObjectEntry( + self.env.docname, objtype, node_id, name) + + def get_objects(self): + for refname, obj in self.data['objects'].items(): + yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1) + + +def setup(app): + app.add_directive('cmake-module', CMakeModule) + app.add_transform(CMakeTransform) + app.add_transform(CMakeXRefTransform) + app.add_domain(CMakeDomain) + return {"parallel_read_safe": True} diff --git a/doc/_extensions/moderncmakedomain/colors.py b/doc/_extensions/moderncmakedomain/colors.py new file mode 100644 index 000000000000..dae00634e85a --- /dev/null +++ b/doc/_extensions/moderncmakedomain/colors.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from pygments.style import Style +from pygments.token import Name, Comment, String, Number, Operator, Whitespace + +class CMakeTemplateStyle(Style): + """ + for more token names, see pygments/styles.default + """ + + background_color = "#f8f8f8" + default_style = "" + + styles = { + Whitespace: "#bbbbbb", + Comment: "italic #408080", + Operator: "#555555", + String: "#217A21", + Number: "#105030", + Name.Builtin: "#333333", # anything lowercase + Name.Function: "#007020", # function + Name.Variable: "#1080B0", # <..> + Name.Tag: "#bb60d5", # ${..} + Name.Constant: "#4070a0", # uppercase only + Name.Entity: "italic #70A020", # @..@ + Name.Attribute: "#906060", # paths, URLs + Name.Label: "#A0A000", # anything left over + Name.Exception: "bold #FF0000", # for debugging only + } diff --git a/doc/conf.py b/doc/conf.py index e065267ef45e..07cfdb59227f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -119,6 +119,7 @@ "zephyr.domain", "zephyr.api_overview", "zephyr.partial_build", + "moderncmakedomain", ] # Only use image conversion when it is really needed, e.g. LaTeX build. From 8a53f69796f0c437c792ca9163446b5d181bb5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 10 Jul 2026 17:26:35 +0200 Subject: [PATCH 268/455] doc: css: render CMake signatures as code blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CMake domain syntax highlights the signatures of the entities it documents, but the signature header keeps the theme's own background, so the highlighting colors end up on a background they were not picked to contrast with. This is most visible in dark mode, where the dark code background of the command name sits inside a light blue header. Give the whole signature the code background instead, so that both themes render it as a single, legible code block. Assisted-by: Claude:opus-4.8 Signed-off-by: Benjamin Cabé --- doc/_static/css/custom.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index a5182c6ac08e..23029392a03d 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -606,6 +606,18 @@ kbd, .kbd, border: 4px solid var(--content-background-color); } +/* Give CMake signatures and their nested parameter terms the code background, so the syntax + highlighting stays legible and the terms aren't left with the theme's too-bright default. */ +.rst-content dl.cmake > dt, +.rst-content dl.cmake dd dl:not(.field-list) > dt { + background: var(--highlight-background-color) !important; + color: var(--highlight-default-color) !important; +} + +.rst-content dl.cmake > dt .sig-name { + color: inherit !important; +} + /* Buttons */ .btn-neutral { From 0285bb20650b9d940d4ad63e1ce587e54da82ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 14 Jan 2026 23:38:38 +0100 Subject: [PATCH 269/455] doc: conf.py: add intersphinx mapping for CMake documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow users to link to CMake documentation using intersphinx. Signed-off-by: Benjamin Cabé --- doc/conf.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/conf.py b/doc/conf.py index 07cfdb59227f..947901efa8f7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -120,6 +120,7 @@ "zephyr.api_overview", "zephyr.partial_build", "moderncmakedomain", + "sphinx.ext.intersphinx", ] # Only use image conversion when it is really needed, e.g. LaTeX build. @@ -163,6 +164,10 @@ todo_include_todos = False +intersphinx_mapping = { + "cmake": ("https://cmake.org/cmake/help/latest", None), +} + nitpick_ignore = [ # ignore C standard identifiers (they are not defined in Zephyr docs) ("c:identifier", "FILE"), From 32ab90b1d9bb4a3e68f21cfe606218d84f18780d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 14 Jan 2026 23:43:19 +0100 Subject: [PATCH 270/455] doc: guidelines: explain how to use CMake domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add instructions on how to use the CMake domain to e.g. cross reference CMake commands and variables in the documentation. Signed-off-by: Benjamin Cabé --- doc/contribute/documentation/guidelines.rst | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/doc/contribute/documentation/guidelines.rst b/doc/contribute/documentation/guidelines.rst index f26205759e91..a246da8c3684 100644 --- a/doc/contribute/documentation/guidelines.rst +++ b/doc/contribute/documentation/guidelines.rst @@ -698,6 +698,51 @@ Cross-referencing C documentation You may provide a custom link text, similar to the built-in :rst:role:`ref` role. +Cross-referencing CMake documentation +===================================== + +You may use the following roles to cross-reference the documentation of Zephyr's CMake modules, +commands, and variables. + +.. rst:role:: cmake:module + + This role is used to reference a CMake module. For example:: + + See :cmake:module:`extensions` for more information. + + Will render as: + + See :cmake:module:`extensions` for more information. + +.. rst:role:: cmake:command + + This role is used to reference a CMake command. For example:: + + See :cmake:command:`yaml_load` for more information. + + Will render as: + + See :cmake:command:`yaml_load` for more information. + + Commands documented by CMake itself are referenced through their fully qualified name, given as + an explicit link target:: + + See :cmake:command:`target_sources ` for more information. + + Will render as: + + See :cmake:command:`target_sources ` for more information. + +.. rst:role:: cmake:variable + + This role is used to reference a CMake variable. For example:: + + See :cmake:variable:`CMAKE_C_COMPILER` for more information. + + Will render as: + + See :cmake:variable:`CMAKE_C_COMPILER` for more information. + Visual Elements *************** From 1f3719324470a79bd3a9801ce5d9658ec0e514d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 14 Jan 2026 23:48:12 +0100 Subject: [PATCH 271/455] doc: cmake: add CMake reference section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new documentation section for CMake reference, that will leverage the sphinxcontrib-moderncmakedomain extension. Signed-off-by: Benjamin Cabé --- doc/build/cmake-ref/index.rst | 4 ++++ doc/build/cmake/index.rst | 2 ++ doc/build/index.rst | 1 + doc/conf.py | 2 ++ doc/index.html | 15 +++++++++++++++ 5 files changed, 24 insertions(+) create mode 100644 doc/build/cmake-ref/index.rst diff --git a/doc/build/cmake-ref/index.rst b/doc/build/cmake-ref/index.rst new file mode 100644 index 000000000000..c62681687676 --- /dev/null +++ b/doc/build/cmake-ref/index.rst @@ -0,0 +1,4 @@ +.. _cmake-reference: + +CMake Reference +=============== diff --git a/doc/build/cmake/index.rst b/doc/build/cmake/index.rst index a46d1229c82c..744e6326ff1f 100644 --- a/doc/build/cmake/index.rst +++ b/doc/build/cmake/index.rst @@ -46,6 +46,8 @@ paths of a target library. When introducing build system code using CMake or adding new CMake files, please follow the style guidelines outlined :ref:`here `. +The :ref:`cmake-reference` section provides a reference for all CMake commands, +variables, and modules used by Zephyr and available to application developers. Build and Configuration Phases ============================== diff --git a/doc/build/index.rst b/doc/build/index.rst index 63e3bcc6dce0..474a00dab14f 100644 --- a/doc/build/index.rst +++ b/doc/build/index.rst @@ -9,6 +9,7 @@ Build and Configuration Systems cmake/index.rst + cmake-ref/index.rst dts/index kconfig/index.rst snippets/index.rst diff --git a/doc/conf.py b/doc/conf.py index 947901efa8f7..b81ed20c48f0 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -258,6 +258,7 @@ "API": f"{reference_prefix}/doxygen/html/index.html", "Kconfig Options": f"{reference_prefix}/kconfig.html", "Devicetree Bindings": f"{reference_prefix}/build/dts/api/bindings.html", + "CMake modules": f"{reference_prefix}/build/cmake-ref/index.html", "West Projects": f"{reference_prefix}/develop/manifest/index.html", "Glossary": f"{reference_prefix}/glossary.html", }, @@ -396,6 +397,7 @@ external_content_contents = [ (ZEPHYR_BASE / "doc", "[!_]*"), (ZEPHYR_BASE, "tests/**/*.pts"), + (ZEPHYR_BASE, "cmake/modules"), ] if not SKIP_EXTERNAL_CONTENT: external_content_contents += [ diff --git a/doc/index.html b/doc/index.html index 57dd40950850..a12be157f1ee 100644 --- a/doc/index.html +++ b/doc/index.html @@ -376,6 +376,10 @@ background-color: rgba(239, 68, 68, 0.2); color: #f87171; } + .ref-cmake { + background-color: rgba(244, 114, 182, 0.2); + color: #f472b6; + } .ref-glossary { background-color: rgba(249, 115, 22, 0.2); color: #fb923c; @@ -611,6 +615,17 @@

West Projects

+ +
+ +
+
+

CMake

+

Build system

+
+ +
+
From b1e1ad7d9c769b8446c68726e44ff3685ba154a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 14 Jan 2026 23:49:34 +0100 Subject: [PATCH 272/455] doc: conf.py: increase navigation depth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add one more level to the HTML sidebar navigation depth as reference documentation such as CMake is typically deeper nested. Signed-off-by: Benjamin Cabé --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index b81ed20c48f0..db3b2e73f337 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -220,7 +220,7 @@ html_theme_options = { "logo_only": True, "prev_next_buttons_location": None, - "navigation_depth": 5, + "navigation_depth": 6, } html_baseurl = "https://docs.zephyrproject.org/latest/" html_title = "Zephyr Project Documentation" From 77c6b14c4ba6a1c2ffa3989768aa39ca5f22afbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 14 Jan 2026 23:50:46 +0100 Subject: [PATCH 273/455] cmake: doc: Add CMake build system reference documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the CMake modules, variables and target properties exposed by the Zephyr build system, using the upstream CMake Sphinx domain so that entities can be cross-referenced from the rest of the documentation rather than looked up by reading the .cmake sources. Signed-off-by: Benjamin Cabé --- cmake/modules/arch.cmake | 41 +- cmake/modules/basic_settings.cmake | 39 +- cmake/modules/boards.cmake | 115 +- cmake/modules/configuration_files.cmake | 45 +- cmake/modules/doc.cmake | 29 +- cmake/modules/extensions.cmake | 4290 ++++++++++------- cmake/modules/git.cmake | 49 + cmake/modules/kconfig.cmake | 47 + cmake/modules/kernel.cmake | 77 +- cmake/modules/python.cmake | 50 + cmake/modules/soc.cmake | 35 +- cmake/modules/west.cmake | 17 + cmake/modules/yaml.cmake | 388 +- doc/build/cmake-ref/index.rst | 47 + doc/build/cmake-ref/module/arch.rst | 1 + doc/build/cmake-ref/module/basic_settings.rst | 1 + doc/build/cmake-ref/module/boards.rst | 1 + .../cmake-ref/module/configuration_files.rst | 1 + doc/build/cmake-ref/module/doc.rst | 1 + doc/build/cmake-ref/module/extensions.rst | 1 + doc/build/cmake-ref/module/git.rst | 1 + doc/build/cmake-ref/module/kconfig.rst | 1 + doc/build/cmake-ref/module/kernel.rst | 1 + doc/build/cmake-ref/module/python.rst | 1 + doc/build/cmake-ref/module/soc.rst | 1 + doc/build/cmake-ref/module/west.rst | 1 + doc/build/cmake-ref/module/yaml.rst | 1 + .../prop_tgt/LIBC_LINK_LIBRARIES.rst | 8 + .../variable/ACTIVE_BOARD_REVISION.rst | 6 + .../variable/APPLICATION_CONFIG_DIR.rst | 6 + doc/build/cmake-ref/variable/ARCH.rst | 6 + doc/build/cmake-ref/variable/ARCH_DIR.rst | 6 + doc/build/cmake-ref/variable/ARCH_ROOT.rst | 7 + doc/build/cmake-ref/variable/BOARD.rst | 14 + doc/build/cmake-ref/variable/BOARD_DIR.rst | 6 + .../cmake-ref/variable/BOARD_QUALIFIERS.rst | 6 + .../cmake-ref/variable/BOARD_REVISION.rst | 6 + doc/build/cmake-ref/variable/BOARD_ROOT.rst | 15 + .../variable/BSIM_COMPONENTS_PATH.rst | 6 + .../cmake-ref/variable/BSIM_OUT_PATH.rst | 6 + doc/build/cmake-ref/variable/CONF_FILE.rst | 6 + .../cmake-ref/variable/DTC_OVERLAY_FILE.rst | 6 + .../cmake-ref/variable/DTS_EXTRA_CPPFLAGS.rst | 6 + .../cmake-ref/variable/EXTRA_CONF_FILE.rst | 6 + .../variable/EXTRA_DTC_OVERLAY_FILE.rst | 6 + .../cmake-ref/variable/LLEXT_APPEND_FLAGS.rst | 7 + .../cmake-ref/variable/LLEXT_REMOVE_FLAGS.rst | 8 + .../variable/NORMALIZED_BOARD_QUALIFIERS.rst | 6 + .../variable/NORMALIZED_BOARD_TARGET.rst | 6 + .../cmake-ref/variable/PYTHON_EXECUTABLE.rst | 7 + .../variable/PYTHON_MINIMUM_REQUIRED.rst | 6 + doc/build/cmake-ref/variable/USE_CCACHE.rst | 6 + doc/build/cmake-ref/variable/WEST.rst | 6 + doc/build/cmake-ref/variable/WEST_PYTHON.rst | 6 + doc/build/cmake-ref/variable/WEST_TOPDIR.rst | 6 + .../variable/ZEPHYR_CURRENT_LIBRARY.rst | 4 + doc/hardware/porting/board_porting.rst | 4 +- doc/services/llext/build.rst | 16 +- 58 files changed, 3560 insertions(+), 1938 deletions(-) create mode 100644 doc/build/cmake-ref/module/arch.rst create mode 100644 doc/build/cmake-ref/module/basic_settings.rst create mode 100644 doc/build/cmake-ref/module/boards.rst create mode 100644 doc/build/cmake-ref/module/configuration_files.rst create mode 100644 doc/build/cmake-ref/module/doc.rst create mode 100644 doc/build/cmake-ref/module/extensions.rst create mode 100644 doc/build/cmake-ref/module/git.rst create mode 100644 doc/build/cmake-ref/module/kconfig.rst create mode 100644 doc/build/cmake-ref/module/kernel.rst create mode 100644 doc/build/cmake-ref/module/python.rst create mode 100644 doc/build/cmake-ref/module/soc.rst create mode 100644 doc/build/cmake-ref/module/west.rst create mode 100644 doc/build/cmake-ref/module/yaml.rst create mode 100644 doc/build/cmake-ref/prop_tgt/LIBC_LINK_LIBRARIES.rst create mode 100644 doc/build/cmake-ref/variable/ACTIVE_BOARD_REVISION.rst create mode 100644 doc/build/cmake-ref/variable/APPLICATION_CONFIG_DIR.rst create mode 100644 doc/build/cmake-ref/variable/ARCH.rst create mode 100644 doc/build/cmake-ref/variable/ARCH_DIR.rst create mode 100644 doc/build/cmake-ref/variable/ARCH_ROOT.rst create mode 100644 doc/build/cmake-ref/variable/BOARD.rst create mode 100644 doc/build/cmake-ref/variable/BOARD_DIR.rst create mode 100644 doc/build/cmake-ref/variable/BOARD_QUALIFIERS.rst create mode 100644 doc/build/cmake-ref/variable/BOARD_REVISION.rst create mode 100644 doc/build/cmake-ref/variable/BOARD_ROOT.rst create mode 100644 doc/build/cmake-ref/variable/BSIM_COMPONENTS_PATH.rst create mode 100644 doc/build/cmake-ref/variable/BSIM_OUT_PATH.rst create mode 100644 doc/build/cmake-ref/variable/CONF_FILE.rst create mode 100644 doc/build/cmake-ref/variable/DTC_OVERLAY_FILE.rst create mode 100644 doc/build/cmake-ref/variable/DTS_EXTRA_CPPFLAGS.rst create mode 100644 doc/build/cmake-ref/variable/EXTRA_CONF_FILE.rst create mode 100644 doc/build/cmake-ref/variable/EXTRA_DTC_OVERLAY_FILE.rst create mode 100644 doc/build/cmake-ref/variable/LLEXT_APPEND_FLAGS.rst create mode 100644 doc/build/cmake-ref/variable/LLEXT_REMOVE_FLAGS.rst create mode 100644 doc/build/cmake-ref/variable/NORMALIZED_BOARD_QUALIFIERS.rst create mode 100644 doc/build/cmake-ref/variable/NORMALIZED_BOARD_TARGET.rst create mode 100644 doc/build/cmake-ref/variable/PYTHON_EXECUTABLE.rst create mode 100644 doc/build/cmake-ref/variable/PYTHON_MINIMUM_REQUIRED.rst create mode 100644 doc/build/cmake-ref/variable/USE_CCACHE.rst create mode 100644 doc/build/cmake-ref/variable/WEST.rst create mode 100644 doc/build/cmake-ref/variable/WEST_PYTHON.rst create mode 100644 doc/build/cmake-ref/variable/WEST_TOPDIR.rst create mode 100644 doc/build/cmake-ref/variable/ZEPHYR_CURRENT_LIBRARY.rst diff --git a/cmake/modules/arch.cmake b/cmake/modules/arch.cmake index e14c75ac6025..8a8369cf4ae4 100644 --- a/cmake/modules/arch.cmake +++ b/cmake/modules/arch.cmake @@ -2,26 +2,27 @@ # # Copyright (c) 2023, Nordic Semiconductor ASA -# -# Configure ARCH settings based on KConfig settings and arch root. -# -# This CMake module will set the following variables in the build system based -# on board directory and arch root. -# -# If no implementation is available for the current arch an error will be raised. -# -# Outcome: -# The following variables will be defined when this CMake module completes: -# -# - ARCH: Name of the arch in use. -# - ARCH_DIR: Directory containing the arch implementation. -# - ARCH_ROOT: ARCH_ROOT with ZEPHYR_BASE appended -# -# Variable dependencies: -# - ARCH_ROOT: CMake list of arch roots containing arch implementations -# -# Variables set by this module and not mentioned above are considered internal -# use only and may be removed, renamed, or re-purposed without prior notice. +#[=======================================================================[.rst: +arch +**** + +Configure arch settings based on Kconfig settings and arch root. + +This CMake module will set the following variables in the build system based on board directory and +arch root. + +If no implementation is available for the current arch, an error will be raised. + +Variables +========= + +The following variables will be defined when this CMake module completes: + +* :cmake:variable:`ARCH` +* :cmake:variable:`ARCH_DIR` +* :cmake:variable:`ARCH_ROOT` + +#]=======================================================================] include_guard(GLOBAL) diff --git a/cmake/modules/basic_settings.cmake b/cmake/modules/basic_settings.cmake index f81aaeac57aa..76ee70a44048 100644 --- a/cmake/modules/basic_settings.cmake +++ b/cmake/modules/basic_settings.cmake @@ -2,22 +2,29 @@ # # Copyright (c) 2022, Nordic Semiconductor ASA -# Setup basic settings for a Zephyr project. -# -# Basic settings are: -# - sysbuild defined configuration settings -# -# Details for sysbuild settings: -# -# Sysbuild is a higher level build system used by Zephyr. -# Sysbuild allows users to build multiple samples for a given system. -# -# For this to work, sysbuild manages other Zephyr CMake build systems by setting -# dedicated build variables. -# This CMake modules loads the sysbuild cache variables as target properties on -# a sysbuild_cache target. -# -# This ensures that quotes and lists are correctly preserved. +#[=======================================================================[.rst: +basic_settings +************** + +Setup basic settings for a Zephyr project. + +Basic settings are: + +* Sysbuild defined configuration settings + +Details for sysbuild settings: + +Sysbuild is a higher level build system used by Zephyr. +Sysbuild allows users to build multiple samples for a given system. + +For this to work, sysbuild manages other Zephyr CMake build systems by setting +dedicated build variables. +This CMake module loads the sysbuild cache variables as target properties on +a ``sysbuild_cache`` target. + +This ensures that quotes and lists are correctly preserved. + +#]=======================================================================] include_guard(GLOBAL) diff --git a/cmake/modules/boards.cmake b/cmake/modules/boards.cmake index 43515adca8c8..661cce1414cd 100644 --- a/cmake/modules/boards.cmake +++ b/cmake/modules/boards.cmake @@ -2,50 +2,77 @@ # # Copyright (c) 2021, Nordic Semiconductor ASA -# Validate board and setup boards target. -# -# This CMake module will validate the BOARD argument as well as splitting the -# BOARD argument into and . -# -# If a board implementation is not found for the specified board an error will -# be raised and list of valid boards will be printed. -# -# If user provided board is a board alias, the board will be adjusted to real -# board name. -# -# If board name is deprecated, then board will be adjusted to new board name and -# a deprecation warning will be printed to the user. -# -# Outcome: -# The following variables will be defined when this CMake module completes: -# -# - BOARD: Board, without revision field. -# - BOARD_REVISION: Board revision -# - BOARD_QUALIFIERS: Board qualifiers -# - NORMALIZED_BOARD_QUALIFIERS: Board qualifiers in lower-case format where slashes have been -# replaced with underscores -# - NORMALIZED_BOARD_TARGET: Board target in lower-case format where slashes have been -# replaced with underscores -# - BOARD_DIR: Board directory with the implementation for selected board -# - ARCH_DIR: Arch dir for extracted from selected board -# - BOARD_ROOT: BOARD_ROOT with ZEPHYR_BASE appended -# -# The following targets will be defined when this CMake module completes: -# - boards: when invoked, a list of valid boards will be printed -# -# Required variables: -# - BOARD: Board name, including any optional revision field, for example: `foo` or `foo@1.0.0` -# -# Optional variables: -# - BOARD_ROOT: CMake list of board roots containing board implementations -# - ARCH_ROOT: CMake list of arch roots containing arch implementations -# -# Optional environment variables: -# - ZEPHYR_BOARD_ALIASES: Environment setting pointing to a CMake file -# containing board aliases. -# -# Variables set by this module and not mentioned above are for internal -# use only, and may be removed, renamed, or re-purposed without prior notice. +#[=======================================================================[.rst: +boards +###### + +Validate board and setup boards target. + +This CMake module will validate the :cmake:variable:`BOARD` argument as well as splitting it into +```` and ````. + +If a board implementation is not found for the specified board an error will be raised and list of +valid boards will be printed. + +If user provided board is a board alias, the board will be adjusted to real board name. + +If board name is deprecated, then board will be adjusted to new board name and a deprecation warning +will be printed to the user. + +Required variables +****************** + +* :cmake:variable:`BOARD` + +Optional variables +****************** + +* :cmake:variable:`BOARD_ROOT` +* :cmake:variable:`ARCH_ROOT` + +Variables +********* + +The following variables will be defined when this CMake module completes: + +* :cmake:variable:`BOARD` +* :cmake:variable:`BOARD_REVISION` +* :cmake:variable:`BOARD_QUALIFIERS` +* :cmake:variable:`NORMALIZED_BOARD_QUALIFIERS` +* :cmake:variable:`NORMALIZED_BOARD_TARGET` +* :cmake:variable:`BOARD_DIR` +* :cmake:variable:`ARCH_DIR` +* :cmake:variable:`BOARD_ROOT` + +Targets +******** + +The following targets will be defined when this CMake module completes: + +* ``boards`` + + When invoked, a list of valid boards will be printed. + +Optional environment variables +****************************** + +:envvar:`ZEPHYR_BOARD_ALIASES` + + Environment setting pointing to a CMake file containing board aliases. + +Example usage +************* + +.. code-block:: cmake + + # BOARD is normally given on the command line, for example: + # west build -b nrf52840dk/nrf52840 + include(boards) + + message(STATUS "Building for ${BOARD} (${NORMALIZED_BOARD_TARGET})") + message(STATUS "Board files are in ${BOARD_DIR}") + +#]=======================================================================] include_guard(GLOBAL) diff --git a/cmake/modules/configuration_files.cmake b/cmake/modules/configuration_files.cmake index 8143b3432f09..e7b553ce8e7a 100644 --- a/cmake/modules/configuration_files.cmake +++ b/cmake/modules/configuration_files.cmake @@ -2,26 +2,31 @@ # # Copyright (c) 2021, Nordic Semiconductor ASA -# Zephyr build system configuration files. -# -# Locate the Kconfig and DT config files that are to be used. -# Also, locate the appropriate application config directory. -# -# Outcome: -# The following variables will be defined when this CMake module completes: -# -# - CONF_FILE: List of Kconfig fragments -# - EXTRA_CONF_FILE: List of additional Kconfig fragments -# - DTC_OVERLAY_FILE: List of devicetree overlay files -# - EXTRA_DTC_OVERLAY_FILE List of additional devicetree overlay files -# - DTS_EXTRA_CPPFLAGS List of additional devicetree preprocessor defines -# - APPLICATION_CONFIG_DIR: Root folder for application configuration -# -# If any of the above variables are already set when this CMake module is -# loaded, then no changes to the variable will happen. -# -# Variables set by this module and not mentioned above are considered internal -# use only and may be removed, renamed, or re-purposed without prior notice. +#[=======================================================================[.rst: +configuration_files +################### + +Locate the Kconfig and DT config files that are to be used. +Also, locate the appropriate application config directory. + +Variables +********* + +The following variables will be defined when this CMake module completes: + +* :cmake:variable:`CONF_FILE` +* :cmake:variable:`EXTRA_CONF_FILE` +* :cmake:variable:`DTC_OVERLAY_FILE` +* :cmake:variable:`EXTRA_DTC_OVERLAY_FILE` +* :cmake:variable:`DTS_EXTRA_CPPFLAGS` +* :cmake:variable:`APPLICATION_CONFIG_DIR` + +If any of the above variables are already set when this CMake module is +loaded, then no changes to the variable will happen. + +Variables set by this module and not mentioned above are considered internal +use only and may be removed, renamed, or re-purposed without prior notice. +#]=======================================================================] include_guard(GLOBAL) diff --git a/cmake/modules/doc.cmake b/cmake/modules/doc.cmake index daf16336405c..8fe3b081faf7 100644 --- a/cmake/modules/doc.cmake +++ b/cmake/modules/doc.cmake @@ -2,18 +2,23 @@ # # Copyright (c) 2021, Nordic Semiconductor ASA -# This CMake module will load all Zephyr CMake modules required for a -# documentation build. -# -# The following CMake modules will be loaded: -# - extensions -# - python -# - west -# - root -# - zephyr_module -# -# Outcome: -# The Zephyr package required for documentation build setup. +#[=======================================================================[.rst: +doc +### + +This CMake module will load all Zephyr CMake modules required for a documentation build. + +The following CMake modules will be loaded: + +* :cmake:module:`extensions` +* :cmake:module:`python` +* :cmake:module:`west` +* ``root`` +* ``zephyr_module`` + +Outcome: +The Zephyr package required for documentation build setup. +#]=======================================================================] include_guard(GLOBAL) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index a581da4c374c..4f740586ce7a 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -9,6 +9,32 @@ include(yaml) include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) + +#[=======================================================================[.rst: +extensions +########## + +Zephyr's CMake extension commands. + +This module defines the commands that Zephyr applications, Zephyr modules, and the build system +itself use to describe what they build. + +Many commands come in an ``_ifdef`` and an ``_ifndef`` flavour, taking a Kconfig option as their +first argument, so that build rules can be made conditional without wrapping them in an ``if()`` +block. + +This module is loaded as part of ``find_package(Zephyr)``, which means that every command documented +below is available in the :file:`CMakeLists.txt` of any Zephyr application or module. + +.. contents:: + :backlinks: entry + :local: + +#]=======================================================================] + + + + ######################################################## # Table of contents ######################################################## @@ -41,49 +67,69 @@ include(CheckCXXCompilerFlag) # 7.2 add_llext_* build control functions # 8. Script mode handling -######################################################## -# 1. Zephyr-aware extensions -######################################################## -# 1.1. zephyr_* -# -# The following methods are for modifying the CMake library[0] called -# "zephyr". zephyr is a catch-all CMake library for source files that -# can be built purely with the include paths, defines, and other -# compiler flags that all zephyr source files use. -# [0] https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html -# -# Example usage: -# zephyr_sources( -# random_esp32.c -# utils.c -# ) -# -# Is short for: -# target_sources(zephyr PRIVATE -# ${CMAKE_CURRENT_SOURCE_DIR}/random_esp32.c -# ${CMAKE_CURRENT_SOURCE_DIR}/utils.c -# ) -# -# As a very high-level introduction here are two call graphs that are -# purposely minimalistic and incomplete. -# -# zephyr_library_cc_option() -# | -# v -# zephyr_library_compile_options() --> target_compile_options() -# -# -# zephyr_cc_option() ---> target_cc_option() -# | -# v -# zephyr_cc_option_fallback() ---> target_cc_option_fallback() -# | -# v -# zephyr_compile_options() ---> target_compile_options() -# +#[=======================================================================[.rst: +Zephyr-aware extensions +*********************** + +``zephyr_*`` +============ + +The following methods are for modifying the `CMake library`_ called ``zephyr``. ``zephyr`` is a +catch-all CMake library for source files that can be built purely with the include paths, defines, +and other compiler flags that all zephyr source files use. + +.. _CMake library: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html + +Example usage: + +.. code-block:: cmake + + zephyr_sources( + random_esp32.c + utils.c + ) + +Is short for: + +.. code-block:: cmake + target_sources(zephyr PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/random_esp32.c + ${CMAKE_CURRENT_SOURCE_DIR}/utils.c + ) -# https://cmake.org/cmake/help/latest/command/target_sources.html +As a very high-level introduction, here are two call graphs that are +purposely minimalistic and incomplete. + +:: + + zephyr_library_cc_option() + | + v + zephyr_library_compile_options() --> target_compile_options() + +:: + + zephyr_cc_option() ---> target_cc_option() + | + v + zephyr_cc_option_fallback() ---> target_cc_option_fallback() + | + v + zephyr_compile_options() ---> target_compile_options() + +#]=======================================================================] + + +#[=======================================================================[.rst: +.. cmake:signature:: zephyr_sources(...) + + Add sources to the ``zephyr`` library. + + See :cmake:command:`target_sources ` for details. + + +#]=======================================================================] function(zephyr_sources) foreach(arg ${ARGV}) if(IS_DIRECTORY ${arg}) @@ -93,22 +139,46 @@ function(zephyr_sources) endforeach() endfunction() -# https://cmake.org/cmake/help/latest/command/target_include_directories.html +#[=======================================================================[.rst: +.. cmake:signature:: zephyr_include_directories(...) + + Add include directories to the ``zephyr`` library. + + See :cmake:command:`target_include_directories ` for details. +#]=======================================================================] function(zephyr_include_directories) target_include_directories(zephyr_interface INTERFACE ${ARGV}) endfunction() -# https://cmake.org/cmake/help/latest/command/target_include_directories.html +#[=======================================================================[.rst: +.. cmake:signature:: zephyr_system_include_directories(...) + + Add system include directories to the ``zephyr`` library. + + See :cmake:command:`target_include_directories ` for details. +#]=======================================================================] function(zephyr_system_include_directories) target_include_directories(zephyr_interface SYSTEM INTERFACE ${ARGV}) endfunction() -# https://cmake.org/cmake/help/latest/command/target_compile_definitions.html +#[=======================================================================[.rst: +.. cmake:signature:: zephyr_compile_definitions(...) + + Add compile definitions to the ``zephyr`` library. + + See :cmake:command:`target_compile_definitions ` for details. +#]=======================================================================] function(zephyr_compile_definitions) target_compile_definitions(zephyr_interface INTERFACE ${ARGV}) endfunction() -# https://cmake.org/cmake/help/latest/command/target_compile_options.html +#[=======================================================================[.rst: +.. cmake:signature:: zephyr_compile_options(...) + + Add compile options to the ``zephyr`` library. + + See :cmake:command:`target_compile_options ` for details. +#]=======================================================================] function(zephyr_compile_options) if(ARGV0 STREQUAL "PROPERTY") set(property $) @@ -127,7 +197,13 @@ function(zephyr_compile_options) endif() endfunction() -# https://cmake.org/cmake/help/latest/command/target_link_libraries.html +#[=======================================================================[.rst: +.. cmake:signature:: zephyr_link_libraries(...) + + Add link libraries to the ``zephyr`` library. + + See :cmake:command:`target_link_libraries ` for details. +#]=======================================================================] function(zephyr_link_libraries) if(ARGV0 STREQUAL "PROPERTY") set(property $) @@ -146,21 +222,58 @@ function(zephyr_link_libraries) endif() endfunction() +#[=======================================================================[.rst: + +.. cmake:signature:: zephyr_libc_link_libraries( ...) + + Add link libraries to the ``zephyr`` library's :cmake:prop_tgt:`LIBC_LINK_LIBRARIES` property. + + This function allows subsystems to define libraries which get added to the + link command after all other libraries and modules. It's useful when using + a toolchain library, like libc or libgcc, as those can get added when + processing the 'lib' directory, before any module libraries and hence might + not get used to resolve symbols from modules. +#]=======================================================================] function(zephyr_libc_link_libraries) set_property(TARGET zephyr_interface APPEND PROPERTY LIBC_LINK_LIBRARIES ${ARGV}) endfunction() -# See this file section 3.1. target_cc_option +#[=======================================================================[.rst: + +.. cmake:signature:: zephyr_cc_option(