From 4d042d31032715e89299c6d52983665d213d060a Mon Sep 17 00:00:00 2001 From: Aaron Wisner Date: Wed, 29 Jul 2026 07:03:35 +0000 Subject: [PATCH 001/600] kernel: add configurable striped semaphore locking Replace the system-wide semaphore spinlock with a configurable array of naturally aligned lock stripes. Hash each semaphore by its natural alignment without changing the semaphore object ABI. Default CONFIG_SEM_LOCK_STRIPES to one so SMP and uniprocessor builds preserve existing global-lock behavior and static memory use. Use a scalar lock unless SMP striping is explicitly enabled, so UP builds retain zero-sized spinlocks and avoid non-portable empty-struct arrays. Require a power-of-two stripe count so the compiler reduces stripe selection to a bitmask. Divide by the alignment of struct k_sem before hashing to discard redundant address bits. Do not impose cache-line alignment or padding. Explain how spinlock and cache-line sizes affect the stripe count needed to reduce cache contention. Validate upstream semaphore regressions with zero-sized uniprocessor spinlocks and SMP configurations using one, 16, and 32 stripes. Reject non-power-of-two stripe counts at compile time. An internal throughput microbenchmark on a quad-core ARM Cortex-A53 SMP system observed an improvement of approximately 3.1%. Assisted-by: Codex:GPT-5 Signed-off-by: Aaron Wisner --- kernel/Kconfig | 16 +++++++++++++++ kernel/sem.c | 54 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/kernel/Kconfig b/kernel/Kconfig index 4f9911205075..fd23d09c93da 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -351,6 +351,22 @@ config WAITQ_SIMPLE endchoice # WAITQ_ALGORITHM +config SEM_LOCK_STRIPES + int "Number of semaphore spinlock stripes" if SMP + default 1 + range 1 256 + help + Number of spinlocks used to synchronize semaphore operations. This + value must be a power of two. The default is one system-wide lock, + which preserves the existing semaphore behavior and memory footprint. + Non-SMP systems always use one lock. + + Additional stripes reduce serialization and hash collisions on SMP + systems at the cost of additional static memory. Stripes are not + individually cache-line aligned. With 4-byte spinlocks and a 64-byte + cache line, 16 stripes fit within a single cache line. Increasing the + count beyond 16 can also reduce cache-line contention. + menu "Misc Kernel related options" config CURRENT_THREAD_USE_NO_TLS diff --git a/kernel/sem.c b/kernel/sem.c index 832de2fb358b..274555773904 100644 --- a/kernel/sem.c +++ b/kernel/sem.c @@ -29,14 +29,35 @@ #include #include -/* We use a system-wide lock to synchronize semaphores, which has - * unfortunate performance impact vs. using a per-object lock - * (semaphores are *very* widely used). But per-object locks require - * significant extra RAM. A properly spin-aware semaphore +/* Optional striped locks reduce the performance impact of a system-wide + * semaphore lock without the significant extra RAM required by per-object + * locks (semaphores are *very* widely used). A properly spin-aware semaphore * implementation would spin on atomic access to the count variable, - * and not a spinlock per se. Useful optimization for the future... + * and not a spinlock per se. Useful optimization for the future... */ +/* A k_spinlock can be zero-sized on uniprocessor configurations. Keep the + * default lock scalar because arrays of empty structures are not portable. + */ +#if defined(CONFIG_SMP) && (CONFIG_SEM_LOCK_STRIPES > 1) +static struct k_spinlock sem_locks[CONFIG_SEM_LOCK_STRIPES]; +#else static struct k_spinlock sem_lock; +#endif + +static inline struct k_spinlock *sem_spinlock_get(struct k_sem *sem) +{ +#if defined(CONFIG_SMP) && (CONFIG_SEM_LOCK_STRIPES > 1) + BUILD_ASSERT(IS_POWER_OF_TWO(CONFIG_SEM_LOCK_STRIPES), + "CONFIG_SEM_LOCK_STRIPES must be a power of two"); + + /* Hash the naturally aligned semaphore address into a lock stripe. */ + return &sem_locks[((uintptr_t)sem / __alignof(struct k_sem)) % + CONFIG_SEM_LOCK_STRIPES]; +#else + ARG_UNUSED(sem); + return &sem_lock; +#endif +} #ifdef CONFIG_OBJ_CORE_SEM static struct k_obj_type obj_type_sem; @@ -94,7 +115,8 @@ static inline bool sem_handle_poll_events(struct k_sem *sem) void z_impl_k_sem_give(struct k_sem *sem) { - k_spinlock_key_t key = k_spin_lock(&sem_lock); + struct k_spinlock *lock = sem_spinlock_get(sem); + k_spinlock_key_t key = k_spin_lock(lock); bool resched; SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_sem, give, sem); @@ -107,9 +129,9 @@ void z_impl_k_sem_give(struct k_sem *sem) } if (unlikely(resched)) { - z_reschedule(&sem_lock, key); + z_reschedule(lock, key); } else { - k_spin_unlock(&sem_lock, key); + k_spin_unlock(lock, key); } SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_sem, give, sem); @@ -126,31 +148,32 @@ static inline void z_vrfy_k_sem_give(struct k_sem *sem) int z_impl_k_sem_take(struct k_sem *sem, k_timeout_t timeout) { + struct k_spinlock *lock = sem_spinlock_get(sem); int ret; __ASSERT(((arch_is_in_isr() == false) || K_TIMEOUT_EQ(timeout, K_NO_WAIT)), ""); - k_spinlock_key_t key = k_spin_lock(&sem_lock); + k_spinlock_key_t key = k_spin_lock(lock); SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_sem, take, sem, timeout); if (likely(sem->count > 0U)) { sem->count--; - k_spin_unlock(&sem_lock, key); + k_spin_unlock(lock, key); ret = 0; goto out; } if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { - k_spin_unlock(&sem_lock, key); + k_spin_unlock(lock, key); ret = -EBUSY; goto out; } SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_sem, take, sem, timeout); - ret = z_pend_curr(&sem_lock, key, &sem->wait_q, timeout); + ret = z_pend_curr(lock, key, &sem->wait_q, timeout); out: SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_sem, take, sem, timeout, ret); @@ -160,7 +183,8 @@ int z_impl_k_sem_take(struct k_sem *sem, k_timeout_t timeout) void z_impl_k_sem_reset(struct k_sem *sem) { - k_spinlock_key_t key = k_spin_lock(&sem_lock); + struct k_spinlock *lock = sem_spinlock_get(sem); + k_spinlock_key_t key = k_spin_lock(lock); bool resched = false; while (z_sched_wake(&sem->wait_q, -EAGAIN, NULL)) { @@ -173,9 +197,9 @@ void z_impl_k_sem_reset(struct k_sem *sem) resched = sem_handle_poll_events(sem) || resched; if (resched) { - z_reschedule(&sem_lock, key); + z_reschedule(lock, key); } else { - k_spin_unlock(&sem_lock, key); + k_spin_unlock(lock, key); } } From 58a459a991adf41095710b5287a7667256b1ccb2 Mon Sep 17 00:00:00 2001 From: Lior David Date: Sat, 8 Aug 2026 17:26:33 +0200 Subject: [PATCH 002/600] arch/riscv: align TLS storage to ARCH_STACK_PTR_ALIGN The TLS storage is stored in a reserved stack area, so it needs to be aligned with ARCH_STACK_PTR_ALIGN (16 bytes), otherwise the stack pointer gets out of alignment which violates the RISCV psABI specification. In addition, the Zcmp instructions such as cm.push and cm.pop will have undefined behavior if called with unaligned stack pointer. This can cause unexpected runtime failures. Signed-off-by: Lior David --- arch/riscv/core/tls.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/riscv/core/tls.c b/arch/riscv/core/tls.c index 76708b8adf91..81631527d475 100644 --- a/arch/riscv/core/tls.c +++ b/arch/riscv/core/tls.c @@ -11,10 +11,15 @@ #include #include +static inline FUNC_NO_STACK_PROTECTOR size_t z_tls_data_size_aligned(void) +{ + return ROUND_UP(z_tls_data_size(), ARCH_STACK_PTR_ALIGN); +} + /* Non-inline wrapper for z_tls_data_size(), required for calling from assembly. */ size_t FUNC_NO_STACK_PROTECTOR z_tls_data_size_asm(void) { - return z_tls_data_size(); + return z_tls_data_size_aligned(); } size_t arch_tls_stack_setup(struct k_thread *new_thread, char *stack_ptr) @@ -27,7 +32,7 @@ size_t arch_tls_stack_setup(struct k_thread *new_thread, char *stack_ptr) * Since we are populating things backwards, setup the TLS data/bss * area first. */ - stack_ptr -= z_tls_data_size(); + stack_ptr -= z_tls_data_size_aligned(); z_tls_copy(stack_ptr); /* From 21a706a2eaea134e95b3c989e92d56a1adef5ee9 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 19 Aug 2026 09:43:02 +0000 Subject: [PATCH 003/600] doc: migration-guide: add a note about ignore_faults Downstream test may start failing after this, add a note about it, the PR has plenty of examples. Link: https://github.com/zephyrproject-rtos/zephyr/pull/116359 Signed-off-by: Fabio Baltieri --- doc/releases/migration-guide-4.5.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index e263384cc745..5c3aedea7c35 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -1969,3 +1969,10 @@ Video ``uint16_t *idx`` output parameter but instead returns a pointer to the imported :c:struct:`video_buffer`, or ``NULL`` on failure. This helps to make the index transparent to the application and also makes the buffer accessible from the application. + +Twister +======= + +* Faults after tests have passed are now explicitly detected and fail the whole + testsuite, if a test produces a fault on purpose then the corresponding test + case has to be marked with ``ignore_faults: true`` (:github:`116359`). From 5e34e0a3b89d508c9830d944f05afd1490efbeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Wed, 19 Aug 2026 14:02:54 +0200 Subject: [PATCH 004/600] twister: runner: Relax platform filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expect that string provided in platform argument is part of the target platform instead of an exact name match. This relaxed setting allows to handle cases like: - unify argument for multiple boards with the same SoC - handle multiple versions of the same board It also matches handling in the west build command implementation. Signed-off-by: Krzysztof Chruściński --- scripts/pylib/twister/twisterlib/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pylib/twister/twisterlib/runner.py b/scripts/pylib/twister/twisterlib/runner.py index 01a5270dffcc..590aaf1ad6c3 100644 --- a/scripts/pylib/twister/twisterlib/runner.py +++ b/scripts/pylib/twister/twisterlib/runner.py @@ -1545,7 +1545,7 @@ def cmake(self, filter_stages=None): if self.instance.platform.arch == cond_args[1]: args.append(cond_args[2]) elif cond_args[0] == "platform" and len(cond_args) == 3: - if self.instance.platform.name == cond_args[1]: + if cond_args[1] in self.instance.platform.name: args.append(cond_args[2]) elif cond_args[0] == "simulation" and len(cond_args) == 3: if self.instance.platform.simulation == cond_args[1]: From 90fd710d1f75fb6744f968b6a70121cf60ec0e21 Mon Sep 17 00:00:00 2001 From: Sergei Ovchinnikov Date: Thu, 2 Jul 2026 10:55:18 +0200 Subject: [PATCH 005/600] drivers: gpio: npm10xx: use mfd configuration function Centralise the GPIO arbitration and remove duplication. Add initial value setting based on provided flags. Signed-off-by: Sergei Ovchinnikov --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig.npm10xx | 5 ++-- drivers/gpio/gpio_npm10xx.c | 46 ++++++++++++++---------------------- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 347d5bf9fea4..025ab29a61d5 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -5,6 +5,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/gpio.h) zephyr_library() # zephyr-keep-sorted-start +zephyr_library_include_directories_ifdef(CONFIG_GPIO_NPM10XX ${ZEPHYR_BASE}/drivers/mfd) zephyr_library_sources_ifdef(CONFIG_GPIO_AD559X gpio_ad559x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_ADP5585 gpio_adp5585.c) zephyr_library_sources_ifdef(CONFIG_GPIO_ADS1X4S0X gpio_ads1x4s0x.c) diff --git a/drivers/gpio/Kconfig.npm10xx b/drivers/gpio/Kconfig.npm10xx index 2f4749a6805e..b0e8199634ce 100644 --- a/drivers/gpio/Kconfig.npm10xx +++ b/drivers/gpio/Kconfig.npm10xx @@ -4,7 +4,8 @@ config GPIO_NPM10XX bool "nPM10xx GPIO driver" default y - depends on DT_HAS_NORDIC_NPM10XX_GPIO_ENABLED + depends on DT_HAS_NORDIC_NPM10XX_GPIO_ENABLED && DT_HAS_NORDIC_NPM10XX_ENABLED + select MFD select I2C help Enable the nPM10xx GPIO driver. @@ -15,4 +16,4 @@ config GPIO_NPM10XX_INIT_PRIORITY default 85 help Initialization priority for the nPM10xx GPIO driver. It must be - greater than the I2C controller init priority. + greater than the MFD init priority. diff --git a/drivers/gpio/gpio_npm10xx.c b/drivers/gpio/gpio_npm10xx.c index 5d634a77f1a8..3712a5be90f6 100644 --- a/drivers/gpio/gpio_npm10xx.c +++ b/drivers/gpio/gpio_npm10xx.c @@ -11,29 +11,17 @@ #include #include -LOG_MODULE_REGISTER(gpio_npm10xx, CONFIG_GPIO_LOG_LEVEL); +#include "mfd_npm10xx.h" -#define NPM10XX_GPIO_PINS 3U +LOG_MODULE_REGISTER(gpio_npm10xx, CONFIG_GPIO_LOG_LEVEL); /* Register Offsets */ -#define NPM10_GPIO_CONFIG0 0xA0U #define NPM10_GPIO_OUTPUT0 0xA6U #define NPM10_GPIO_READ 0xACU -/* CONFIGx (0xA0–0xA2) */ -#define GPIO_CONFIG_INPUT BIT(0) -#define GPIO_CONFIG_OUTPUT BIT(1) -#define GPIO_CONFIG_OPENDRAIN BIT(2) -#define GPIO_CONFIG_PULLDOWN BIT(3) -#define GPIO_CONFIG_PULLUP BIT(4) -#define GPIO_CONFIG_DRIVE BIT(5) -#define GPIO_CONFIG_DEBOUNCE BIT(6) - -/* USAGEx (0xA3–0xA5) */ -#define GPIO_USAGE_POL_INVERT BIT(4) - struct gpio_npm10xx_config { struct gpio_driver_config common; + const struct device *mfd; struct i2c_dt_spec i2c; }; @@ -41,29 +29,30 @@ struct gpio_npm10xx_data { struct gpio_driver_data common; }; +int gpio_npm10xx_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, + gpio_port_value_t value); + int gpio_npm10xx_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { const struct gpio_npm10xx_config *config = dev->config; - uint8_t conf; + int ret; if (k_is_in_isr()) { return -EWOULDBLOCK; } - if (pin >= NPM10XX_GPIO_PINS) { - LOG_ERR("pin number out of range %d", pin); - return -EINVAL; + ret = mfd_npm10xx_pin_configure(config->mfd, pin, NPM10_PIN_GPIO, flags); + if (ret < 0) { + LOG_ERR("Failed to configure pin %u for GPIO usage", pin); + return ret; } - conf = FIELD_PREP(GPIO_CONFIG_INPUT, !!(flags & GPIO_INPUT)) | - FIELD_PREP(GPIO_CONFIG_OUTPUT, !!(flags & GPIO_OUTPUT)) | - FIELD_PREP(GPIO_CONFIG_OPENDRAIN, (flags & GPIO_OPEN_DRAIN) == GPIO_OPEN_DRAIN) | - FIELD_PREP(GPIO_CONFIG_PULLDOWN, !!(flags & GPIO_PULL_DOWN)) | - FIELD_PREP(GPIO_CONFIG_PULLUP, !!(flags & GPIO_PULL_UP)) | - FIELD_PREP(GPIO_CONFIG_DRIVE, !!(flags & NPM10XX_GPIO_DRIVE_HIGH)) | - FIELD_PREP(GPIO_CONFIG_DEBOUNCE, !!(flags & NPM10XX_GPIO_DEBOUNCE_ON)); + if (flags & (GPIO_OUTPUT_INIT_LOW | GPIO_OUTPUT_INIT_HIGH)) { + return gpio_npm10xx_port_set_masked_raw( + dev, BIT(pin), flags & GPIO_OUTPUT_INIT_HIGH ? BIT(pin) : 0U); + } - return i2c_reg_write_byte_dt(&config->i2c, NPM10_GPIO_CONFIG0 + pin, conf); + return 0; } int gpio_npm10xx_port_get_raw(const struct device *dev, gpio_port_value_t *value) @@ -83,7 +72,7 @@ int gpio_npm10xx_port_set_masked_raw(const struct device *dev, gpio_port_pins_t const struct gpio_npm10xx_config *config = dev->config; int ret; - for (size_t i = 0; i < NPM10XX_GPIO_PINS; i++) { + for (size_t i = 0; i < NPM10_PIN_NUM; i++) { if (IS_BIT_SET(mask, i)) { ret = i2c_reg_write_byte_dt(&config->i2c, NPM10_GPIO_OUTPUT0 + i, IS_BIT_SET(value, i)); @@ -143,6 +132,7 @@ static DEVICE_API(gpio, gpio_npm10xx_api) = { #define GPIO_NPM10XX_DEFINE(n) \ static const struct gpio_npm10xx_config gpio_npm10xx_config##n = { \ .common = GPIO_COMMON_CONFIG_FROM_DT_INST(n), \ + .mfd = DEVICE_DT_GET(DT_INST_PARENT(n)), \ .i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(n)), \ }; \ \ From 1086a51931c923e6d87ddfa4b61ba33c7bb3eb49 Mon Sep 17 00:00:00 2001 From: Sergei Ovchinnikov Date: Fri, 3 Jul 2026 10:34:37 +0200 Subject: [PATCH 006/600] tests: drivers: build_all: gpio: add compatible to npm1012 node The GPIO driver now depends on MFD, the test_i2c_npm1012 parent node needs its compatible specified to enable its driver Signed-off-by: Sergei Ovchinnikov --- tests/drivers/build_all/gpio/app.overlay | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/drivers/build_all/gpio/app.overlay b/tests/drivers/build_all/gpio/app.overlay index 59befdd212c5..ebd651dbbdc4 100644 --- a/tests/drivers/build_all/gpio/app.overlay +++ b/tests/drivers/build_all/gpio/app.overlay @@ -494,6 +494,7 @@ test_i2c_npm1012: pmic@1f { reg = <0x1f>; + compatible = "nordic,npm10xx"; npm1012_gpio: gpio-controller { compatible = "nordic,npm10xx-gpio"; From 9eb485f6c509125bb1e94ffa3a80d4b88c6ee2e3 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 24 Aug 2026 13:51:52 -0400 Subject: [PATCH 007/600] drivers: timer: separate the arm reach from the unannounced span TIMER_CORE_MAX_UNANNOUNCED_CYCLES was the smaller of two unrelated limits: what the counter can still resolve, and what the alarm can express. The tick clamp divides by it, so where a tick is longer than the alarm can hold, it comes out zero and the arm path collapses: span = 0 /* clamped */ want = 0 * CYC_PER_TICK = 0 rel = (want > done) ? want - done : 0 /* 0 */ rel < ALARM_MIN /* floored */ set_reload(ALARM_MIN) On frdm_mcxn947 at 150 MHz with CONFIG_SYS_CLOCK_TICKS_PER_SEC=1 a tick is 150000000 cycles against a 24-bit SysTick reload of 16777215, so the driver arms 1499 cycles, the 10 us floor, and interrupts 78000 times a second announcing nothing. Asking for one tick a second then costs more interrupts than any other tick rate. Only the counter's reach bounds how much unannounced time may accumulate. The alarm's reach bounds a single arm. Split them: MAX_UNANNOUNCED_CYCLES = COUNTER_SAFE_SPAN MAX_ARM_CYCLES = MIN(COUNTER_SAFE_SPAN, ALARM_MAX_CYCLES) The tick clamp uses the first, so a tick always fits, and each arm is capped at the second. That board now spans 14 ticks and arms 16777215 cycles at a time, nine interrupts to cross one tick, which is what the driver already did for a timeout longer than its reload. The cap is guarded on the two bounds differing, so it folds away where the alarm covers the whole span and the span clamp has already bounded the value. Fixes: #117169 Signed-off-by: Nicolas Pitre --- drivers/timer/system_timer_generic.h | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/timer/system_timer_generic.h b/drivers/timer/system_timer_generic.h index 886f97957bb8..a280593ec2df 100644 --- a/drivers/timer/system_timer_generic.h +++ b/drivers/timer/system_timer_generic.h @@ -275,10 +275,18 @@ typedef uint64_t timer_core_cycles_t; #endif /* - * Furthest ahead of the last announce that the core will arm: what the counter - * can still resolve, or what the alarm can express, whichever binds first. + * Furthest ahead of the last announce that unannounced time may run: what the + * counter can still resolve. Nothing else bounds it, the alarm's reach being a + * bound on one arm rather than on the total. */ -#define TIMER_CORE_MAX_UNANNOUNCED_CYCLES \ +#define TIMER_CORE_MAX_UNANNOUNCED_CYCLES TIMER_CORE_COUNTER_SAFE_SPAN + +/* + * Furthest one arm reaches: what the alarm can express, never past what the + * counter can resolve. A deadline beyond this is walked to over several arms, + * each announcing nothing until the last. + */ +#define TIMER_CORE_MAX_ARM_CYCLES \ ((timer_core_cycles_t)MIN((uint64_t)TIMER_CORE_COUNTER_SAFE_SPAN, \ (uint64_t)TIMER_CORE_ALARM_MAX_CYCLES)) @@ -536,6 +544,15 @@ static void timer_core_arm(uint32_t ticks) */ timer_core_cycles_t rel = (want > done) ? (want - done) : 0; + /* Only where the alarm binds before the counter does; the span clamp + * above already holds `rel` inside the counter's reach, so this folds + * away for hardware whose alarm covers the whole span. + */ + if ((TIMER_CORE_MAX_ARM_CYCLES < TIMER_CORE_MAX_UNANNOUNCED_CYCLES) && + (rel > TIMER_CORE_MAX_ARM_CYCLES)) { + rel = TIMER_CORE_MAX_ARM_CYCLES; + } + if (rel < TIMER_CORE_ALARM_MIN_CYCLES) { /* * The announce is due (or overdue): fire as soon as the floor @@ -577,8 +594,13 @@ static void timer_core_arm(uint32_t ticks) if ((ticks <= span) && (timer_core_last_elapsed <= (span - ticks))) { span = timer_core_last_elapsed + ticks; } - timer_core_set_compare(timer_core_last_cycle + - (timer_core_cycles_t)span * TIMER_CORE_CYC_PER_TICK); + timer_core_cycles_t offset = (timer_core_cycles_t)span * TIMER_CORE_CYC_PER_TICK; + + if ((TIMER_CORE_MAX_ARM_CYCLES < TIMER_CORE_MAX_UNANNOUNCED_CYCLES) && + (offset > TIMER_CORE_MAX_ARM_CYCLES)) { + offset = TIMER_CORE_MAX_ARM_CYCLES; + } + timer_core_set_compare(timer_core_last_cycle + offset); #else /* Nothing to narrow to: the span and the baseline are the same width, so * form the deadline directly and let the clamp subtract the baseline off. @@ -586,8 +608,8 @@ static void timer_core_arm(uint32_t ticks) uint64_t deadline = (timer_core_last_tick + timer_core_last_elapsed + ticks) * TIMER_CORE_CYC_PER_TICK; - if ((deadline - timer_core_last_cycle) > TIMER_CORE_MAX_UNANNOUNCED_CYCLES) { - deadline = timer_core_last_cycle + TIMER_CORE_MAX_UNANNOUNCED_CYCLES; + if ((deadline - timer_core_last_cycle) > TIMER_CORE_MAX_ARM_CYCLES) { + deadline = timer_core_last_cycle + TIMER_CORE_MAX_ARM_CYCLES; } timer_core_set_compare(deadline); #endif From 00867e136c36ee319c7bd4ba7f3d34e69c9ec858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 25 Aug 2026 11:48:30 +0200 Subject: [PATCH 008/600] cmake: armclang: use the target CPU for compiler capability probes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMake stopped adding -mcpu itself once CMP0123 became NEW, and Zephyr only passes it through TOOLCHAIN_C_FLAGS, which does not reach try_compile. Compiler capability probes were therefore evaluated against armclang's default CPU rather than the one being built for. Add -mcpu to CMAKE_REQUIRED_FLAGS so probe results match the target. Assisted-by: Claude:opus-5 Signed-off-by: Benjamin Cabé --- cmake/compiler/armclang/target.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/compiler/armclang/target.cmake b/cmake/compiler/armclang/target.cmake index def9778ccb87..fd2a5b5f157c 100644 --- a/cmake/compiler/armclang/target.cmake +++ b/cmake/compiler/armclang/target.cmake @@ -59,7 +59,7 @@ foreach(isystem_include_dir ${NOSTDINC}) list(APPEND isystem_include_flags -isystem ${isystem_include_dir}) endforeach() -set(CMAKE_REQUIRED_FLAGS ${isystem_include_flags}) +set(CMAKE_REQUIRED_FLAGS -mcpu=${GCC_M_CPU} ${isystem_include_flags}) string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") if(CONFIG_ARMCLANG_STD_LIBC) From a270495489e9289088948053ad251f8078884bb0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 25 Aug 2026 16:06:11 +0300 Subject: [PATCH 009/600] coredump: intel_adsp_mem_window: clamp writes to the debug slot size coredump_mem_window_backend_buffer_output() only rejected a write that starts at or past the end of the slot; it never clamped buflen itself. A chunk that pushes mem_wptr from just below the ADSP_DW_SLOT_SIZE - 4 limit to past it therefore overruns the current debug window slot and spills into whatever slot is physically adjacent in memory. With the static slot layout (CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER=n) this overrun landed in unused space past the last real slot, so it went unnoticed. With the dynamic debug slot manager enabled, the telemetry/coredump slot can end up placed right before another consumer's slot (e.g. the mtrace log ring buffer), and the same overrun corrupts that live slot instead. Clamp buflen to the remaining space in the slot before copying so a write can never cross into the next slot. Signed-off-by: Peter Ujfalusi --- .../debug/coredump/coredump_backend_intel_adsp_mem_window.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c b/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c index 17bf3461037b..1ed5cd5b3dbe 100644 --- a/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c +++ b/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c @@ -87,6 +87,11 @@ static void coredump_mem_window_backend_buffer_output(uint8_t *buf, size_t bufle return; } + /* Clamp to the remaining space: writing past the slot boundary would + * spill into whatever debug window slot physically follows this one. + */ + buflen = MIN(buflen, ADSP_DW_SLOT_SIZE - 4 - mem_wptr); + if (buf) { for (data_left = buflen; data_left > 0; data_left--) { *mem_window_sink = *coredump_data; From ddea58c9940faf645821ad871661898b4bab06be Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 25 Aug 2026 16:06:31 +0300 Subject: [PATCH 010/600] coredump: intel_adsp_mem_window: reuse the telemetry slot if one exists coredump_mem_window_backend_start() unconditionally force-seized debug slot 1 for ADSP_DW_SLOT_TELEMETRY, even when telemetry_init() had already been assigned a different slot by the dynamic debug slot manager. This could leave two descriptors of type ADSP_DW_SLOT_TELEMETRY around, with the host possibly reading the stale one instead of the slot the coredump was actually written to. Try adsp_dw_request_slot() first so the coredump reuses the slot telemetry already owns, if any, and only fall back to forcibly seizing slot 1 (then slot 0) when no telemetry slot exists yet. Signed-off-by: Peter Ujfalusi --- .../coredump/coredump_backend_intel_adsp_mem_window.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c b/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c index 1ed5cd5b3dbe..3a6edd015eb6 100644 --- a/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c +++ b/subsys/debug/coredump/coredump_backend_intel_adsp_mem_window.c @@ -32,8 +32,14 @@ static void coredump_mem_window_backend_start(void) #ifdef CONFIG_INTEL_ADSP_DEBUG_SLOT_MANAGER struct adsp_dw_desc slot_desc = { .type = ADSP_DW_SLOT_TELEMETRY, }; - /* Forcibly take debug slot 1 */ - coredump_slot_addr = adsp_dw_seize_slot(1, &slot_desc, NULL); + /* Reuse the telemetry slot if one is already assigned, avoiding a second, + * duplicate ADSP_DW_SLOT_TELEMETRY descriptor pointing elsewhere. + */ + coredump_slot_addr = adsp_dw_request_slot(&slot_desc, NULL); + if (!coredump_slot_addr) { + /* No telemetry slot exists yet, forcibly take debug slot 1 */ + coredump_slot_addr = adsp_dw_seize_slot(1, &slot_desc, NULL); + } if (!coredump_slot_addr) { /* Try to get the first slot if slot 1 is not available as fallback */ coredump_slot_addr = adsp_dw_seize_slot(0, &slot_desc, NULL); From a84f4e5773aecdc807115e900ee18eb198db7968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 25 Aug 2026 18:36:39 +0200 Subject: [PATCH 011/600] ztest: remove deprecated shuffle repeat count options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove ZTEST_SHUFFLE_SUITE_REPEAT_COUNT and ZTEST_SHUFFLE_TEST_REPEAT_COUNT, deprecated in Zephyr 4.0 when ZTEST_REPEAT introduced the generic ZTEST_SUITE_REPEAT_COUNT and ZTEST_TEST_REPEAT_COUNT options. ZTEST_SHUFFLE now only controls ordering: a shuffled run executes each suite and test once unless ZTEST_REPEAT is enabled, whereas the removed options used to imply three iterations of each. No in-tree test sets either option. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- doc/releases/migration-guide-4.5.rst | 7 +++++++ doc/releases/release-notes-4.5.rst | 5 +++++ subsys/testsuite/ztest/Kconfig | 17 ----------------- subsys/testsuite/ztest/src/ztest.c | 6 ------ 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/doc/releases/migration-guide-4.5.rst b/doc/releases/migration-guide-4.5.rst index 5c3aedea7c35..b767f481daa6 100644 --- a/doc/releases/migration-guide-4.5.rst +++ b/doc/releases/migration-guide-4.5.rst @@ -1743,6 +1743,13 @@ Other subsystems ZTEST_BENCHMARK(suite, my_bench, 100, setup, teardown) { /* ... */ } ZTEST_BENCHMARK_TIMED(suite, my_bench, 1000, setup, teardown) { /* ... */ } +* The ``CONFIG_ZTEST_SHUFFLE_SUITE_REPEAT_COUNT`` and ``CONFIG_ZTEST_SHUFFLE_TEST_REPEAT_COUNT`` + Kconfig options, deprecated since Zephyr 4.0, have been removed. With + :kconfig:option:`CONFIG_ZTEST_SHUFFLE` alone, suites and tests now run once per execution, in a + shuffled order; to repeat them, enable :kconfig:option:`CONFIG_ZTEST_REPEAT` and set + :kconfig:option:`CONFIG_ZTEST_SUITE_REPEAT_COUNT` and + :kconfig:option:`CONFIG_ZTEST_TEST_REPEAT_COUNT`. + * The CPU load metric module has been merged into the unified :ref:`cpu_load` module. The :kconfig:option:`CONFIG_CPU_LOAD_METRIC` option is deprecated; enable :kconfig:option:`CONFIG_CPU_LOAD` with the :kconfig:option:`CONFIG_CPU_LOAD_BACKEND_RUNTIME_STATS` diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index b165232c4fec..e55c7cc4fd4f 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -243,6 +243,11 @@ Removed APIs and options * ``stream_flash_erase_page()`` +* ZTest + + * ``CONFIG_ZTEST_SHUFFLE_SUITE_REPEAT_COUNT`` + * ``CONFIG_ZTEST_SHUFFLE_TEST_REPEAT_COUNT`` + * West sign support for imgtool, which was deprecated in Zephyr 4.0, has been removed. * The ``scripts/logging/dictionary/log_parser_uart.py`` dictionary logging script, which was diff --git a/subsys/testsuite/ztest/Kconfig b/subsys/testsuite/ztest/Kconfig index 2be312bf43a6..a64d24fb4567 100644 --- a/subsys/testsuite/ztest/Kconfig +++ b/subsys/testsuite/ztest/Kconfig @@ -170,23 +170,6 @@ config ZTEST_SHUFFLE help This rule will shuffle the order of tests and test suites. -if ZTEST_SHUFFLE -config ZTEST_SHUFFLE_SUITE_REPEAT_COUNT - int "[DEPRECATED] Number of iterations the test suite will run" - default 3 - help - This is used to execute a test suite N number of times. - [DEPRECATED] use ZTEST_SUITE_REPEAT_COUNT instead. - -config ZTEST_SHUFFLE_TEST_REPEAT_COUNT - int "[DEPRECATED] Number of iterations the test will run" - default 3 - help - This is used to execute a test case N number of times. - [DEPRECATED] use ZTEST_TEST_REPEAT_COUNT instead. - -endif #ZTEST_SHUFFLE - config ZTEST_REPEAT bool "Repeat the tests and suites" help diff --git a/subsys/testsuite/ztest/src/ztest.c b/subsys/testsuite/ztest/src/ztest.c index c4a5142fa0ec..587abb8d1c4c 100644 --- a/subsys/testsuite/ztest/src/ztest.c +++ b/subsys/testsuite/ztest/src/ztest.c @@ -30,21 +30,15 @@ static bool failed_expectation; #ifdef CONFIG_ZTEST_SHUFFLE #include #include -#ifndef CONFIG_ZTEST_REPEAT -#define NUM_ITER_PER_SUITE CONFIG_ZTEST_SHUFFLE_SUITE_REPEAT_COUNT -#define NUM_ITER_PER_TEST CONFIG_ZTEST_SHUFFLE_TEST_REPEAT_COUNT -#endif #endif /* CONFIG_ZTEST_SHUFFLE */ #ifdef CONFIG_ZTEST_REPEAT #define NUM_ITER_PER_SUITE CONFIG_ZTEST_SUITE_REPEAT_COUNT #define NUM_ITER_PER_TEST CONFIG_ZTEST_TEST_REPEAT_COUNT #else -#ifndef CONFIG_ZTEST_SHUFFLE #define NUM_ITER_PER_SUITE 1 #define NUM_ITER_PER_TEST 1 #endif -#endif #ifdef CONFIG_ZTEST_COVERAGE_RESET_BEFORE_TESTS #include From 1da4cec0e3cce9f0a3f387cb2afa5c2e2a1ec748 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Tue, 25 Aug 2026 08:10:57 +0200 Subject: [PATCH 012/600] doc: threads: fix typo While reading through the documentation of threads, I cleaned up this typo I noticed along the way. Signed-off-by: Remo Senekowitsch --- doc/kernel/services/threads/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/kernel/services/threads/index.rst b/doc/kernel/services/threads/index.rst index 294f73302967..308dce395a49 100644 --- a/doc/kernel/services/threads/index.rst +++ b/doc/kernel/services/threads/index.rst @@ -518,7 +518,7 @@ The size parameter for the stack must be one of three values: ``K_THREAD_STACK`` or ``K_KERNEL_STACK`` family of stack instantiation macros. - For a stack object defined with the ``K_THREAD_STACK`` family of - macros, the return value of :c:macro:`K_THREAD_STACK_SIZEOF()` for that' + macros, the return value of :c:macro:`K_THREAD_STACK_SIZEOF()` for that object. - For a stack object defined with the ``K_KERNEL_STACK`` family of macros, the return value of :c:macro:`K_KERNEL_STACK_SIZEOF()` for that From 86c1cfba0dd06e4edec83839e24d6d2aecb3b4a3 Mon Sep 17 00:00:00 2001 From: Mark Geiger Date: Tue, 25 Aug 2026 23:01:07 +0200 Subject: [PATCH 013/600] drivers: gnss: add option to suspend gnss_nmea_generic add method to suspend generic nmea gnss driver and call it via pm action Signed-off-by: Mark Geiger --- drivers/gnss/gnss_nmea_generic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gnss/gnss_nmea_generic.c b/drivers/gnss/gnss_nmea_generic.c index bef7989828df..128e07a815f6 100644 --- a/drivers/gnss/gnss_nmea_generic.c +++ b/drivers/gnss/gnss_nmea_generic.c @@ -81,6 +81,14 @@ static int gnss_nmea_generic_resume(const struct device *dev) return ret; } +static int gnss_nmea_generic_suspend(const struct device *dev) +{ + struct gnss_nmea_generic_data *data = dev->data; + + modem_chat_release(&data->chat); + return modem_pipe_close(data->uart_pipe, K_SECONDS(10)); +} + static DEVICE_API(gnss, gnss_api) = { }; @@ -172,6 +180,8 @@ static int gnss_nmea_generic_pm_action(const struct device *dev, enum pm_device_ switch (action) { case PM_DEVICE_ACTION_RESUME: return gnss_nmea_generic_resume(dev); + case PM_DEVICE_ACTION_SUSPEND: + return gnss_nmea_generic_suspend(dev); default: return -ENOTSUP; } From 36b3c8f4b1f15b8a975e332d60d7a86facb99652 Mon Sep 17 00:00:00 2001 From: Angel Covarrubias Date: Tue, 25 Aug 2026 16:20:16 -0600 Subject: [PATCH 014/600] drivers: modem: sim7080: handle F_GETFL/F_SETFL in offload_ioctl The native TLS socket layer calls zsock_fcntl(F_GETFL) on the underlying offloaded socket to clear and restore O_NONBLOCK around the TLS handshake (ztls_connect_ctx), before issuing the underlying connect. offload_ioctl() only handled the poll ioctls, so the fcntl failed with EINVAL and any connection over TLS-native sockets on top of this driver aborted before AT+CAOPEN was ever sent, with only a confusing "AT+CACLOSE=0 ret: -5" from the cleanup path in the log. Handle ZVFS_F_GETFL and ZVFS_F_SETFL by returning 0: the offloaded socket is always blocking and non-blocking reads are requested per call via MSG_DONTWAIT. This is the same approach used by the ublox-sara-r4 and hl78xx drivers. Tested on SIM7080G (Cat-M1) hardware against an MQTT broker on port 8883 with CONFIG_NET_SOCKETS_SOCKOPT_TLS=y and CONFIG_MQTT_LIB_TLS=y: without this change mqtt_connect() always fails with -EINVAL; with it the TLS handshake and MQTT session complete normally. Note: this issue was diagnosed and the fix developed with the assistance of an AI tool; the change was reviewed and verified on real hardware by the author. Assisted-by: AI tool (Claude Code) Signed-off-by: Angel Covarrubias --- .../vendor_standalone/simcom/sim7080/sim7080_sock.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/modem/vendor_standalone/simcom/sim7080/sim7080_sock.c b/drivers/modem/vendor_standalone/simcom/sim7080/sim7080_sock.c index e0c67fa21ea6..98e81239ac12 100644 --- a/drivers/modem/vendor_standalone/simcom/sim7080/sim7080_sock.c +++ b/drivers/modem/vendor_standalone/simcom/sim7080/sim7080_sock.c @@ -532,7 +532,8 @@ static int offload_poll(struct zsock_pollfd *fds, int nfds, int msecs) } /* - * Offloads ioctl. Only supported ioctl is poll_offload. + * Offloads ioctl. Supports the poll offload requests and the + * F_GETFL/F_SETFL fcntl requests. */ static int offload_ioctl(void *obj, unsigned int request, va_list args) { @@ -555,6 +556,16 @@ static int offload_ioctl(void *obj, unsigned int request, va_list args) return offload_poll(fds, nfds, timeout); } + case ZVFS_F_GETFL: + /* The socket is always blocking, no flags are set. */ + return 0; + + case ZVFS_F_SETFL: + /* Accept and ignore: the socket stays blocking, non-blocking + * reads are requested per call via MSG_DONTWAIT. + */ + return 0; + default: errno = EINVAL; return -1; From 55afd0c7da343031231bb06c681fb0cc06f726db Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 21:50:51 -0400 Subject: [PATCH 015/600] drivers: i2c: dw: scope the NVIC pending-clear to RTS5912 i2c_dw_transfer_complete() clears the NVIC pending bit for its own IRQ line after clearing the IP-level interrupt source. This was added along with the RTS5912 support in commit 748789eadf710 ("drivers: i2c: rts5912 i2c dirver") but gated on CONFIG_CPU_CORTEX_M, so every Cortex-M user of the DesignWare IP -- RP2040/RP2350, SiWG917, Synaptics SR100 -- silently inherited a Realtek-specific workaround, and the shared IP driver grew a dependency on cmsis_core.h. Clearing the NVIC pending bit after the source has already been cleared also discards any interrupt that latched in between, so it is not a harmless no-op on parts that do not need it. Gate the call, the cmsis_core.h include and the irqnumber config member on CONFIG_I2C_RTS5912 instead, matching the other RTS5912 carve-outs already present in Kconfig.dw. Behaviour on RTS5912 is unchanged; other Cortex-M platforms return to the pre-748789eadf710 behaviour. Dropping irqnumber for everyone else also removes a DT_INST_IRQN() on PCIe instances, which have no devicetree interrupt of their own. Also drop the unused zephyr/arch/cpu.h include added by the same commit; it is a pure dispatch header and kernel.h and irq.h already provide everything the driver uses. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/i2c/i2c_dw.c | 19 ++++++++++++++----- drivers/i2c/i2c_dw.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/i2c_dw.c b/drivers/i2c/i2c_dw.c index ede63d142ee5..a24e23e11872 100644 --- a/drivers/i2c/i2c_dw.c +++ b/drivers/i2c/i2c_dw.c @@ -7,8 +7,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#ifdef CONFIG_CPU_CORTEX_M +#ifdef CONFIG_I2C_RTS5912 #include #endif @@ -506,9 +505,13 @@ static inline void i2c_dw_transfer_complete(const struct device *dev) write_intr_mask(DW_DISABLE_ALL_I2C_INT, reg_base); value = read_clr_intr(reg_base); -#ifdef CONFIG_CPU_CORTEX_M +#ifdef CONFIG_I2C_RTS5912 const struct i2c_dw_rom_config *const rom = dev->config; - /* clear pending interrupt */ + /* + * The RTS5912 keeps the NVIC line asserted after the IP-level interrupt + * source has been cleared, so the pending bit has to be dropped by hand. + * This is a quirk of that SoC, not of the DesignWare IP. + */ NVIC_ClearPendingIRQ(rom->irqnumber); #endif k_sem_give(&dw->device_sync_sem); @@ -1617,6 +1620,12 @@ static int i2c_dw_initialize(const struct device *dev) #define TIMEOUT_DW_CONFIG(n) #endif +#ifdef CONFIG_I2C_RTS5912 +#define IRQN_DW_CONFIG(n) .irqnumber = DT_INST_IRQN(n), +#else +#define IRQN_DW_CONFIG(n) +#endif + /* clang-format off */ #define I2C_DEVICE_INIT_DW(n) \ PINCTRL_DW_DEFINE(n); \ @@ -1629,7 +1638,7 @@ static int i2c_dw_initialize(const struct device *dev) (HOLD_TIME_TO_TICKS(DT_INST_PROP(n, i2c_sda_hold_time_ns))), \ (DT_INST_PROP_OR(n, sda_hold_tx, SDA_HOLD_INVALID))), \ .sda_hold_rx = DT_INST_PROP_OR(n, sda_hold_rx, SDA_HOLD_INVALID), \ - .irqnumber = DT_INST_IRQN(n), \ + IRQN_DW_CONFIG(n) \ .lcnt_offset = (int16_t)DT_INST_PROP_OR(n, lcnt_offset, 0), \ .hcnt_offset = (int16_t)DT_INST_PROP_OR(n, hcnt_offset, 0), \ .fs_spk_len = MAX((uint8_t)DT_INST_PROP_OR(n, fs_spike_len, 0), DW_IC_SPKLEN_MIN), \ diff --git a/drivers/i2c/i2c_dw.h b/drivers/i2c/i2c_dw.h index e152057bccd4..4691497dabbe 100644 --- a/drivers/i2c/i2c_dw.h +++ b/drivers/i2c/i2c_dw.h @@ -153,7 +153,9 @@ struct i2c_dw_rom_config { uint32_t bitrate; uint32_t sda_hold_tx; uint32_t sda_hold_rx; +#ifdef CONFIG_I2C_RTS5912 uint32_t irqnumber; +#endif int16_t lcnt_offset; int16_t hcnt_offset; uint8_t fs_spk_len; From fd518df1559fcc062add7118f31ad2b2ba024018 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 22:59:12 -0400 Subject: [PATCH 016/600] drivers: i2c: dw: use k_irq_clear_pending() Replace the direct NVIC_ClearPendingIRQ() call in the RTS5912 quirk path with the portable k_irq_clear_pending() and drop the cmsis_core.h include, removing the last piece of CPU-specific code from the DesignWare I2C driver. RTS5912 is a plain-NVIC Cortex-M33, so the capability is always available where the quirk compiles. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/i2c/i2c_dw.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/i2c/i2c_dw.c b/drivers/i2c/i2c_dw.c index a24e23e11872..e04aadb55ccc 100644 --- a/drivers/i2c/i2c_dw.c +++ b/drivers/i2c/i2c_dw.c @@ -7,10 +7,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifdef CONFIG_I2C_RTS5912 -#include -#endif - #include #include #include @@ -512,7 +508,7 @@ static inline void i2c_dw_transfer_complete(const struct device *dev) * source has been cleared, so the pending bit has to be dropped by hand. * This is a quirk of that SoC, not of the DesignWare IP. */ - NVIC_ClearPendingIRQ(rom->irqnumber); + k_irq_clear_pending(rom->irqnumber); #endif k_sem_give(&dw->device_sync_sem); } From 305f7fe714ae7cc26ab2f8e429c639dd90409cd9 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Tue, 25 Aug 2026 15:46:24 -0700 Subject: [PATCH 017/600] i3c: support expected NACK from target device on data xfer There are some situations where a NACK is expected from the target after sending the address. One example would be MCTP, where the target would NACK if there is no data to return during polling. This use case is not entirely conforming to traditional I2C/I3C transfer mechanism where a NACK after the address byte means there is no target with this address or the target is not responding to the transfer request. In order to support MCTP, we will need to handle this type of transfer gracefully, by adding a new transfer message flag to indicate this type of transfer. A new return value is also being introduced to i3c_transfer() to signal the expected NACK is received. Since most of the wire interactions are handled by the hardware, each individual driver will need to purposely handle this use case. For now, this commit only introduces the building block needed for the drivers to implement the bits to support this use case. Also changed the RTIO generic I3C code to support this. Relates to #114312 Signed-off-by: Daniel Leung --- drivers/i3c/i3c_rtio.c | 2 ++ drivers/i3c/i3c_rtio_default.c | 6 ++++++ include/zephyr/drivers/i3c.h | 5 +++++ include/zephyr/rtio/sqe.h | 5 +++++ 4 files changed, 18 insertions(+) diff --git a/drivers/i3c/i3c_rtio.c b/drivers/i3c/i3c_rtio.c index 660a7977bccb..38cdff0f951f 100644 --- a/drivers/i3c/i3c_rtio.c +++ b/drivers/i3c/i3c_rtio.c @@ -48,6 +48,8 @@ struct rtio_sqe *i3c_rtio_copy(struct rtio *r, struct rtio_iodev *iodev, const s ((msgs[i].flags & I3C_MSG_RESTART) ? RTIO_IODEV_I3C_RESTART : 0) | ((msgs[i].flags & I3C_MSG_HDR) ? RTIO_IODEV_I3C_HDR : 0) | ((msgs[i].flags & I3C_MSG_NBCH) ? RTIO_IODEV_I3C_NBCH : 0) | + ((msgs[i].flags & I3C_MSG_NOACK_EXPECTED) ? + RTIO_IODEV_I3C_NOACK_EXPECTED : 0) | RTIO_IODEV_I3C_HDR_MODE_SET(msgs[i].hdr_mode) | RTIO_IODEV_I3C_HDR_CMD_CODE_SET(msgs[i].hdr_cmd_code); } diff --git a/drivers/i3c/i3c_rtio_default.c b/drivers/i3c/i3c_rtio_default.c index b3cf6a309ef9..561eaa19d7fe 100644 --- a/drivers/i3c/i3c_rtio_default.c +++ b/drivers/i3c/i3c_rtio_default.c @@ -24,6 +24,8 @@ static inline void i3c_msg_from_rx(const struct rtio_iodev_sqe *iodev_sqe, struc ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_RESTART) ? I3C_MSG_RESTART : 0) | ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_HDR) ? I3C_MSG_HDR : 0) | ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_NBCH) ? I3C_MSG_NBCH : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_NOACK_EXPECTED) ? + I3C_MSG_NOACK_EXPECTED : 0) | I3C_MSG_READ; } @@ -38,6 +40,8 @@ static inline void i3c_msg_from_tx(const struct rtio_iodev_sqe *iodev_sqe, struc ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_RESTART) ? I3C_MSG_RESTART : 0) | ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_HDR) ? I3C_MSG_HDR : 0) | ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_NBCH) ? I3C_MSG_NBCH : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_NOACK_EXPECTED) ? + I3C_MSG_NOACK_EXPECTED : 0) | I3C_MSG_WRITE; } @@ -52,6 +56,8 @@ static inline void i3c_msg_from_tiny_tx(const struct rtio_iodev_sqe *iodev_sqe, ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_RESTART) ? I3C_MSG_RESTART : 0) | ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_HDR) ? I3C_MSG_HDR : 0) | ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_NBCH) ? I3C_MSG_NBCH : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I3C_NOACK_EXPECTED) ? + I3C_MSG_NOACK_EXPECTED : 0) | I3C_MSG_WRITE; } diff --git a/include/zephyr/drivers/i3c.h b/include/zephyr/drivers/i3c.h index 0bd89ac9af34..ea7ce19e4cec 100644 --- a/include/zephyr/drivers/i3c.h +++ b/include/zephyr/drivers/i3c.h @@ -347,6 +347,9 @@ enum i3c_data_rate { /** Skip I3C broadcast header. Private Transfers only. */ #define I3C_MSG_NBCH BIT(4) +/** NACK is expected from target */ +#define I3C_MSG_NOACK_EXPECTED BIT(5) + /** I3C HDR Mode 0 */ #define I3C_MSG_HDR_MODE0 BIT(0) @@ -2011,6 +2014,8 @@ static inline int z_impl_i3c_do_ccc_cb(const struct device *dev, * @retval 0 on success. * @retval -EBUSY Bus is busy. * @retval -EIO General input / output error. + * @retval -ENODATA If message has flag I3C_MSG_NOACK_EXPECTED set and + * the target NACK the transfer. */ __syscall int i3c_transfer(struct i3c_device_desc *target, struct i3c_msg *msgs, uint8_t num_msgs); diff --git a/include/zephyr/rtio/sqe.h b/include/zephyr/rtio/sqe.h index 8115e03bb5ea..1ce2e8ece0f0 100644 --- a/include/zephyr/rtio/sqe.h +++ b/include/zephyr/rtio/sqe.h @@ -235,6 +235,11 @@ extern "C" { */ #define RTIO_IODEV_I3C_NBCH BIT(4) +/** + * @brief Equivalent to the I3C_MSG_NOACK_EXPECTED + */ +#define RTIO_IODEV_I3C_NOACK_EXPECTED BIT(5) + /** * @brief I3C HDR Mode Mask */ From 6f76eaa552714555986db14c136c8d386753d35d Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:39:57 -0400 Subject: [PATCH 018/600] soc: silabs: use portable IRQ pending API Replace direct NVIC_ClearPendingIRQ() calls with k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- soc/silabs/silabs_s2/soc.c | 3 ++- soc/silabs/silabs_sim3/sim3u/soc.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/soc/silabs/silabs_s2/soc.c b/soc/silabs/silabs_s2/soc.c index 896f7ef8ff2c..fc5e361989c9 100644 --- a/soc/silabs/silabs_s2/soc.c +++ b/soc/silabs/silabs_s2/soc.c @@ -9,6 +9,7 @@ * @brief SoC initialization for Silicon Labs Series 2 products */ +#include #include #include @@ -115,7 +116,7 @@ void soc_prep_hook(void) __DSB(); __ISB(); - NVIC_ClearPendingIRQ(SMU_SECURE_IRQn); + k_irq_clear_pending(SMU_SECURE_IRQn); SMU->IF_CLR = SMU_IF_PPUSEC | SMU_IF_BMPUSEC; SMU->IEN = SMU_IEN_PPUSEC | SMU_IEN_BMPUSEC; #endif diff --git a/soc/silabs/silabs_sim3/sim3u/soc.c b/soc/silabs/silabs_sim3/sim3u/soc.c index 9544d2edff9f..9dfd82df419f 100644 --- a/soc/silabs/silabs_sim3/sim3u/soc.c +++ b/soc/silabs/silabs_sim3/sim3u/soc.c @@ -91,7 +91,7 @@ static void vmon_init(void) { /* VMON must be enabled for flash write/erase support */ - NVIC_ClearPendingIRQ(VDDLOW_IRQn); + k_irq_clear_pending(VDDLOW_IRQn); IRQ_CONNECT(VDDLOW_IRQn, 0, vddlow_irq_handler, NULL, 0); irq_enable(VDDLOW_IRQn); From bf16c3af1364490285f4da859509253f22247896 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:39:57 -0400 Subject: [PATCH 019/600] drivers: timer: gecko_burtc: use portable IRQ pending API Replace the direct NVIC_ClearPendingIRQ() call with k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/timer/gecko_burtc_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/timer/gecko_burtc_timer.c b/drivers/timer/gecko_burtc_timer.c index 5772f0e9a8ca..c31a7d8dcc64 100644 --- a/drivers/timer/gecko_burtc_timer.c +++ b/drivers/timer/gecko_burtc_timer.c @@ -212,7 +212,7 @@ static int burtc_init(void) /* Enable compare match interrupt */ BURTC_IntClear(BURTC_IF_COMP); BURTC_IntEnable(BURTC_IF_COMP); - NVIC_ClearPendingIRQ(TIMER_IRQ); + k_irq_clear_pending(TIMER_IRQ); IRQ_CONNECT(TIMER_IRQ, DT_INST_IRQ(0, priority), burtc_isr, 0, 0); irq_enable(TIMER_IRQ); From bdaf153ade076ef63c62cfa85c701ae1d74d2f1c Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:40:43 -0400 Subject: [PATCH 020/600] drivers: gpio: si32: use portable IRQ pending API Replace the direct NVIC_ClearPendingIRQ() call with k_irq_clear_pending() and drop the cmsis_core.h include. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/gpio/gpio_si32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio_si32.c b/drivers/gpio/gpio_si32.c index 4c738200f6e3..689bdde0dd50 100644 --- a/drivers/gpio/gpio_si32.c +++ b/drivers/gpio/gpio_si32.c @@ -6,6 +6,7 @@ #define DT_DRV_COMPAT silabs_si32_gpio +#include #include #include #include @@ -205,7 +206,7 @@ static void gpio_si32_irq_handler(const struct device *arg) ARG_UNUSED(arg); irq_disable(PMATCH_IRQn); - NVIC_ClearPendingIRQ(PMATCH_IRQn); + k_irq_clear_pending(PMATCH_IRQn); for (size_t i = 0; i < ARRAY_SIZE(gpio_devices); i++) { const struct device *dev = gpio_devices[i]; From 41b15df0d3cbed3b87b646915bd596d7da589759 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:38:07 -0400 Subject: [PATCH 021/600] drivers: counter: rza2m_ostm: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. This driver called the arm_gic_irq_* pending helpers directly; the portable API removes the GIC-specific dependency. The gic.h include stays for GIC_SPI_INT_BASE. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_renesas_rza2m_ostm.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/counter/counter_renesas_rza2m_ostm.c b/drivers/counter/counter_renesas_rza2m_ostm.c index 47934e7f0a5e..1d6c8861c274 100644 --- a/drivers/counter/counter_renesas_rza2m_ostm.c +++ b/drivers/counter/counter_renesas_rza2m_ostm.c @@ -6,11 +6,12 @@ #define DT_DRV_COMPAT renesas_rza2m_ostm_counter +#include #include #include #include -#include #include +#include #define RZA2M_OSTM_TOP_VALUE UINT32_MAX @@ -145,12 +146,12 @@ static int renesas_rza2m_ostm_abs_alarm_set(const struct device *dev, uint32_t v if (irq_on_late) { irq_enable(data->cycle_end_irq); - arm_gic_irq_set_pending(data->cycle_end_irq); + k_irq_set_pending(data->cycle_end_irq); } else { data->alarm_cb = NULL; } } else { - arm_gic_irq_clear_pending(data->cycle_end_irq); + k_irq_clear_pending(data->cycle_end_irq); irq_enable(data->cycle_end_irq); } @@ -187,12 +188,12 @@ static int renesas_rza2m_ostm_rel_alarm_set(const struct device *dev, uint32_t v if (diff > max_rel_val || diff == 0) { if (irq_on_late) { irq_enable(data->cycle_end_irq); - arm_gic_irq_set_pending(data->cycle_end_irq); + k_irq_set_pending(data->cycle_end_irq); } else { data->alarm_cb = NULL; } } else { - arm_gic_irq_clear_pending(data->cycle_end_irq); + k_irq_clear_pending(data->cycle_end_irq); irq_enable(data->cycle_end_irq); } @@ -292,7 +293,7 @@ static int counter_rza2m_ostm_start(const struct device *dev) renesas_rza2m_ostm_switch_timer_mode(dev); - arm_gic_irq_clear_pending(data->cycle_end_irq); + k_irq_clear_pending(data->cycle_end_irq); data->is_started = true; if (data->top_cb) { irq_enable(data->cycle_end_irq); @@ -320,7 +321,7 @@ static int counter_rza2m_ostm_stop(const struct device *dev) /* Disable irq */ irq_disable(data->cycle_end_irq); - arm_gic_irq_clear_pending(data->cycle_end_irq); + k_irq_clear_pending(data->cycle_end_irq); data->top_cb = NULL; data->alarm_cb = NULL; @@ -441,7 +442,7 @@ static int counter_rza2m_ostm_cancel_alarm(const struct device *dev, uint8_t cha } irq_disable(data->cycle_end_irq); - arm_gic_irq_clear_pending(data->cycle_end_irq); + k_irq_clear_pending(data->cycle_end_irq); data->alarm_cb = NULL; data->user_data = NULL; @@ -530,7 +531,7 @@ static uint32_t counter_rza2m_ostm_get_pending_int(const struct device *dev) { struct counter_rza2m_ostm_data *data = dev->data; - return arm_gic_irq_is_pending(data->cycle_end_irq); + return k_irq_is_pending(data->cycle_end_irq); } static uint32_t counter_rza2m_ostm_get_top_value(const struct device *dev) From c9d1e904cf64e3942037a6b44683b2da73330f27 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:38:07 -0400 Subject: [PATCH 022/600] drivers: counter: max32: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_max32_timer.c | 2 +- drivers/counter/counter_max32_wut.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/counter/counter_max32_timer.c b/drivers/counter/counter_max32_timer.c index a7de09e04f75..32d14debbfad 100644 --- a/drivers/counter/counter_max32_timer.c +++ b/drivers/counter/counter_max32_timer.c @@ -192,7 +192,7 @@ static int set_cc(const struct device *dev, uint8_t id, uint32_t val, uint32_t f * for absolute depending on the flag. */ if (irq_on_late) { - NVIC_SetPendingIRQ(MXC_TMR_GET_IRQ(MXC_TMR_GET_IDX(regs))); + k_irq_set_pending(MXC_TMR_GET_IRQ(MXC_TMR_GET_IDX(regs))); } else { config->ch_data[id].callback = NULL; } diff --git a/drivers/counter/counter_max32_wut.c b/drivers/counter/counter_max32_wut.c index 50aa4c7ecefd..9a3cced980c4 100644 --- a/drivers/counter/counter_max32_wut.c +++ b/drivers/counter/counter_max32_wut.c @@ -164,7 +164,7 @@ static int counter_max32_wut_set_alarm(const struct device *dev, uint8_t chan, irq_on_late = alarm_cfg->flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE; if (irq_on_late || !absolute) { - NVIC_SetPendingIRQ(cfg->irq_number); + k_irq_set_pending(cfg->irq_number); } else { data->alarm.callback = NULL; data->alarm.user_data = NULL; From a6400dc045db629efac4d363213d4cb5fd4e8dd2 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:43 -0400 Subject: [PATCH 023/600] drivers: counter: mcux_qtmr: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_mcux_qtmr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/counter_mcux_qtmr.c b/drivers/counter/counter_mcux_qtmr.c index e163f45d9dbd..63b433fa5622 100644 --- a/drivers/counter/counter_mcux_qtmr.c +++ b/drivers/counter/counter_mcux_qtmr.c @@ -300,7 +300,7 @@ static int mcux_qtmr_set_alarm(const struct device *dev, uint8_t chan_id, * forcing the interrupt. */ atomic_set(&data->irq_pending, 1); - NVIC_SetPendingIRQ(config->irqn); + k_irq_set_pending(config->irqn); } else { QTMR_DisableInterrupts(config->base, config->channel, kQTMR_Compare1InterruptEnable); From d6010435aedcf8838b5a41abfc45d80d08ff6952 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 024/600] drivers: counter: mcux_sysctr: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_mcux_sysctr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/counter/counter_mcux_sysctr.c b/drivers/counter/counter_mcux_sysctr.c index 70e07a1d208c..5b7615a5dfaf 100644 --- a/drivers/counter/counter_mcux_sysctr.c +++ b/drivers/counter/counter_mcux_sysctr.c @@ -299,7 +299,7 @@ static int mcux_sysctr_set_alarm(const struct device *dev, uint8_t chan_id, */ if (irq_on_late) { atomic_or(&data->irq_pending, BIT(chan_id)); - NVIC_SetPendingIRQ(config->irqn); + k_irq_set_pending(config->irqn); } else { data->channels[chan_id].callback = NULL; } @@ -396,7 +396,7 @@ static int mcux_sysctr_set_alarm_64(const struct device *dev, uint8_t chan_id, if (irq_on_late) { atomic_or(&data->irq_pending, BIT(chan_id)); - NVIC_SetPendingIRQ(config->irqn); + k_irq_set_pending(config->irqn); } else { data->channels[chan_id].callback_64 = NULL; } From 01981397e1b55652904d94ed0f00fe0174489186 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 025/600] drivers: counter: mcux_rtc_jdp: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_mcux_rtc_jdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/counter_mcux_rtc_jdp.c b/drivers/counter/counter_mcux_rtc_jdp.c index eace08b6a5d6..a3b1fd3cbc1c 100644 --- a/drivers/counter/counter_mcux_rtc_jdp.c +++ b/drivers/counter/counter_mcux_rtc_jdp.c @@ -143,7 +143,7 @@ static inline void mcux_rtc_jdp_alarm_sw_pend(struct mcux_rtc_jdp_data *data, { data->sw_pending_mask |= BIT(chan_id); irq_enable(config->irqn); - NVIC_SetPendingIRQ(config->irqn); + k_irq_set_pending(config->irqn); } static inline int mcux_rtc_jdp_alarm_handle_late_or_drop(struct mcux_rtc_jdp_data *data, From 9f1eeff96cfdde8d70ef4c5656fd44c899a5e8c1 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 026/600] drivers: counter: mcux_wake_timer: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). This also deletes the driver's private GIC-or-NVIC dispatch helpers, which are exactly what the portable API now provides. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_mcux_wake_timer.c | 44 ++++------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/drivers/counter/counter_mcux_wake_timer.c b/drivers/counter/counter_mcux_wake_timer.c index 7ec5d7935512..5abfc1e8a725 100644 --- a/drivers/counter/counter_mcux_wake_timer.c +++ b/drivers/counter/counter_mcux_wake_timer.c @@ -10,9 +10,6 @@ #include #include #include -#if defined(CONFIG_GIC) -#include -#endif /* CONFIG_GIC */ #include #include @@ -68,33 +65,6 @@ struct mcux_wake_timer_data { #endif }; -static ALWAYS_INLINE void irq_set_pending(unsigned int irq) -{ -#if defined(CONFIG_GIC) - arm_gic_irq_set_pending(irq); -#else - NVIC_SetPendingIRQ(irq); -#endif /* CONFIG_GIC */ -} - -static ALWAYS_INLINE bool irq_is_pending(unsigned int irq) -{ -#if defined(CONFIG_GIC) - return arm_gic_irq_is_pending(irq); -#else - return NVIC_GetPendingIRQ((IRQn_Type)irq) != 0U; -#endif /* CONFIG_GIC */ -} - -static ALWAYS_INLINE void irq_clear_pending(unsigned int irq) -{ -#if defined(CONFIG_GIC) - arm_gic_irq_clear_pending(irq); -#else - NVIC_ClearPendingIRQ((IRQn_Type)irq); -#endif /* CONFIG_GIC */ -} - /* Load a non-zero count and launch the countdown. The counter must be halted * (it always is after init, a stop, or a previous time-out) before a write. */ @@ -126,7 +96,7 @@ static uint32_t mcux_wake_timer_get_pending_int(const struct device *dev) * hardware time-out. */ if (((config->base->WAKE_TIMER_CTRL & WAKETIMER_WAKE_TIMER_CTRL_INTR_EN_MASK) != 0U) && - irq_is_pending(config->irqn)) { + k_irq_is_pending(config->irqn)) { return 1U; } @@ -154,7 +124,7 @@ static int mcux_wake_timer_start(const struct device *dev) */ WAKETIMER_DisableInterrupts(config->base, kWAKETIMER_WakeInterruptEnable); WAKETIMER_HaltTimer(config->base); - irq_clear_pending(config->irqn); + k_irq_clear_pending(config->irqn); data->alarm_callback = NULL; data->alarm_user_data = NULL; @@ -176,7 +146,7 @@ static int mcux_wake_timer_stop(const struct device *dev) WAKETIMER_DisableInterrupts(config->base, kWAKETIMER_WakeInterruptEnable); WAKETIMER_HaltTimer(config->base); - irq_clear_pending(config->irqn); + k_irq_clear_pending(config->irqn); data->alarm_callback = NULL; data->alarm_user_data = NULL; @@ -233,12 +203,12 @@ static int mcux_wake_timer_set_alarm(const struct device *dev, uint8_t chan_id, */ WAKETIMER_HaltTimer(config->base); WAKETIMER_ClearStatusFlags(config->base, kWAKETIMER_WakeFlag); - irq_clear_pending(config->irqn); + k_irq_clear_pending(config->irqn); WAKETIMER_EnableInterrupts(config->base, kWAKETIMER_WakeInterruptEnable); if (sw_pending) { /* Trigger the callback immediately through the interrupt path. */ - irq_set_pending(config->irqn); + k_irq_set_pending(config->irqn); } else { mcux_wake_timer_load(config->base, ticks); } @@ -271,7 +241,7 @@ static int mcux_wake_timer_cancel_alarm(const struct device *dev, uint8_t chan_i WAKETIMER_DisableInterrupts(config->base, kWAKETIMER_WakeInterruptEnable); WAKETIMER_HaltTimer(config->base); WAKETIMER_ClearStatusFlags(config->base, kWAKETIMER_WakeFlag); - irq_clear_pending(config->irqn); + k_irq_clear_pending(config->irqn); data->alarm_callback = NULL; data->alarm_user_data = NULL; @@ -336,7 +306,7 @@ static int mcux_wake_timer_stop(const struct device *dev) WAKETIMER_DisableInterrupts(config->base, kWAKETIMER_WakeInterruptEnable); WAKETIMER_HaltTimer(config->base); - irq_clear_pending(config->irqn); + k_irq_clear_pending(config->irqn); k_spin_unlock(&data->lock, key); From 70d58653a8dba820c530f5416f47b69da22d9f18 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 027/600] drivers: counter: nxp_s32_sys_timer: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). This also deletes the driver's private GIC-or-NVIC dispatch helper, which is exactly what the portable API now provides. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_nxp_s32_sys_timer.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/counter/counter_nxp_s32_sys_timer.c b/drivers/counter/counter_nxp_s32_sys_timer.c index 66d7ccac8395..ecbfeb7ccc2f 100644 --- a/drivers/counter/counter_nxp_s32_sys_timer.c +++ b/drivers/counter/counter_nxp_s32_sys_timer.c @@ -9,9 +9,6 @@ #include #include #include -#if defined(CONFIG_GIC) -#include -#endif /* CONFIG_GIC */ #include #include @@ -71,15 +68,6 @@ struct nxp_s32_sys_timer_config { unsigned int irqn; }; -static ALWAYS_INLINE void irq_set_pending(unsigned int irq) -{ -#if defined(CONFIG_GIC) - arm_gic_irq_set_pending(irq); -#else - NVIC_SetPendingIRQ(irq); -#endif /* CONFIG_GIC */ -} - static uint32_t ticks_add(uint32_t val1, uint32_t val2, uint32_t top) { uint32_t to_top; @@ -164,7 +152,7 @@ static int stm_set_alarm(const struct device *dev, uint8_t channel, uint32_t tic */ if (irq_on_late) { atomic_or(&data->irq_pending, BIT(channel)); - irq_set_pending(config->irqn); + k_irq_set_pending(config->irqn); } else { ch_data->callback = NULL; } From 36c8272b3f8b0b1213c07104e3e39695c47814d3 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 028/600] drivers: ieee802154: kw41z: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/ieee802154/ieee802154_kw41z.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ieee802154/ieee802154_kw41z.c b/drivers/ieee802154/ieee802154_kw41z.c index 0a3f35fbaf06..344f7568ab58 100644 --- a/drivers/ieee802154/ieee802154_kw41z.c +++ b/drivers/ieee802154/ieee802154_kw41z.c @@ -1061,7 +1061,7 @@ static int kw41z_init(const struct device *dev) ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TRCV_MSK_MASK; /* Configure Radio IRQ */ - NVIC_ClearPendingIRQ(Radio_1_IRQn); + k_irq_clear_pending(Radio_1_IRQn); IRQ_CONNECT(Radio_1_IRQn, RADIO_0_IRQ_PRIO, kw41z_isr, 0, 0); return 0; From 27d14379f3519faa0274425de755a456b51c5959 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 029/600] drivers: watchdog: mcux_wwdt: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/watchdog/wdt_mcux_wwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/wdt_mcux_wwdt.c b/drivers/watchdog/wdt_mcux_wwdt.c index ba56daf06ec0..d99bfc561379 100644 --- a/drivers/watchdog/wdt_mcux_wwdt.c +++ b/drivers/watchdog/wdt_mcux_wwdt.c @@ -330,7 +330,7 @@ static DEVICE_API(wdt, mcux_wwdt_api) = { /* Defensive: clear any peripheral status and NVIC pending */ \ WWDT_ClearStatusFlags((WWDT_Type *)DT_INST_REG_ADDR(id), \ WWDT_GetStatusFlags((WWDT_Type *)DT_INST_REG_ADDR(id))); \ - NVIC_ClearPendingIRQ(DT_INST_IRQN(id)); \ + k_irq_clear_pending(DT_INST_IRQN(id)); \ irq_enable(DT_INST_IRQN(id)); \ } From dd8910f13290abf7cc5f918ac18427dfa99077ee Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 030/600] soc: nxp: rw: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- soc/nxp/rw/power.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/soc/nxp/rw/power.c b/soc/nxp/rw/power.c index 5bda9e52bf36..484e0923c871 100644 --- a/soc/nxp/rw/power.c +++ b/soc/nxp/rw/power.c @@ -3,6 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -54,7 +55,7 @@ static void pin0_isr(const struct device *dev) uint8_t level = ~(DT_ENUM_IDX(DT_NODELABEL(pin0), wakeup_level)) & 0x1; POWER_ConfigWakeupPin(kPOWER_WakeupPin0, level); - NVIC_ClearPendingIRQ(DT_IRQN(DT_NODELABEL(pin0))); + k_irq_clear_pending(DT_IRQN(DT_NODELABEL(pin0))); DisableIRQ(DT_IRQN(DT_NODELABEL(pin0))); POWER_DisableWakeup(DT_IRQN(DT_NODELABEL(pin0))); } @@ -66,7 +67,7 @@ static void pin1_isr(const struct device *dev) uint8_t level = ~(DT_ENUM_IDX(DT_NODELABEL(pin1), wakeup_level)) & 0x1; POWER_ConfigWakeupPin(kPOWER_WakeupPin1, level); - NVIC_ClearPendingIRQ(DT_IRQN(DT_NODELABEL(pin1))); + k_irq_clear_pending(DT_IRQN(DT_NODELABEL(pin1))); DisableIRQ(DT_IRQN(DT_NODELABEL(pin1))); POWER_DisableWakeup(DT_IRQN(DT_NODELABEL(pin1))); } @@ -170,14 +171,14 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id) #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pin0)) POWER_ConfigWakeupPin(kPOWER_WakeupPin0, DT_ENUM_IDX(DT_NODELABEL(pin0), wakeup_level)); POWER_ClearWakeupStatus(DT_IRQN(DT_NODELABEL(pin0))); - NVIC_ClearPendingIRQ(DT_IRQN(DT_NODELABEL(pin0))); + k_irq_clear_pending(DT_IRQN(DT_NODELABEL(pin0))); EnableIRQ(DT_IRQN(DT_NODELABEL(pin0))); POWER_EnableWakeup(DT_IRQN(DT_NODELABEL(pin0))); #endif #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pin1)) POWER_ConfigWakeupPin(kPOWER_WakeupPin1, DT_ENUM_IDX(DT_NODELABEL(pin1), wakeup_level)); POWER_ClearWakeupStatus(DT_IRQN(DT_NODELABEL(pin1))); - NVIC_ClearPendingIRQ(DT_IRQN(DT_NODELABEL(pin1))); + k_irq_clear_pending(DT_IRQN(DT_NODELABEL(pin1))); EnableIRQ(DT_IRQN(DT_NODELABEL(pin1))); POWER_EnableWakeup(DT_IRQN(DT_NODELABEL(pin1))); #endif @@ -234,7 +235,7 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id) #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(standby)) RTC_ClearStatusFlags(RTC, kRTC_WakeupFlag); #endif - NVIC_ClearPendingIRQ(DT_IRQN(DT_NODELABEL(rtc))); + k_irq_clear_pending(DT_IRQN(DT_NODELABEL(rtc))); sys_clock_idle_exit(); { k_spinlock_key_t key = sys_clock_lock(); From 10050aad729c7431c689cd817899d892b7bd2723 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:22:44 -0400 Subject: [PATCH 031/600] soc: nxp: lpc55xxx: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- soc/nxp/lpc/lpc55xxx/soc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/soc/nxp/lpc/lpc55xxx/soc.c b/soc/nxp/lpc/lpc55xxx/soc.c index d749c3b53b85..94368b3663a7 100644 --- a/soc/nxp/lpc/lpc55xxx/soc.c +++ b/soc/nxp/lpc/lpc55xxx/soc.c @@ -11,6 +11,7 @@ * hardware for the nxp_lpc55s69 platform. */ +#include #include #include #include @@ -319,8 +320,8 @@ __weak void clock_init(void) #if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(usbhfs), nxp_uhc_ohci, okay) /* set BOD VBAT level to 1.65V */ POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false); - NVIC_ClearPendingIRQ(USB0_IRQn); - NVIC_ClearPendingIRQ(USB0_NEEDCLK_IRQn); + k_irq_clear_pending(USB0_IRQn); + k_irq_clear_pending(USB0_NEEDCLK_IRQn); /*< Turn on USB Phy */ #if defined(CONFIG_SOC_LPC55S36) POWER_DisablePD(kPDRUNCFG_PD_USBFSPHY); From cb3178d8eea2d02978f30c5d6c6acac1d1ac0782 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:38:07 -0400 Subject: [PATCH 032/600] drivers: serial: lpc11u6x: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/serial/uart_lpc11u6x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/serial/uart_lpc11u6x.c b/drivers/serial/uart_lpc11u6x.c index de60bd4b9416..d1619a138c0f 100644 --- a/drivers/serial/uart_lpc11u6x.c +++ b/drivers/serial/uart_lpc11u6x.c @@ -5,7 +5,6 @@ */ #define DT_DRV_COMPAT nxp_lpc11u6x_uart -#include #include #include @@ -232,7 +231,7 @@ static void lpc11u6x_uart0_irq_tx_enable(const struct device *dev) /* Due to hardware limitations, first TX interrupt is not triggered when * enabling it in the IER register. We have to trigger it. */ - NVIC_SetPendingIRQ(DT_INST_IRQN(0)); + k_irq_set_pending(DT_INST_IRQN(0)); } static void lpc11u6x_uart0_irq_tx_disable(const struct device *dev) From 39602248a8fcfda5e327bb5cd892bb946e1bcf9a Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:41:59 -0400 Subject: [PATCH 033/600] drivers: counter: mcux_qtmr: update comment for portable IRQ API The late-alarm comment still referred to NVIC_SetPendingIRQ() after the code moved to k_irq_set_pending(); make it match. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_mcux_qtmr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/counter_mcux_qtmr.c b/drivers/counter/counter_mcux_qtmr.c index 63b433fa5622..019ebd57aeaf 100644 --- a/drivers/counter/counter_mcux_qtmr.c +++ b/drivers/counter/counter_mcux_qtmr.c @@ -144,7 +144,7 @@ static void mcux_qtmr_isr(const struct device *timers[]) uint32_t channel_status = QTMR_GetStatus(config->base, ch); bool sw_pending = (atomic_clear(&data->irq_pending) != 0); - /* A late alarm forced through NVIC_SetPendingIRQ has no + /* A late alarm forced through k_irq_set_pending() has no * hardware compare flag set. Synthesize the compare event * so the handler runs the alarm callback immediately. */ From 1bf50b335dcc6170f84bfeac399c6729a740f80d Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:16:26 -0400 Subject: [PATCH 034/600] drivers: entropy: stm32: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/entropy/entropy_stm32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/entropy/entropy_stm32.c b/drivers/entropy/entropy_stm32.c index c2e913b7edd8..76a524808bb3 100644 --- a/drivers/entropy/entropy_stm32.c +++ b/drivers/entropy/entropy_stm32.c @@ -473,7 +473,7 @@ static uint16_t generate_from_isr(uint8_t *buf, uint16_t len) * to 1 (the bit is set when NVIC pending IRQ status is * changed from 0 to 1) */ - NVIC_ClearPendingIRQ(IRQN); + k_irq_clear_pending(IRQN); #endif /* !IRQLESS_TRNG */ do { @@ -503,7 +503,7 @@ static uint16_t generate_from_isr(uint8_t *buf, uint16_t len) ret = random_sample_get(&rnd_sample); #if !IRQLESS_TRNG - NVIC_ClearPendingIRQ(IRQN); + k_irq_clear_pending(IRQN); #endif /* !IRQLESS_TRNG */ if (ret < 0) { From 25064db654b1c87329328a27c96e74c978068c37 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:16:26 -0400 Subject: [PATCH 035/600] drivers: counter: stm32_rtc: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_stm32_rtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/counter_stm32_rtc.c b/drivers/counter/counter_stm32_rtc.c index 6b015de2df6d..a361e3e27eb4 100644 --- a/drivers/counter/counter_stm32_rtc.c +++ b/drivers/counter/counter_stm32_rtc.c @@ -525,7 +525,7 @@ static int rtc_stm32_get_value_64(const struct device *dev, uint64_t *ticks) #ifdef CONFIG_COUNTER_RTC_STM32_SUBSECONDS static void rtc_stm32_set_int_pending(void) { - NVIC_SetPendingIRQ(DT_INST_IRQN(0)); + k_irq_set_pending(DT_INST_IRQN(0)); } #endif /* CONFIG_COUNTER_RTC_STM32_SUBSECONDS */ From 8fe2ee23c131cb0a23a283867580bcd990744f56 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:16:26 -0400 Subject: [PATCH 036/600] drivers: counter: stm32_timer: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(). This also deletes the driver's private counter_stm32_set_pending() helper, which hand-dispatched between the ARM GIC and the NVIC -- exactly what the portable API now does -- along with its conditional gic.h include. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_stm32_timer.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/counter/counter_stm32_timer.c b/drivers/counter/counter_stm32_timer.c index b3fe75ee84e5..6b8a824c3421 100644 --- a/drivers/counter/counter_stm32_timer.c +++ b/drivers/counter/counter_stm32_timer.c @@ -14,9 +14,6 @@ #include #include #include -#if defined(CONFIG_GIC) -#include -#endif /* CONFIG_GIC */ #include #include @@ -223,22 +220,13 @@ static uint32_t counter_stm32_ticks_sub(uint32_t val, uint32_t old, uint32_t top return (val >= old) ? (val - old) : val + top + 1U - old; } -static void counter_stm32_set_pending(unsigned int irq) -{ -#if defined(CONFIG_GIC) - arm_gic_irq_set_pending(irq); -#else /* NVIC */ - NVIC_SetPendingIRQ(irq); -#endif /* CONFIG_GIC */ -} - static void counter_stm32_counter_stm32_set_cc_int_pending(const struct device *dev, uint8_t chan) { const struct counter_stm32_config *config = dev->config; struct counter_stm32_data *data = dev->data; atomic_or(&data->cc_int_pending, BIT(chan)); - counter_stm32_set_pending(config->irqn); + k_irq_set_pending(config->irqn); } static int counter_stm32_set_cc(const struct device *dev, uint8_t id, From 09fb8a776e1359aaf0fdca5c5037e9bee9a8847f Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:16:26 -0400 Subject: [PATCH 037/600] drivers: timer: stm32_lptim: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/timer/stm32_lptim_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/timer/stm32_lptim_timer.c b/drivers/timer/stm32_lptim_timer.c index dbea11b77e66..e98ff276dc18 100644 --- a/drivers/timer/stm32_lptim_timer.c +++ b/drivers/timer/stm32_lptim_timer.c @@ -369,7 +369,7 @@ void sys_clock_set_timeout(uint32_t ticks, bool idle) LL_LPTIM_DisableIT_ARROK(LPTIM); LL_LPTIM_ClearFlag_ARROK(LPTIM); - NVIC_ClearPendingIRQ(DT_IRQN(LPTIM_SYSTIMER_NODE)); + k_irq_clear_pending(DT_IRQN(LPTIM_SYSTIMER_NODE)); /* Stop clocks for LPTIM, since RTC is used instead */ clock_control_off(clk_ctrl, (clock_control_subsys_t) &lptim_clk[0]); From 454c23d8ebdd84a43cb425a7e8b470a98c5c5bc6 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:41:25 -0400 Subject: [PATCH 038/600] drivers: lora: stm32wl: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/lora/loramac-node/sx126x_stm32wl.c | 2 +- drivers/lora/native/sx126x/sx126x_hal_stm32wl.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/lora/loramac-node/sx126x_stm32wl.c b/drivers/lora/loramac-node/sx126x_stm32wl.c index 00ddb71015b3..e5369801a278 100644 --- a/drivers/lora/loramac-node/sx126x_stm32wl.c +++ b/drivers/lora/loramac-node/sx126x_stm32wl.c @@ -41,7 +41,7 @@ uint32_t sx126x_get_dio1_pin_state(struct sx126x_data *dev_data) void sx126x_dio1_irq_enable(struct sx126x_data *dev_data) { - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); irq_enable(DT_INST_IRQN(0)); } diff --git a/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c b/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c index d1f4b35175c3..b7ffbac7a6ee 100644 --- a/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c +++ b/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c @@ -85,7 +85,7 @@ int sx126x_hal_set_dio1_callback(const struct device *dev, data->dio1_callback = callback; if (callback != NULL) { - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); irq_enable(DT_INST_IRQN(0)); } else { irq_disable(DT_INST_IRQN(0)); @@ -105,7 +105,7 @@ void sx126x_hal_dio1_irq_enable(const struct device *dev) * can wake the radio from a duty-cycle sleep phase and * abort the cycle. */ - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); irq_enable(DT_INST_IRQN(0)); } From 7dc5112b90a2202f60c3c7c2c0b413772bb7f3d9 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:09:11 -0400 Subject: [PATCH 039/600] drivers: adc: smartbond: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ()/ NVIC_EnableIRQ() calls with the portable k_irq_set_pending()/ k_irq_clear_pending()/irq_enable() equivalents. The Smartbond DA1469x is a plain-NVIC Cortex-M33, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/adc/adc_smartbond_gpadc.c | 4 ++-- drivers/adc/adc_smartbond_sdadc.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/adc/adc_smartbond_gpadc.c b/drivers/adc/adc_smartbond_gpadc.c index e1be9f3f1ffa..f2b7e835844e 100644 --- a/drivers/adc/adc_smartbond_gpadc.c +++ b/drivers/adc/adc_smartbond_gpadc.c @@ -381,8 +381,8 @@ static int adc_smartbond_init(const struct device *dev) IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), adc_smartbond_isr, DEVICE_DT_INST_GET(0), 0); - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); - NVIC_EnableIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); + irq_enable(DT_INST_IRQN(0)); adc_context_unlock_unconditionally(&data->ctx); diff --git a/drivers/adc/adc_smartbond_sdadc.c b/drivers/adc/adc_smartbond_sdadc.c index f6633078e81e..ef85f3ac1bb0 100644 --- a/drivers/adc/adc_smartbond_sdadc.c +++ b/drivers/adc/adc_smartbond_sdadc.c @@ -385,8 +385,8 @@ static int sdadc_smartbond_init(const struct device *dev) IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), sdadc_smartbond_isr, DEVICE_DT_INST_GET(0), 0); - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); - NVIC_EnableIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); + irq_enable(DT_INST_IRQN(0)); adc_context_unlock_unconditionally(&data->ctx); From 9de57e70410cc2f988b13167c0ccfd8dda018f8b Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:09:11 -0400 Subject: [PATCH 040/600] drivers: entropy: smartbond: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ()/ NVIC_EnableIRQ() calls with the portable k_irq_set_pending()/ k_irq_clear_pending()/irq_enable() equivalents. The Smartbond DA1469x is a plain-NVIC Cortex-M33, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/entropy/entropy_smartbond.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/entropy/entropy_smartbond.c b/drivers/entropy/entropy_smartbond.c index 197e801e8536..85d4c724eded 100644 --- a/drivers/entropy/entropy_smartbond.c +++ b/drivers/entropy/entropy_smartbond.c @@ -94,7 +94,7 @@ static void trng_enable(bool enable) } else { CRG_TOP->CLK_AMBA_REG &= ~CRG_TOP_CLK_AMBA_REG_TRNG_CLK_ENABLE_Msk; TRNG->TRNG_CTRL_REG = 0; - NVIC_ClearPendingIRQ(IRQN); + k_irq_clear_pending(IRQN); entropy_smartbond_pm_policy_state_lock_put(); } @@ -323,7 +323,7 @@ static int entropy_smartbond_get_entropy_isr(const struct device *dev, uint8_t * * to 1 (the bit is set when NVIC pending IRQ status is * changed from 0 to 1) */ - NVIC_ClearPendingIRQ(IRQN); + k_irq_clear_pending(IRQN); do { uint8_t bytes[4]; @@ -345,7 +345,7 @@ static int entropy_smartbond_get_entropy_isr(const struct device *dev, uint8_t * __WFE(); } - NVIC_ClearPendingIRQ(IRQN); + k_irq_clear_pending(IRQN); if (random_word_get(bytes) != 0) { continue; } From c01e2c55a7dbabd9d569ab44818aa1e4e6854f18 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:09:11 -0400 Subject: [PATCH 041/600] drivers: counter: smartbond: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ()/ NVIC_EnableIRQ() calls with the portable k_irq_set_pending()/ k_irq_clear_pending()/irq_enable() equivalents. The Smartbond DA1469x is a plain-NVIC Cortex-M33, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_smartbond_timer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/counter/counter_smartbond_timer.c b/drivers/counter/counter_smartbond_timer.c index d6afecef900e..a0b50c800de5 100644 --- a/drivers/counter/counter_smartbond_timer.c +++ b/drivers/counter/counter_smartbond_timer.c @@ -269,7 +269,7 @@ static int counter_smartbond_set_alarm(const struct device *dev, uint8_t chan, * for absolute depending on the flag. */ if (irq_on_late) { - NVIC_SetPendingIRQ(config->irqn); + k_irq_set_pending(config->irqn); } else { data->callback = NULL; } @@ -280,7 +280,7 @@ static int counter_smartbond_set_alarm(const struct device *dev, uint8_t chan, * should be triggered. No need to enable interrupt * on TIMER just make sure interrupt is pending. */ - NVIC_SetPendingIRQ(config->irqn); + k_irq_set_pending(config->irqn); } else { timer->TIMER2_CTRL_REG |= TIMER2_TIMER2_CTRL_REG_TIM_IRQ_EN_Msk; } @@ -322,7 +322,7 @@ static uint32_t counter_smartbond_get_pending_int(const struct device *dev) /* There is no register to check TIMER peripheral to check for interrupt * pending, check directly in NVIC. */ - return NVIC_GetPendingIRQ(config->irqn); + return k_irq_is_pending(config->irqn); } static int counter_smartbond_init_timer(const struct device *dev) From 09ffe52fd528826ae0c944d075085e64ebf8dd1d Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:09:11 -0400 Subject: [PATCH 042/600] drivers: timer: smartbond: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ()/ NVIC_EnableIRQ() calls with the portable k_irq_set_pending()/ k_irq_clear_pending()/irq_enable() equivalents. The Smartbond DA1469x is a plain-NVIC Cortex-M33, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/timer/smartbond_timer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/timer/smartbond_timer.c b/drivers/timer/smartbond_timer.c index 133b6c27e371..3cf2ded4c088 100644 --- a/drivers/timer/smartbond_timer.c +++ b/drivers/timer/smartbond_timer.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -103,7 +102,7 @@ static void schedule_next_interrupt(uint32_t ticks) * not but time expired anyway so make sure that interrupt is pending. */ if ((int32_t)(target_val - timer_val_32_noupdate() - 1) < 0) { - NVIC_SetPendingIRQ(TIMER2_IRQn); + k_irq_set_pending(TIMER2_IRQn); } } From a82cc1085d0d762c43192754240bc87f7fdbcb17 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:09:11 -0400 Subject: [PATCH 043/600] drivers: usb: smartbond: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ()/ NVIC_EnableIRQ() calls with the portable k_irq_set_pending()/ k_irq_clear_pending()/irq_enable() equivalents. The Smartbond DA1469x is a plain-NVIC Cortex-M33, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/usb/device/usb_dc_smartbond.c | 3 ++- drivers/usb/udc/udc_smartbond.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/device/usb_dc_smartbond.c b/drivers/usb/device/usb_dc_smartbond.c index 60990d00946f..9fa00384e477 100644 --- a/drivers/usb/device/usb_dc_smartbond.c +++ b/drivers/usb/device/usb_dc_smartbond.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -1152,7 +1153,7 @@ static int usb_init(void) IRQ_CONNECT(VBUS_IRQ, VBUS_IRQ_PRI, usb_dc_smartbond_vbus_isr, 0, 0); CRG_TOP->VBUS_IRQ_CLEAR_REG = 1; - NVIC_ClearPendingIRQ(VBUS_IRQ); + k_irq_clear_pending(VBUS_IRQ); /* Both connect and disconnect needs to be handled */ CRG_TOP->VBUS_IRQ_MASK_REG = CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_FALL_Msk | CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_RISE_Msk; diff --git a/drivers/usb/udc/udc_smartbond.c b/drivers/usb/udc/udc_smartbond.c index 3e7445e9c2bd..be606b994059 100644 --- a/drivers/usb/udc/udc_smartbond.c +++ b/drivers/usb/udc/udc_smartbond.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -1613,7 +1614,7 @@ static int udc_smartbond_init(const struct device *dev) /* Both connect and disconnect needs to be handled */ CRG_TOP->VBUS_IRQ_MASK_REG = CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_FALL_Msk | CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_RISE_Msk; - NVIC_SetPendingIRQ(config->vbus_irq); + k_irq_set_pending(config->vbus_irq); irq_enable(config->vbus_irq); return 0; From 1b5fa366b92e1ba7611bb035f8bfc6a14d478ec5 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:09:11 -0400 Subject: [PATCH 044/600] drivers: bluetooth: hci_da1469x: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ()/ NVIC_EnableIRQ() calls with the portable k_irq_set_pending()/ k_irq_clear_pending()/irq_enable() equivalents. The Smartbond DA1469x is a plain-NVIC Cortex-M33, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/bluetooth/hci/hci_da1469x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci/hci_da1469x.c b/drivers/bluetooth/hci/hci_da1469x.c index f44e9acd4cc8..45a968d42eeb 100644 --- a/drivers/bluetooth/hci/hci_da1469x.c +++ b/drivers/bluetooth/hci/hci_da1469x.c @@ -194,7 +194,7 @@ static void rx_isr_start(void) { if (rx.deferred) { rx.deferred = false; - NVIC_SetPendingIRQ(CMAC2SYS_IRQn); + k_irq_set_pending(CMAC2SYS_IRQn); } irq_enable(CMAC2SYS_IRQn); From 2be19f6503a91f3d335c52ce6811d02ed5917207 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:13:20 -0400 Subject: [PATCH 045/600] drivers: peci: npcx: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. These platforms are plain-NVIC Cortex-M, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/peci/peci_npcx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/peci/peci_npcx.c b/drivers/peci/peci_npcx.c index 9a0518a6c21e..339f7da8e76a 100644 --- a/drivers/peci/peci_npcx.c +++ b/drivers/peci/peci_npcx.c @@ -124,7 +124,7 @@ static int peci_npcx_enable(const struct device *dev) reg->PECI_CTL_STS = BIT(NPCX_PECI_CTL_STS_DONE) | BIT(NPCX_PECI_CTL_STS_CRC_ERR) | BIT(NPCX_PECI_CTL_STS_ABRT_ERR); - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); irq_enable(DT_INST_IRQN(0)); k_sem_give(&data->lock); From 3a09d2c8b0f44cc0a5cecb7d452dcd936c9a7824 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:13:20 -0400 Subject: [PATCH 046/600] mgmt: ec_host_cmd: shi_npcx: use portable IRQ pending API Replace direct NVIC_SetPendingIRQ()/NVIC_ClearPendingIRQ() calls with k_irq_set_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. These platforms are plain-NVIC Cortex-M, so the pending-state capabilities are always available where this code builds. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- .../mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c b/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c index 3c2ba60e5f47..8bdaa92a2c56 100644 --- a/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c +++ b/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c @@ -6,6 +6,7 @@ #include "ec_host_cmd_backend_shi.h" +#include #include #include #include @@ -851,7 +852,7 @@ static int shi_npcx_enable(const struct device *dev) return ret; } - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + k_irq_clear_pending(DT_INST_IRQN(0)); /* * Clear the pending bit because switching the pinmux (pinctrl) might cause a faking WUI * pending bit set. From 8db81989fb874b285a40d2aaff7ee5640f64e359 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:38:06 -0400 Subject: [PATCH 047/600] drivers: counter: bee: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/counter/counter_bee_rtc.c | 4 ++-- drivers/counter/counter_bee_timer.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/counter/counter_bee_rtc.c b/drivers/counter/counter_bee_rtc.c index 45052e8ecade..92bd7197f092 100644 --- a/drivers/counter/counter_bee_rtc.c +++ b/drivers/counter/counter_bee_rtc.c @@ -319,11 +319,11 @@ static DEVICE_API(counter, counter_bee_rtc_driver_api) = { RTC_IRQ_CONFIG_FUNC(index); \ static void set_irq_pending_##index(void) \ { \ - (NVIC_SetPendingIRQ(DT_INST_IRQN(index))); \ + (k_irq_set_pending(DT_INST_IRQN(index))); \ } \ static uint32_t get_irq_pending_##index(void) \ { \ - return NVIC_GetPendingIRQ(DT_INST_IRQN(index)); \ + return k_irq_is_pending(DT_INST_IRQN(index)); \ } #define BEE_RTC_INIT(index) \ diff --git a/drivers/counter/counter_bee_timer.c b/drivers/counter/counter_bee_timer.c index 5ca8cc0fb249..b6beb835ef5a 100644 --- a/drivers/counter/counter_bee_timer.c +++ b/drivers/counter/counter_bee_timer.c @@ -346,7 +346,7 @@ static DEVICE_API(counter, counter_bee_timer_driver_api) = { TIMER_IRQ_HANDLER(index); \ static uint32_t get_irq_pending_##index(void) \ { \ - return NVIC_GetPendingIRQ(DT_IRQN(PARENT_NODE(index))); \ + return k_irq_is_pending(DT_IRQN(PARENT_NODE(index))); \ } #if defined(CONFIG_SOC_SERIES_RTL87X2G) From 49cfe1fac41e40ba845936811f0cd99fddc75aa3 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:38:06 -0400 Subject: [PATCH 048/600] drivers: watchdog: bee: use portable IRQ pending API Replace direct NVIC pending-state calls with k_irq_set_pending()/ k_irq_is_pending()/k_irq_clear_pending(), dropping the dependency on cmsis_core.h where nothing else needed it. Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- drivers/watchdog/wdt_bee_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/wdt_bee_core.c b/drivers/watchdog/wdt_bee_core.c index 3311408f95fb..e52edb9f55a3 100644 --- a/drivers/watchdog/wdt_bee_core.c +++ b/drivers/watchdog/wdt_bee_core.c @@ -6,6 +6,7 @@ #define DT_DRV_COMPAT realtek_bee_core_wdt +#include #include #include #include @@ -180,7 +181,7 @@ static int core_wdt_bee_init(const struct device *dev) nvic_init_struct.NVIC_IRQChannelPriority = 0; NVIC_Init(&nvic_init_struct); #else - NVIC_ClearPendingIRQ(config->irq_num); + k_irq_clear_pending(config->irq_num); config->cfg_func(); #endif From 392e04a79507841b02158ca8d81455e328723f2c Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 14 Aug 2026 23:39:57 -0400 Subject: [PATCH 049/600] soc: realtek: bee: use portable IRQ pending API Replace the NVIC_GetEnableIRQ()/NVIC_EnableIRQ()/NVIC_DisableIRQ() calls in the vector-table takeover loop with irq_is_enabled()/ irq_enable()/irq_disable(). Assisted-by: Claude:claude-opus-5 Signed-off-by: Anas Nashif --- soc/realtek/bee/rtl8752h/soc.c | 7 ++++--- soc/realtek/bee/rtl87x2g/soc.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/soc/realtek/bee/rtl8752h/soc.c b/soc/realtek/bee/rtl8752h/soc.c index f056b2cb6c57..3799cd442333 100644 --- a/soc/realtek/bee/rtl8752h/soc.c +++ b/soc/realtek/bee/rtl8752h/soc.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -60,11 +61,11 @@ static void rtl8752h_isr_register(void) uint32_t expected_zephyr_isr = FlashVectorTable_INT[irq]; if (current_isr != expected_zephyr_isr) { - if (NVIC_GetEnableIRQ(irq) == 1) { - NVIC_DisableIRQ(irq); + if (irq_is_enabled(irq) != 0) { + irq_disable(irq); z_isr_install(irq, (void *)current_isr, NULL); RamVectorTableUpdate(irq + 16, (IRQ_Fun)_isr_wrapper); - NVIC_EnableIRQ(irq); + irq_enable(irq); } else { z_isr_install(irq, (void *)current_isr, NULL); RamVectorTableUpdate(irq + 16, (IRQ_Fun)_isr_wrapper); diff --git a/soc/realtek/bee/rtl87x2g/soc.c b/soc/realtek/bee/rtl87x2g/soc.c index c2acded7ac4b..8a26e3770886 100644 --- a/soc/realtek/bee/rtl87x2g/soc.c +++ b/soc/realtek/bee/rtl87x2g/soc.c @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -116,11 +117,11 @@ static void rtl87x2g_isr_register(void) uint32_t expected_zephyr_isr = FlashVectorTable_INT[irq]; if (current_isr != expected_zephyr_isr) { - if (NVIC_GetEnableIRQ(irq) == 1) { - NVIC_DisableIRQ(irq); + if (irq_is_enabled(irq) != 0) { + irq_disable(irq); z_isr_install(irq, (void *)current_isr, NULL); RamVectorTableUpdate(irq + 16, (IRQ_Fun)_isr_wrapper); - NVIC_EnableIRQ(irq); + irq_enable(irq); } else { z_isr_install(irq, (void *)current_isr, NULL); RamVectorTableUpdate(irq + 16, (IRQ_Fun)_isr_wrapper); From 34d80445149c312d08414c71c6d0951688a10449 Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Sun, 23 Aug 2026 23:55:12 -0400 Subject: [PATCH 050/600] drivers: ethernet: phy: tja1103: init work before enabling IRQ The delayable phy_work item was initialized only after the link interrupt had already been enabled and armed. On hardware that asserts the PHY interrupt GPIO immediately at configuration time, the ISR (phy_tja1103_handle_irq) could run and call k_work_reschedule() on phy_work before k_work_init_delayable() had ever executed, rescheduling a work item with no valid handler and corrupting the workqueue state (fault or undefined behavior on early link-up, observed on TJA1103-based 100BASE-T1 hardware). Initialize data->timeout and the delayable work at the top of phy_tja1103_cfg_irq_poll(), before the interrupt is enabled, so the handler is always valid when the ISR fires. Replace the direct phy_work_handler() call at the end with a K_NO_WAIT reschedule so the first poll still runs from the workqueue context rather than inline. Signed-off-by: Benjamin Perseghetti --- drivers/ethernet/phy/phy_tja1103.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/ethernet/phy/phy_tja1103.c b/drivers/ethernet/phy/phy_tja1103.c index a8834dafdda1..d0a48d5e81aa 100644 --- a/drivers/ethernet/phy/phy_tja1103.c +++ b/drivers/ethernet/phy/phy_tja1103.c @@ -257,6 +257,13 @@ static void phy_tja1103_cfg_irq_poll(const struct device *dev) { struct phy_tja1103_data *const data = dev->data; + /* The interrupt can fire as soon as its GPIO is enabled. Initialize the + * delayable work before enabling that interrupt so the ISR can always + * reschedule a work item with a valid handler. + */ + data->timeout = sys_timepoint_calc(K_MSEC(SWITCH_WAIT_RANGE(1000, 3000))); + k_work_init_delayable(&data->phy_work, phy_work_handler); + #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) int ret; const struct phy_tja1103_config *const cfg = dev->config; @@ -298,11 +305,7 @@ static void phy_tja1103_cfg_irq_poll(const struct device *dev) } #endif - data->timeout = sys_timepoint_calc(K_MSEC(SWITCH_WAIT_RANGE(1000, 3000))); - - k_work_init_delayable(&data->phy_work, phy_work_handler); - - phy_work_handler(&data->phy_work.work); + (void)k_work_reschedule(&data->phy_work, K_NO_WAIT); } static int phy_tja1103_init(const struct device *dev) From d4fa60f022e15a137bc9e3e65f3009fa2edb719b Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Fri, 14 Aug 2026 10:45:39 -0400 Subject: [PATCH 051/600] drivers: sensor: icm45686: zero int_config before disabling INTs icm45686_stream_init passed an uninitialized int_config to icm456xx_set_config_int, whose purpose there is to disable every INT1 source. The unwritten fields are stack garbage, so the call could instead enable arbitrary INT1 sources and assert a data-ready edge before the RTIO stream is armed. The APEX (icm45686.c) and trigger (icm45686_trigger.c) init paths already memset it to INV_IMU_DISABLE first, so do the same on the streaming path. Signed-off-by: Benjamin Perseghetti --- drivers/sensor/tdk/icm45686/icm45686_stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/sensor/tdk/icm45686/icm45686_stream.c b/drivers/sensor/tdk/icm45686/icm45686_stream.c index 3dd8c0517ddf..878db072c3fd 100644 --- a/drivers/sensor/tdk/icm45686/icm45686_stream.c +++ b/drivers/sensor/tdk/icm45686/icm45686_stream.c @@ -570,6 +570,7 @@ int icm45686_stream_init(const struct device *dev) LOG_ERR("Failed to configure interrupt"); } + memset(&int_config, INV_IMU_DISABLE, sizeof(int_config)); err = icm456xx_set_config_int(&data->driver, INV_IMU_INT1, &int_config); if (err) { LOG_ERR("Failed to disable all INTs"); From 326d1599ddb94ad280da2da9cc573d2c201a4ee5 Mon Sep 17 00:00:00 2001 From: James Goppert Date: Fri, 14 Aug 2026 10:09:30 -0400 Subject: [PATCH 052/600] drivers: sensor: icm45686: ignore early stream interrupts icm45686_event_handler loaded read_cfg from data->stream.iodev_sqe->sqe.iodev->data at function entry, before the guard that checks whether iodev_sqe is NULL. A data-ready edge that arrives before a streaming submission is armed therefore dereferenced a NULL pointer. Handle the no-submission case in its own guard that ignores the spurious interrupt and returns, keep the cancelled submission path separate, and defer the read_cfg load until after both checks. Add a driver test that invokes the handler with no submission armed and confirms the interrupt is ignored. The test compiles the stream translation unit directly with stubbed bus helpers so the guard can be exercised without a full bus and device instance, and enables CONFIG_ICM45686_STREAM through Kconfig so the driver data layout matches the streaming build. Signed-off-by: James Goppert Signed-off-by: Benjamin Perseghetti --- drivers/sensor/tdk/icm45686/icm45686_stream.c | 11 ++-- .../sensor/icm45686_stream/CMakeLists.txt | 15 ++++++ tests/drivers/sensor/icm45686_stream/Kconfig | 12 +++++ tests/drivers/sensor/icm45686_stream/prj.conf | 5 ++ .../drivers/sensor/icm45686_stream/src/main.c | 50 +++++++++++++++++++ .../drivers/sensor/icm45686_stream/tests.yaml | 7 +++ 6 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 tests/drivers/sensor/icm45686_stream/CMakeLists.txt create mode 100644 tests/drivers/sensor/icm45686_stream/Kconfig create mode 100644 tests/drivers/sensor/icm45686_stream/prj.conf create mode 100644 tests/drivers/sensor/icm45686_stream/src/main.c create mode 100644 tests/drivers/sensor/icm45686_stream/tests.yaml diff --git a/drivers/sensor/tdk/icm45686/icm45686_stream.c b/drivers/sensor/tdk/icm45686/icm45686_stream.c index 878db072c3fd..91cfafc7a1ce 100644 --- a/drivers/sensor/tdk/icm45686/icm45686_stream.c +++ b/drivers/sensor/tdk/icm45686/icm45686_stream.c @@ -187,13 +187,17 @@ static void icm45686_event_handler(const struct device *dev) { struct icm45686_data *data = dev->data; const struct icm45686_config *cfg = dev->config; - const struct sensor_read_config *read_cfg = data->stream.iodev_sqe->sqe.iodev->data; + const struct sensor_read_config *read_cfg; uint8_t val = 0; uint64_t cycles; int err; - if (!data->stream.iodev_sqe || - FIELD_GET(RTIO_SQE_CANCELED, data->stream.iodev_sqe->sqe.flags)) { + if (!data->stream.iodev_sqe) { + LOG_WRN("Callback triggered before a streaming submission - Ignoring"); + return; + } + + if (FIELD_GET(RTIO_SQE_CANCELED, data->stream.iodev_sqe->sqe.flags)) { LOG_WRN("Callback triggered with no streaming submission - Disabling interrupts"); (void)atomic_set(&data->stream.state, ICM45686_STREAM_OFF); (void)gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_DISABLE); @@ -211,6 +215,7 @@ static void icm45686_event_handler(const struct device *dev) data->stream.settings.enabled.fifo_full = false; return; } + read_cfg = data->stream.iodev_sqe->sqe.iodev->data; if (atomic_cas(&data->stream.state, ICM45686_STREAM_ON, ICM45686_STREAM_BUSY) == false) { LOG_WRN("Event handler triggered while a stream is in progress! Ignoring"); diff --git a/tests/drivers/sensor/icm45686_stream/CMakeLists.txt b/tests/drivers/sensor/icm45686_stream/CMakeLists.txt new file mode 100644 index 000000000000..1babef7a6ba7 --- /dev/null +++ b/tests/drivers/sensor/icm45686_stream/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2026 CogniPilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(icm45686_stream) + +target_include_directories(app PRIVATE + ${ZEPHYR_BASE}/drivers/sensor/tdk/icm45686 + ${ZEPHYR_HAL_TDK_MODULE_DIR} + ${ZEPHYR_HAL_TDK_MODULE_DIR}/common + ${ZEPHYR_HAL_TDK_MODULE_DIR}/icm456xx + ${ZEPHYR_HAL_TDK_MODULE_DIR}/icm456xx/icm456xx_h +) +target_sources(app PRIVATE src/main.c) diff --git a/tests/drivers/sensor/icm45686_stream/Kconfig b/tests/drivers/sensor/icm45686_stream/Kconfig new file mode 100644 index 000000000000..f5ee5bb88573 --- /dev/null +++ b/tests/drivers/sensor/icm45686_stream/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2026 CogniPilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +source "Kconfig.zephyr" + +# The test compiles the streaming translation unit directly, without a +# device-tree instance of the sensor, so the driver's own dependency chain +# for this symbol is not active. Provide the value here so the shared +# driver data layout selects its streaming fields. +config ICM45686_STREAM + bool + default y diff --git a/tests/drivers/sensor/icm45686_stream/prj.conf b/tests/drivers/sensor/icm45686_stream/prj.conf new file mode 100644 index 000000000000..cd0169451119 --- /dev/null +++ b/tests/drivers/sensor/icm45686_stream/prj.conf @@ -0,0 +1,5 @@ +CONFIG_ZTEST=y +CONFIG_GPIO=y +CONFIG_SENSOR=y +CONFIG_SENSOR_ASYNC_API=y +CONFIG_SENSOR_CLOCK_SYSTEM=y diff --git a/tests/drivers/sensor/icm45686_stream/src/main.c b/tests/drivers/sensor/icm45686_stream/src/main.c new file mode 100644 index 000000000000..75a862c50a59 --- /dev/null +++ b/tests/drivers/sensor/icm45686_stream/src/main.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "icm45686_stream.c" + +int icm45686_prep_reg_read_rtio_async(const struct icm45686_bus *bus, uint8_t reg, uint8_t *buf, + size_t size, struct rtio_sqe **out) +{ + ARG_UNUSED(bus); + ARG_UNUSED(reg); + ARG_UNUSED(buf); + ARG_UNUSED(size); + ARG_UNUSED(out); + return -ENOTSUP; +} + +int icm45686_prep_reg_write_rtio_async(const struct icm45686_bus *bus, uint8_t reg, + const uint8_t *buf, size_t size, struct rtio_sqe **out) +{ + ARG_UNUSED(bus); + ARG_UNUSED(reg); + ARG_UNUSED(buf); + ARG_UNUSED(size); + ARG_UNUSED(out); + return -ENOTSUP; +} + +ZTEST(icm45686_stream, test_early_irq_without_submission_is_ignored) +{ + static const struct icm45686_config config; + struct icm45686_data data = {0}; + const struct device dev = { + .name = "icm45686-test", + .config = &config, + .data = &data, + }; + + icm45686_event_handler(&dev); + + zassert_is_null(data.stream.iodev_sqe); + zassert_equal(atomic_get(&data.stream.state), ICM45686_STREAM_OFF); +} + +ZTEST_SUITE(icm45686_stream, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/sensor/icm45686_stream/tests.yaml b/tests/drivers/sensor/icm45686_stream/tests.yaml new file mode 100644 index 000000000000..13bbb05b8e2c --- /dev/null +++ b/tests/drivers/sensor/icm45686_stream/tests.yaml @@ -0,0 +1,7 @@ +tests: + drivers.sensor.icm45686_stream.early_irq: + tags: + - drivers + - sensor + platform_allow: + - native_sim From f9e0631a5fcb80a0a46c6bab038ab09b851213f3 Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Thu, 20 Aug 2026 16:45:10 -0400 Subject: [PATCH 053/600] drivers: sensor: icm45686: rate-limit stream ignore-path logs The event handler drops an interrupt whenever a stream is already in progress or no submission is armed. During a stall these fire on every data-ready edge, and one warning per event floods the log backend and can starve the threads that would clear the stall. Count the dropped events and emit at most one summary per second so the fault stays visible without flooding. A summary that carries the suppressed count is used rather than a plain rate-limited log (LOG_WRN_RATELIMIT) so the operator sees how many edges were dropped in the interval, not just that dropping occurred. The counters and the report deadline live in per-instance driver data so a two-IMU system attributes and rate-limits each sensor independently instead of aggregating them into one shared total. An alternative that removes the busy-path re-entries outright, disabling the DRDY interrupt on entry to ICM45686_STREAM_BUSY and re-arming it from icm45686_stream_submit, was considered and set aside: it changes the pulse-mode interrupt timing for every user of this driver, so it is left for a separate change validated on hardware. Signed-off-by: Benjamin Perseghetti --- drivers/sensor/tdk/icm45686/icm45686.h | 9 +++++ drivers/sensor/tdk/icm45686/icm45686_stream.c | 39 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/drivers/sensor/tdk/icm45686/icm45686.h b/drivers/sensor/tdk/icm45686/icm45686.h index 8d5f0787da09..ef41e789115a 100644 --- a/drivers/sensor/tdk/icm45686/icm45686.h +++ b/drivers/sensor/tdk/icm45686/icm45686.h @@ -140,6 +140,15 @@ struct icm45686_stream { bool fifo_full: 1; } events; } data; + /* Counts of interrupts dropped by each ignore path, summarized at + * most once per report interval. Per-instance so a multi-device + * system attributes and rate-limits each sensor independently. + */ + struct { + atomic_t busy_ignored; + atomic_t no_submission_ignored; + int64_t report_deadline; + } ignore_stats; }; struct icm45686_data { diff --git a/drivers/sensor/tdk/icm45686/icm45686_stream.c b/drivers/sensor/tdk/icm45686/icm45686_stream.c index 91cfafc7a1ce..224b0adf4dad 100644 --- a/drivers/sensor/tdk/icm45686/icm45686_stream.c +++ b/drivers/sensor/tdk/icm45686/icm45686_stream.c @@ -30,6 +30,34 @@ enum icm45686_stream_state { ICM45686_STREAM_BUSY = 2, }; +/* + * Both ignore paths in icm45686_event_handler can recur on every interrupt + * while a stream is stalled. Logging each occurrence floods the backend and + * can starve the very threads that would clear the stall, so count the + * occurrences and emit at most one summary per second. + */ +static void icm45686_report_ignored_events(struct icm45686_data *data) +{ + int64_t now = k_uptime_get(); + uint32_t busy; + uint32_t no_sub; + + if (now < data->stream.ignore_stats.report_deadline) { + return; + } + data->stream.ignore_stats.report_deadline = now + 1000; + + busy = (uint32_t)atomic_set(&data->stream.ignore_stats.busy_ignored, 0); + no_sub = (uint32_t)atomic_set(&data->stream.ignore_stats.no_submission_ignored, 0); + + if (busy != 0U) { + LOG_WRN("Ignored %u interrupt(s): event while a stream was in progress", busy); + } + if (no_sub != 0U) { + LOG_WRN("Ignored %u interrupt(s): callback before a streaming submission", no_sub); + } +} + static struct sensor_stream_trigger *get_read_config_trigger(const struct sensor_read_config *cfg, enum sensor_trigger_type trig) { @@ -193,7 +221,8 @@ static void icm45686_event_handler(const struct device *dev) int err; if (!data->stream.iodev_sqe) { - LOG_WRN("Callback triggered before a streaming submission - Ignoring"); + (void)atomic_inc(&data->stream.ignore_stats.no_submission_ignored); + icm45686_report_ignored_events(data); return; } @@ -218,7 +247,13 @@ static void icm45686_event_handler(const struct device *dev) read_cfg = data->stream.iodev_sqe->sqe.iodev->data; if (atomic_cas(&data->stream.state, ICM45686_STREAM_ON, ICM45686_STREAM_BUSY) == false) { - LOG_WRN("Event handler triggered while a stream is in progress! Ignoring"); + /* + * A data-ready edge arrived while the previous readout was still + * in flight. Drop the event but intentionally leave the DRDY + * interrupt armed so the next edge after completion is serviced. + */ + (void)atomic_inc(&data->stream.ignore_stats.busy_ignored); + icm45686_report_ignored_events(data); return; } From 1661f944a029c2a10bd1deb4011bb147407984be Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Sun, 23 Aug 2026 23:56:29 -0400 Subject: [PATCH 054/600] drivers: sensor: rm3100: only set the SPI read bit on SPI transfers The bus read helpers unconditionally OR the SPI read-address flag into the register address. On I2C the register address must be sent raw: with the flag set every read addresses a nonexistent register and returns zeros, so the sensor appears dead on I2C buses. Gate the flag on the RTIO bus type so I2C transfers send the plain register address while SPI behavior is unchanged. Signed-off-by: Benjamin Perseghetti --- drivers/sensor/pni/rm3100/rm3100.c | 6 +++++- drivers/sensor/pni/rm3100/rm3100_bus.h | 8 +++++++- drivers/sensor/pni/rm3100/rm3100_stream.c | 10 ++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/sensor/pni/rm3100/rm3100.c b/drivers/sensor/pni/rm3100/rm3100.c index 2a27c9a85483..d94eed9b5436 100644 --- a/drivers/sensor/pni/rm3100/rm3100.c +++ b/drivers/sensor/pni/rm3100/rm3100.c @@ -90,7 +90,11 @@ static void rm3100_submit_one_shot(const struct device *dev, struct rtio_iodev_s return; } - uint8_t val = RM3100_REG_MX | REG_READ_BIT; + uint8_t val = RM3100_REG_MX; + + if (rtio_is_spi(data->rtio.type)) { + val |= REG_READ_BIT; + } rtio_sqe_prep_tiny_write(write_sqe, data->rtio.iodev, diff --git a/drivers/sensor/pni/rm3100/rm3100_bus.h b/drivers/sensor/pni/rm3100/rm3100_bus.h index 8517f7995979..be47b037e6cd 100644 --- a/drivers/sensor/pni/rm3100/rm3100_bus.h +++ b/drivers/sensor/pni/rm3100/rm3100_bus.h @@ -31,7 +31,13 @@ static inline int rm3100_bus_read(const struct device *dev, return -ENOMEM; } - reg = reg | REG_READ_BIT; + /* The read-address flag is an SPI-only convention. Set it only on + * SPI transfers. On I2C the register address must be sent raw, or + * reads hit a nonexistent register and return zeros. + */ + if (rtio_is_spi(data->rtio.type)) { + reg = reg | REG_READ_BIT; + } rtio_sqe_prep_write(write_sqe, iodev, RTIO_PRIO_HIGH, ®, 1, NULL); write_sqe->flags |= RTIO_SQE_TRANSACTION; diff --git a/drivers/sensor/pni/rm3100/rm3100_stream.c b/drivers/sensor/pni/rm3100/rm3100_stream.c index a216389a02fd..d367bba2eddc 100644 --- a/drivers/sensor/pni/rm3100/rm3100_stream.c +++ b/drivers/sensor/pni/rm3100/rm3100_stream.c @@ -112,7 +112,10 @@ static void rm3100_stream_get_data(const struct device *dev) uint8_t val; - val = RM3100_REG_STATUS | REG_READ_BIT; + val = RM3100_REG_STATUS; + if (rtio_is_spi(data->rtio.type)) { + val |= REG_READ_BIT; + } rtio_sqe_prep_tiny_write(status_wr_sqe, data->rtio.iodev, @@ -134,7 +137,10 @@ static void rm3100_stream_get_data(const struct device *dev) } status_rd_sqe->flags |= RTIO_SQE_CHAINED; - val = RM3100_REG_MX | REG_READ_BIT; + val = RM3100_REG_MX; + if (rtio_is_spi(data->rtio.type)) { + val |= REG_READ_BIT; + } rtio_sqe_prep_tiny_write(write_sqe, data->rtio.iodev, From 2fee0f1be7c89b8e8548fdde0f9f5ad763b528e9 Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Sun, 23 Aug 2026 23:57:24 -0400 Subject: [PATCH 055/600] drivers: sensor: rm3100: encode a fixed channel spec when streaming In a streaming read-config the channels union member holds the trigger array, so the encoded channel mask was computed from reinterpreted trigger data and came out 0, making every streamed frame fail decode with -ENODATA. A data-ready event always carries all three axes, so encode a fixed MAGN_XYZ channel spec in the stream path. Signed-off-by: Benjamin Perseghetti --- drivers/sensor/pni/rm3100/rm3100_stream.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/sensor/pni/rm3100/rm3100_stream.c b/drivers/sensor/pni/rm3100/rm3100_stream.c index d367bba2eddc..924701ac5f04 100644 --- a/drivers/sensor/pni/rm3100/rm3100_stream.c +++ b/drivers/sensor/pni/rm3100/rm3100_stream.c @@ -17,6 +17,15 @@ #include LOG_MODULE_REGISTER(RM3100_STREAM, CONFIG_SENSOR_LOG_LEVEL); +/** A streaming read-config describes triggers, not channels: struct sensor_read_config + * keeps "channels" and "triggers" in the same union and "count" is the trigger count. + * A data-ready event always carries all three axes, so the encoded channel mask is + * fixed here instead of being derived from the read-config. + */ +static const struct sensor_chan_spec rm3100_stream_chan_spec[] = { + { SENSOR_CHAN_MAGN_XYZ, 0 }, +}; + static void rm3100_complete_result(struct rtio *ctx, const struct rtio_sqe *sqe, int err, void *arg) { @@ -69,9 +78,6 @@ static void rm3100_stream_get_data(const struct device *dev) } struct rtio_iodev_sqe *iodev_sqe = data->stream.iodev_sqe; - const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; - const struct sensor_chan_spec *const channels = cfg->channels; - const size_t num_channels = cfg->count; uint8_t *buf; uint32_t buf_len; uint32_t min_buf_len = sizeof(struct rm3100_encoded_data); @@ -88,7 +94,8 @@ static void rm3100_stream_get_data(const struct device *dev) edata = (struct rm3100_encoded_data *)buf; - err = rm3100_encode(dev, channels, num_channels, buf); + err = rm3100_encode(dev, rm3100_stream_chan_spec, + ARRAY_SIZE(rm3100_stream_chan_spec), buf); if (err != 0) { LOG_ERR("Failed to encode sensor data"); rtio_iodev_sqe_err(iodev_sqe, err); From 877e9bcf4ed87f10f1ff9deeecdd874360a0c48a Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Sun, 23 Aug 2026 23:58:26 -0400 Subject: [PATCH 056/600] drivers: sensor: rm3100: fix bus SQE pool corruption under streaming load The RM3100 streaming read acquires five bus SQEs one at a time and, on a short pool, calls rtio_sqe_drop_all() to clean up. rtio_sqe_drop_all frees every SQE queued on the shared bus RTIO context, including SQEs still in flight from another read chain, returning an in-use SQE to the pool. A later allocation then hands out that SQE with a corrupted back-pointer and the completion path dereferences it, hard-faulting in the RTIO executor (observed on I2C RM3100 hardware as a bus fault in the sensor work queue after "Failed to acquire RTIO SQEs"). Acquire the five SQEs of a chain as one array with rtio_sqe_acquire_array, which pushes nothing until all five are held and rolls its own back on shortage, so a partial acquire can no longer orphan or double-free an SQE. The incomplete NULL check that omitted the two status SQEs is retired with it. Double the bus context pool from 8 to 16 so a five-SQE chain does not run the pool short under streaming load. The one-shot read path still acquires its SQEs individually and returns early on shortage without preparing them. That is a lower-pressure path outside the streaming scope of this change and is left to a follow-up. Signed-off-by: Benjamin Perseghetti --- drivers/sensor/pni/rm3100/rm3100.c | 2 +- drivers/sensor/pni/rm3100/rm3100_stream.c | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/sensor/pni/rm3100/rm3100.c b/drivers/sensor/pni/rm3100/rm3100.c index d94eed9b5436..5e08f0772ac2 100644 --- a/drivers/sensor/pni/rm3100/rm3100.c +++ b/drivers/sensor/pni/rm3100/rm3100.c @@ -227,7 +227,7 @@ static int rm3100_init(const struct device *dev) #define RM3100_DEFINE(inst) \ \ - RTIO_DEFINE(rm3100_rtio_ctx_##inst, 8, 8); \ + RTIO_DEFINE(rm3100_rtio_ctx_##inst, 16, 16); \ COND_CODE_1(DT_INST_ON_BUS(inst, i2c), \ (I2C_DT_IODEV_DEFINE(rm3100_bus_##inst, DT_DRV_INST(inst))), \ ()); \ diff --git a/drivers/sensor/pni/rm3100/rm3100_stream.c b/drivers/sensor/pni/rm3100/rm3100_stream.c index 924701ac5f04..2fc43db57528 100644 --- a/drivers/sensor/pni/rm3100/rm3100_stream.c +++ b/drivers/sensor/pni/rm3100/rm3100_stream.c @@ -102,21 +102,28 @@ static void rm3100_stream_get_data(const struct device *dev) return; } - struct rtio_sqe *status_wr_sqe = rtio_sqe_acquire(data->rtio.ctx); - struct rtio_sqe *status_rd_sqe = rtio_sqe_acquire(data->rtio.ctx); - struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx); - struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx); - struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx); - - if (!write_sqe || !read_sqe || !complete_sqe) { + /* + * Acquire the chain's five SQEs atomically: the bus RTIO context is + * shared across reads, so a per-SQE acquire that falls back to + * rtio_sqe_drop_all could free an SQE still in flight from another + * chain. + */ + struct rtio_sqe *sqes[5]; + + if (rtio_sqe_acquire_array(data->rtio.ctx, ARRAY_SIZE(sqes), sqes) != 0) { LOG_ERR("Failed to acquire RTIO SQEs"); - rtio_sqe_drop_all(data->rtio.ctx); data->stream.iodev_sqe = NULL; rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); return; } + struct rtio_sqe *status_wr_sqe = sqes[0]; + struct rtio_sqe *status_rd_sqe = sqes[1]; + struct rtio_sqe *write_sqe = sqes[2]; + struct rtio_sqe *read_sqe = sqes[3]; + struct rtio_sqe *complete_sqe = sqes[4]; + uint8_t val; val = RM3100_REG_STATUS; From db102a77a76350bef0ed73bea6a49552fdd7d403 Mon Sep 17 00:00:00 2001 From: Kevin Degnan Date: Wed, 26 Aug 2026 00:22:00 +0000 Subject: [PATCH 057/600] net: ppp_l2: add proto_type to incoming packets This adds the relevent ETH_TYPE flag to incoming ppp packets. Fixes #116350 Signed-off-by: Kevin Degnan --- subsys/net/l2/ppp/ppp_l2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/subsys/net/l2/ppp/ppp_l2.c b/subsys/net/l2/ppp/ppp_l2.c index b3738d2f71a7..3710fae9a08b 100644 --- a/subsys/net/l2/ppp/ppp_l2.c +++ b/subsys/net/l2/ppp/ppp_l2.c @@ -107,6 +107,10 @@ static enum net_verdict process_ppp_msg(struct net_if *iface, if ((IS_ENABLED(CONFIG_NET_IPV4) && protocol == PPP_IP) || (IS_ENABLED(CONFIG_NET_IPV6) && protocol == PPP_IPV6)) { + + net_pkt_set_ll_proto_type(pkt, protocol == PPP_IP ? NET_ETH_PTYPE_IP : + NET_ETH_PTYPE_IPV6); + /* Remove the protocol field so that IP packet processing * continues properly in net_core.c:process_data() */ From b1cb0e7ad25bde2608f2a0e6b3eb0f41195b4a18 Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Sun, 23 Aug 2026 23:59:11 -0400 Subject: [PATCH 058/600] drivers: input: crsf: harden the ISR parser against hostile input crsf_process_bytes runs in UART ISR context on whatever chunk the async driver hands up, so it must survive torn and malformed frames from a noisy link without walking off a buffer. Sanity-check the incoming chunk on entry: reject a NULL pointer, an empty chunk, or a chunk larger than one RX DMA buffer before dereferencing it. Bound every store into rd_data against its size, resetting the state machine on any violation so no protocol-invariant slip can write out of bounds. Guard every length decrement: check payload_remaining before decrementing in the TYPE, IGNORE, and DATA states, treating zero-when-a-byte-is-still-expected as a framing error that reframes to SYNC. Route all resets through a single crsf_reset_parser helper to keep the hot ISR loop cheap. Signed-off-by: Benjamin Perseghetti --- drivers/input/input_crsf.c | 48 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/input/input_crsf.c b/drivers/input/input_crsf.c index c488fa1854b4..23ba95c87ae6 100644 --- a/drivers/input/input_crsf.c +++ b/drivers/input/input_crsf.c @@ -324,18 +324,35 @@ static void input_crsf_input_report_thread(const struct device *dev, void *dummy } } +static inline void crsf_reset_parser(struct input_crsf_data *data) +{ + data->rx_state = RX_STATE_SYNC; + data->xfer_bytes = 0; + data->payload_remaining = 0; +} + /* * Byte Processor: Implements State Machine - * Called by the Async Callback + * Called by the Async Callback in UART ISR context. */ static void crsf_process_bytes(const struct device *dev, uint8_t *bytes, size_t len) { struct input_crsf_data *const data = dev->data; + /* A chunk larger than one RX DMA buffer cannot come from this driver's RX path. */ + if (bytes == NULL || len == 0 || len > CRSF_RX_BUF_SIZE) { + LOG_DBG("Dropping invalid CRSF chunk (len %zu)", len); + return; + } + for (int offset = 0; offset < len; offset++) { switch (data->rx_state) { case RX_STATE_SYNC: /* logic: waiting for [SYNC, LEN, TYPE] sequence or just SYNC validation */ + if (data->xfer_bytes >= sizeof(data->rd_data)) { + crsf_reset_parser(data); + break; + } data->rd_data[data->xfer_bytes++] = bytes[offset]; if (data->rd_data[0] != CRSF_SYNC_BYTE) { @@ -358,7 +375,16 @@ static void crsf_process_bytes(const struct device *dev, uint8_t *bytes, size_t break; case RX_STATE_TYPE: + /* Len promised a type byte; if the counter is spent, reframe. */ + if (data->payload_remaining == 0) { + crsf_reset_parser(data); + break; + } if (is_crsf_whitelisted(bytes[offset])) { + if (data->xfer_bytes >= sizeof(data->rd_data)) { + crsf_reset_parser(data); + break; + } data->rx_state = RX_STATE_DATA; data->rd_data[data->xfer_bytes++] = bytes[offset]; data->payload_remaining--; @@ -370,17 +396,30 @@ static void crsf_process_bytes(const struct device *dev, uint8_t *bytes, size_t break; case RX_STATE_IGNORE: { + /* Nothing left to skip but we never reframed: framing slip. */ + if (data->payload_remaining == 0) { + crsf_reset_parser(data); + break; + } data->payload_remaining--; /* If we've skipped everything, reset to SYNC */ if (data->payload_remaining == 0) { - data->rx_state = RX_STATE_SYNC; - data->xfer_bytes = 0; + crsf_reset_parser(data); } break; } case RX_STATE_DATA: + /* Expected payload/CRC byte but the counter is already spent. */ + if (data->payload_remaining == 0) { + crsf_reset_parser(data); + break; + } + if (data->xfer_bytes >= sizeof(data->rd_data)) { + crsf_reset_parser(data); + break; + } data->rd_data[data->xfer_bytes++] = bytes[offset]; data->payload_remaining--; @@ -392,8 +431,7 @@ static void crsf_process_bytes(const struct device *dev, uint8_t *bytes, size_t k_msgq_put(&data->rx_queue, data->rd_data, K_NO_WAIT); /* Reset for next frame */ - data->rx_state = RX_STATE_SYNC; - data->xfer_bytes = 0; + crsf_reset_parser(data); } break; } From 26c6fb44d8e4ac8441002b7d653994ed32123f5f Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Sun, 23 Aug 2026 23:59:59 -0400 Subject: [PATCH 059/600] drivers: input: crsf: contain out-of-bounds async rx-ready windows A field fault traced to the CRSF RX_RDY path: the parser was handed a buffer window whose base pointer read a wild address, faulting in ISR context. The serial driver's async double-buffer accounting was audited and found self-consistent: the RX_RDY event reports a (buf, offset, len) window derived under lock from a single view, with offset and length bounded by the buffer length, so the wild base pointer cannot come from that accounting alone and the root mechanism could not be pinned in the serial layer. This guard is containment at the buffer owner, not a fix for a proven serial-driver defect. The CRSF driver supplies the two RX DMA buffers, so it can validate the event before trusting it: require the RX_RDY buffer to be one of those two buffers and the offset/len window to stay within CRSF_RX_BUF_SIZE before invalidating cache or parsing. Anything else is dropped so a stale or corrupt window can never reach crsf_process_bytes. Signed-off-by: Benjamin Perseghetti --- drivers/input/input_crsf.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/input/input_crsf.c b/drivers/input/input_crsf.c index 23ba95c87ae6..b46a737a0304 100644 --- a/drivers/input/input_crsf.c +++ b/drivers/input/input_crsf.c @@ -459,13 +459,28 @@ static void crsf_uart_callback(const struct device *uart_dev, struct uart_event LOG_ERR("CRSF TX Aborted"); break; - case UART_RX_RDY: + case UART_RX_RDY: { + uint8_t *rx_buf = evt->data.rx.buf; + size_t rx_off = evt->data.rx.offset; + size_t rx_len = evt->data.rx.len; + + /* + * The window must name one of our two RX buffers and stay inside + * it, or the async event is corrupt and gets dropped. + */ + if ((rx_buf != data->rx_buf_a && rx_buf != data->rx_buf_b) || rx_len == 0 || + rx_off > CRSF_RX_BUF_SIZE || rx_len > (size_t)CRSF_RX_BUF_SIZE - rx_off) { + LOG_DBG("Dropping out-of-range CRSF RX window"); + break; + } + #ifdef CRSF_INVALIDATE_CACHE - arch_dcache_invd_range(&evt->data.rx.buf[evt->data.rx.offset], evt->data.rx.len); + arch_dcache_invd_range(&rx_buf[rx_off], rx_len); #endif /* Process received data chunk */ - crsf_process_bytes(dev, &evt->data.rx.buf[evt->data.rx.offset], evt->data.rx.len); + crsf_process_bytes(dev, &rx_buf[rx_off], rx_len); break; + } case UART_RX_BUF_REQUEST: /* Provide the next buffer to keep reception continuous */ From 6a9d7efbeadf05f06b3f2155d9c4d999926c7455 Mon Sep 17 00:00:00 2001 From: Eryk Szpotanski Date: Wed, 26 Aug 2026 13:50:53 +0200 Subject: [PATCH 060/600] arch: riscv: irq: Add missing header Adds missing header that is required for ISR tracing. Signed-off-by: Eryk Szpotanski Signed-off-by: Maciej Torhan --- include/zephyr/arch/riscv/irq.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/zephyr/arch/riscv/irq.h b/include/zephyr/arch/riscv/irq.h index 0fefd2d063a1..b122ff08a8dc 100644 --- a/include/zephyr/arch/riscv/irq.h +++ b/include/zephyr/arch/riscv/irq.h @@ -18,6 +18,7 @@ extern "C" { #endif +#include #include #ifndef _ASMLANGUAGE From ceb28342befb91553340bec6079c1fc9370b8598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:06:28 +0000 Subject: [PATCH 061/600] ci: github: bump the actions-deps group across 1 directory with 5 updates Bumps the actions-deps group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) | `21` | `23` | | [github/codeql-action/init](https://github.com/github/codeql-action) | `4.37.6` | `4.37.7` | | [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.37.6` | `4.37.7` | | [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.37.6` | `4.37.7` | | [zgosalvez/github-actions-ensure-sha-pinned-actions](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions) | `5.0.6` | `5.0.7` | Updates `dawidd6/action-download-artifact` from 21 to 23 - [Release notes](https://github.com/dawidd6/action-download-artifact/releases) - [Commits](https://github.com/dawidd6/action-download-artifact/compare/b6e2e70617bc3265edd6dab6c906732b2f1ae151...57aa996fc1713cc1579039614f4645a7f4841fd4) Updates `github/codeql-action/init` from 4.37.6 to 4.37.7 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3...ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd) Updates `github/codeql-action/analyze` from 4.37.6 to 4.37.7 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3...ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd) Updates `github/codeql-action/upload-sarif` from 4.37.6 to 4.37.7 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3...ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd) Updates `zgosalvez/github-actions-ensure-sha-pinned-actions` from 5.0.6 to 5.0.7 - [Release notes](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/releases) - [Commits](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/compare/46cfe808a5f1588656ef299eedd0ce2fd7ec0dcc...c5fc58bd0be7a4b94b73ce40250322d5b838a108) --- updated-dependencies: - dependency-name: dawidd6/action-download-artifact dependency-version: '23' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions-deps - dependency-name: github/codeql-action/init dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-deps - dependency-name: github/codeql-action/analyze dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-deps - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-deps - dependency-name: zgosalvez/github-actions-ensure-sha-pinned-actions dependency-version: 5.0.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-deps ... Signed-off-by: dependabot[bot] --- .github/workflows/bsim-tests-publish.yaml | 2 +- .github/workflows/codeql.yml | 4 ++-- .github/workflows/doc-publish-pr.yml | 2 +- .github/workflows/doc-publish.yml | 2 +- .github/workflows/gcc-analyzer.yml | 2 +- .github/workflows/pinned-gh-actions.yml | 2 +- .github/workflows/sanitizers.yml | 2 +- .github/workflows/scorecards.yml | 2 +- .github/workflows/twister-publish.yaml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/bsim-tests-publish.yaml b/.github/workflows/bsim-tests-publish.yaml index 3ce3fa1acbbf..25a0b5c23584 100644 --- a/.github/workflows/bsim-tests-publish.yaml +++ b/.github/workflows/bsim-tests-publish.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: Download artifacts - uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 + uses: dawidd6/action-download-artifact@57aa996fc1713cc1579039614f4645a7f4841fd4 # v23 with: run_id: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 550bbf190d70..f78de6812e45 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -61,6 +61,6 @@ jobs: exit 0 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/doc-publish-pr.yml b/.github/workflows/doc-publish-pr.yml index 4a3633aaf9f1..e20b401cdd5a 100644 --- a/.github/workflows/doc-publish-pr.yml +++ b/.github/workflows/doc-publish-pr.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Download artifacts id: download-artifacts - uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 + uses: dawidd6/action-download-artifact@57aa996fc1713cc1579039614f4645a7f4841fd4 # v23 with: workflow: doc-build.yml run_id: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/doc-publish.yml b/.github/workflows/doc-publish.yml index 137a88b1b635..fc2a8928c773 100644 --- a/.github/workflows/doc-publish.yml +++ b/.github/workflows/doc-publish.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Download artifacts - uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 + uses: dawidd6/action-download-artifact@57aa996fc1713cc1579039614f4645a7f4841fd4 # v23 with: workflow: doc-build.yml run_id: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/gcc-analyzer.yml b/.github/workflows/gcc-analyzer.yml index fc5c0d4b3a98..d647d5aa6001 100644 --- a/.github/workflows/gcc-analyzer.yml +++ b/.github/workflows/gcc-analyzer.yml @@ -177,7 +177,7 @@ jobs: - name: Upload SARIF to code scanning if: steps.merge.outputs.found == 'true' - uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: sarif_file: results.sarif # Keeps these alerts in their own lane in the dashboard, so they diff --git a/.github/workflows/pinned-gh-actions.yml b/.github/workflows/pinned-gh-actions.yml index 3240038dd6eb..745c9f80a4f6 100644 --- a/.github/workflows/pinned-gh-actions.yml +++ b/.github/workflows/pinned-gh-actions.yml @@ -23,4 +23,4 @@ jobs: with: persist-credentials: false - name: Ensure SHA pinned actions - uses: zgosalvez/github-actions-ensure-sha-pinned-actions@46cfe808a5f1588656ef299eedd0ce2fd7ec0dcc # v5.0.6 + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@c5fc58bd0be7a4b94b73ce40250322d5b838a108 # v5.0.7 diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index f6a7d339351b..4b85bbc211ca 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -116,7 +116,7 @@ jobs: run: python3 scripts/ci/sarif_summary.py results-${{ matrix.sanitizer }}.sarif - name: Upload SARIF to code scanning - uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: sarif_file: results-${{ matrix.sanitizer }}.sarif # One category per sanitizer. They report disjoint rule sets, and a diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 797a3383e6d5..4c500e118fc4 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -57,6 +57,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: sarif_file: results.sarif diff --git a/.github/workflows/twister-publish.yaml b/.github/workflows/twister-publish.yaml index 9e6fb069e62d..a0a63085145a 100644 --- a/.github/workflows/twister-publish.yaml +++ b/.github/workflows/twister-publish.yaml @@ -40,7 +40,7 @@ jobs: - name: Download Artifacts id: download-artifacts - uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 + uses: dawidd6/action-download-artifact@57aa996fc1713cc1579039614f4645a7f4841fd4 # v23 with: path: artifacts workflow: twister.yml From 96c3397c7062744f72eae08cdc277b05eac7c6ad Mon Sep 17 00:00:00 2001 From: yi chen <94xhn1@gmail.com> Date: Thu, 9 Jul 2026 11:29:59 +0800 Subject: [PATCH 062/600] tests: stream_flash: use write_block_size instead of hardcoded buf_len test_stream_flash_buf_size_greater_than_page_size hardcoded a buf_len of 0x10 for the stream_flash_init() call that's meant to succeed, assuming that value is always valid regardless of the flash device's actual write_block_size. On a device whose write_block_size is larger than 16 bytes, stream_flash_init() rejects that buf_len as invalid, so the test fails with an unrelated error instead of testing what it's meant to test. Use flash_get_parameters(fdev)->write_block_size instead, the same pattern already used earlier in this file, so the buf_len is always valid for whatever device the test runs on. Fixes #110948 Signed-off-by: yi chen <94xhn1@gmail.com> --- tests/subsys/storage/stream/stream_flash/src/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/subsys/storage/stream/stream_flash/src/main.c b/tests/subsys/storage/stream/stream_flash/src/main.c index 61018074d169..803442e21773 100644 --- a/tests/subsys/storage/stream/stream_flash/src/main.c +++ b/tests/subsys/storage/stream/stream_flash/src/main.c @@ -319,9 +319,11 @@ ZTEST(lib_stream_flash, test_stream_flash_bytes_buffered) ZTEST(lib_stream_flash, test_stream_flash_buf_size_greater_than_page_size) { int rc; + const struct flash_parameters *fparam = flash_get_parameters(fdev); /* To illustrate that other params does not trigger error */ - rc = stream_flash_init(&ctx, fdev, generic_buf, 0x10, 0, FLASH_AVAILABLE, NULL); + rc = stream_flash_init(&ctx, fdev, generic_buf, fparam->write_block_size, 0, + FLASH_AVAILABLE, NULL); zassert_equal(rc, 0, "expected success"); /* Only change buf_len param */ From c9dc69bd18624ce12c0cd224a2b9749cef8e6431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:18 +0200 Subject: [PATCH 063/600] doc: licensing: add the exceptions section to REUSE.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zephyr is Apache-2.0 as a whole but reuses a few components under other licenses. Introduce the exceptions section in REUSE.toml and document the Zephyr-Description/-Origin/-Kconfig-Condition keys the licensing page is generated from. Per-component entries follow. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 0796e2952d3e..17a0387a20e3 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -28,3 +28,26 @@ SPDX-FileCopyrightText = "Copyright The Zephyr Project Contributors" path = "**/*.rst" SPDX-License-Identifier = "CC-BY-4.0" SPDX-FileCopyrightText = "Copyright The Zephyr Project Contributors" + +# ----------------------------------------------------------------------------- +# Licensing exceptions +# ----------------------------------------------------------------------------- +# +# Zephyr as a whole is Apache-2.0 licensed, but it imports or reuses a few +# components that use other licenses. Each such component is described below as +# a single REUSE annotation and is rendered on the licensing documentation page +# (see doc/LICENSING.rst) by the "zephyr.licensing" Sphinx extension. +# +# The license metadata is standard REUSE/SPDX and the standard SPDX-FileComment +# holds the free-text rationale ("Impact"). SPDX has no non-deprecated file-level +# field for an upstream URL (the ArtifactOf* file tags were deprecated in favour +# of relationships, which REUSE.toml cannot express, and Package* fields are not +# valid on files), so three Zephyr-specific keys are used (the `reuse` tool +# ignores keys it does not know about): +# +# Zephyr-Description Short human description of the component, used as +# its title on the page. Its presence promotes a +# plain REUSE annotation to a documented exception. +# Zephyr-Origin URL of the upstream project the files come from. +# Zephyr-Kconfig-Condition Kconfig option(s) gating whether the files end up in +# a build, rendered as :kconfig:option: references. From ecbec6263a90d381a812190ed6ae95bafc1103c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:18 +0200 Subject: [PATCH 064/600] doc: licensing: document the Bootstrap dashboard files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bootstrap ships the dashboard's CSS and JavaScript under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/MIT.txt | 21 +++++++++++++++++++++ REUSE.toml | 10 ++++++++++ 2 files changed, 31 insertions(+) create mode 100644 LICENSES/MIT.txt diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 000000000000..8aa26455d23a --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/REUSE.toml b/REUSE.toml index 17a0387a20e3..4cd6b186e62b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -51,3 +51,13 @@ SPDX-FileCopyrightText = "Copyright The Zephyr Project Contributors" # Zephyr-Origin URL of the upstream project the files come from. # Zephyr-Kconfig-Condition Kconfig option(s) gating whether the files end up in # a build, rendered as :kconfig:option: references. + +[[annotations]] +path = [ + "scripts/dashboard/static/css/bootstrap-chop.css", + "scripts/dashboard/static/js/bootstrap-chop.js", +] +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Used by the dashboard tool only and never linked into a Zephyr firmware image." +Zephyr-Description = "Bootstrap JavaScript and CSS Files" +Zephyr-Origin = "https://getbootstrap.com/" From 1ec3a57d6fbc73fc72892fe652b6cb39240c1f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:18 +0200 Subject: [PATCH 065/600] doc: licensing: document the Bosch BMI08x sensor config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BMI08x sensor configuration blob is imported from the Bosch Sensortec BMI08x API under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/BSD-3-Clause.txt | 28 ++++++++++++++++++++++++++++ REUSE.toml | 8 ++++++++ 2 files changed, 36 insertions(+) create mode 100644 LICENSES/BSD-3-Clause.txt diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt new file mode 100644 index 000000000000..ddd44f66e975 --- /dev/null +++ b/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) [year], [fullname] + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/REUSE.toml b/REUSE.toml index 4cd6b186e62b..a960a71d159f 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -61,3 +61,11 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Used by the dashboard tool only and never linked into a Zephyr firmware image." Zephyr-Description = "Bootstrap JavaScript and CSS Files" Zephyr-Origin = "https://getbootstrap.com/" + +[[annotations]] +path = "drivers/sensor/bosch/bmi08x/bmi08x_config_file.h" +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Sensor configuration data compiled into the firmware only when the BMI08x driver is enabled." +Zephyr-Description = "Bosch BMI08x Sensor Configuration File" +Zephyr-Origin = "https://github.com/BoschSensortec/BMI08x-Sensor-API" +Zephyr-Kconfig-Condition = "CONFIG_BMI08X" From 49db62e5e0a068949a43588bc30aca456603f992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:19 +0200 Subject: [PATCH 066/600] doc: licensing: document the Coccinelle scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Coccinelle semantic-patch scripts are imported under GPL-2.0. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/GPL-2.0.txt | 339 +++++++++++++++++++++++++++++++++++++++++++ REUSE.toml | 16 ++ 2 files changed, 355 insertions(+) create mode 100644 LICENSES/GPL-2.0.txt diff --git a/LICENSES/GPL-2.0.txt b/LICENSES/GPL-2.0.txt new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/LICENSES/GPL-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/REUSE.toml b/REUSE.toml index a960a71d159f..6d29302b126f 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -69,3 +69,19 @@ SPDX-FileComment = "Sensor configuration data compiled into the firmware only wh Zephyr-Description = "Bosch BMI08x Sensor Configuration File" Zephyr-Origin = "https://github.com/BoschSensortec/BMI08x-Sensor-API" Zephyr-Kconfig-Condition = "CONFIG_BMI08X" + +[[annotations]] +path = [ + "scripts/coccicheck", + "scripts/coccinelle/array_size.cocci", + "scripts/coccinelle/deref_null.cocci", + "scripts/coccinelle/mini_lock.cocci", + "scripts/coccinelle/noderef.cocci", + "scripts/coccinelle/returnvar.cocci", + "scripts/coccinelle/semicolon.cocci", + "scripts/coccinelle/unsigned_lesser_than_zero.cocci", +] +SPDX-License-Identifier = "GPL-2.0" +SPDX-FileComment = "Used by Coccinelle, a tool for transforming C code, and never linked into the firmware." +Zephyr-Description = "Coccinelle Scripts" +Zephyr-Origin = "https://coccinelle.gitlabpages.inria.fr/website/" From b745bdea2a88d3736a207b7ff535c856cbc0bd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:19 +0200 Subject: [PATCH 067/600] doc: licensing: document the CI scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkpatch, checkstack and the spelling list come from the Linux kernel under GPL-2.0. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 6d29302b126f..a710c6fe07c5 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -85,3 +85,14 @@ SPDX-License-Identifier = "GPL-2.0" SPDX-FileComment = "Used by Coccinelle, a tool for transforming C code, and never linked into the firmware." Zephyr-Description = "Coccinelle Scripts" Zephyr-Origin = "https://coccinelle.gitlabpages.inria.fr/website/" + +[[annotations]] +path = [ + "scripts/checkpatch.pl", + "scripts/checkstack.pl", + "scripts/spelling.txt", +] +SPDX-License-Identifier = "GPL-2.0" +SPDX-FileComment = "Used in Continuous Integration (CI) and never linked into the firmware." +Zephyr-Description = "Continuous Integration Scripts" +Zephyr-Origin = "https://www.kernel.org/" From c86d023c4eee73a50fd65699e63eb7ee7786ec27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:19 +0200 Subject: [PATCH 068/600] doc: licensing: document the Doxygen Awesome theme files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Doxygen Awesome CSS/JS theme is imported under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index a710c6fe07c5..1fad19c713d4 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -96,3 +96,15 @@ SPDX-License-Identifier = "GPL-2.0" SPDX-FileComment = "Used in Continuous Integration (CI) and never linked into the firmware." Zephyr-Description = "Continuous Integration Scripts" Zephyr-Origin = "https://www.kernel.org/" + +[[annotations]] +path = [ + "doc/_doxygen/doxygen-awesome-darkmode-toggle.js", + "doc/_doxygen/doxygen-awesome-sidebar-only-darkmode-toggle.css", + "doc/_doxygen/doxygen-awesome-sidebar-only.css", + "doc/_doxygen/doxygen-awesome.css", +] +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Style the Doxygen API documentation and are never linked into a firmware." +Zephyr-Description = "Doxygen Awesome CSS Theme Files" +Zephyr-Origin = "https://github.com/jothepro/doxygen-awesome-css" From 9a3a2bf9ad97347de1fdfc5f520d9c727c71cd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:20 +0200 Subject: [PATCH 069/600] doc: licensing: document the ENE KB1200_EVB OpenOCD config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kb1200_evb OpenOCD configuration is imported under GPL-2.0-or-later. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/GPL-2.0-or-later.txt | 339 ++++++++++++++++++++++++++++++++++ REUSE.toml | 7 + 2 files changed, 346 insertions(+) create mode 100644 LICENSES/GPL-2.0-or-later.txt diff --git a/LICENSES/GPL-2.0-or-later.txt b/LICENSES/GPL-2.0-or-later.txt new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/LICENSES/GPL-2.0-or-later.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/REUSE.toml b/REUSE.toml index 1fad19c713d4..e792928304a3 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -108,3 +108,10 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Style the Doxygen API documentation and are never linked into a firmware." Zephyr-Description = "Doxygen Awesome CSS Theme Files" Zephyr-Origin = "https://github.com/jothepro/doxygen-awesome-css" + +[[annotations]] +path = "boards/ene/kb1200_evb/support/openocd.cfg" +SPDX-License-Identifier = "GPL-2.0-or-later" +SPDX-FileComment = "Used by OpenOCD when programming and debugging the kb1200_evb board. Never linked into the firmware." +Zephyr-Description = "ENE KB1200_EVB Board OpenOCD Configuration" +Zephyr-Origin = "https://openocd.org/" From 43a04f75e6b1892a83bfe6cf38463fe3358bea9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:20 +0200 Subject: [PATCH 070/600] doc: licensing: document the FUSE interface header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FUSE ABI header is imported under its dual license and used under the BSD-2-Clause option. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/BSD-2-Clause.txt | 24 ++++++++++++++++++++++++ LICENSES/Linux-syscall-note.txt | 12 ++++++++++++ REUSE.toml | 7 +++++++ 3 files changed, 43 insertions(+) create mode 100644 LICENSES/BSD-2-Clause.txt create mode 100644 LICENSES/Linux-syscall-note.txt diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt new file mode 100644 index 000000000000..c2f3d37bef47 --- /dev/null +++ b/LICENSES/BSD-2-Clause.txt @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) [year], [fullname] + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/Linux-syscall-note.txt b/LICENSES/Linux-syscall-note.txt new file mode 100644 index 000000000000..fcd056364e09 --- /dev/null +++ b/LICENSES/Linux-syscall-note.txt @@ -0,0 +1,12 @@ + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + + Linus Torvalds diff --git a/REUSE.toml b/REUSE.toml index e792928304a3..f7696d992cb4 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -115,3 +115,10 @@ SPDX-License-Identifier = "GPL-2.0-or-later" SPDX-FileComment = "Used by OpenOCD when programming and debugging the kb1200_evb board. Never linked into the firmware." Zephyr-Description = "ENE KB1200_EVB Board OpenOCD Configuration" Zephyr-Origin = "https://openocd.org/" + +[[annotations]] +path = "subsys/fs/fuse_client/fuse_abi.h" +SPDX-License-Identifier = "((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause)" +SPDX-FileComment = "Used under the BSD-2-Clause option of its dual license. Only built when the gating option is enabled." +Zephyr-Description = "FUSE Interface Definition Header File" +Zephyr-Kconfig-Condition = "CONFIG_FUSE_CLIENT" From 9b82b5969ff08100a695613b11d3b21e5fb97c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:21 +0200 Subject: [PATCH 071/600] doc: licensing: document the GCOV coverage header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GCOV coverage header comes from GCC under GPL-3.0-or-later with the GCC Runtime Library Exception. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/GCC-exception-3.1.txt | 69 ++++ LICENSES/GPL-3.0-or-later.txt | 674 +++++++++++++++++++++++++++++++++ REUSE.toml | 8 + 3 files changed, 751 insertions(+) create mode 100644 LICENSES/GCC-exception-3.1.txt create mode 100644 LICENSES/GPL-3.0-or-later.txt diff --git a/LICENSES/GCC-exception-3.1.txt b/LICENSES/GCC-exception-3.1.txt new file mode 100644 index 000000000000..f45e79128320 --- /dev/null +++ b/LICENSES/GCC-exception-3.1.txt @@ -0,0 +1,69 @@ +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +General information: http://www.gnu.org/licenses/gcc-exception.html Copyright +(C) 2009 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. This GCC Runtime Library Exception +("Exception") is an additional permission under section 7 of the GNU General +Public License, version 3 ("GPLv3"). It applies to a given file (the "Runtime +Library") that bears a notice placed by the copyright holder of the file +stating that the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of certain +GCC header files and runtime libraries with the compiled program. The purpose +of this Exception is to allow compilation of non-GPL (including proprietary) +programs to use, in this way, the header files and runtime libraries covered +by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime Library for +execution after a Compilation Process, or makes use of an interface provided +by the Runtime Library, but is not otherwise based on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of the +GNU General Public License (GPL) with the option of using any subsequent +versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with the +license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for input to +an assembler, loader, linker and/or execution phase. Notwithstanding that, +Target Code does not include data in any format that is used as a compiler +intermediate representation, or used for producing a compiler intermediate +representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, use +of source code generators and preprocessors need not be considered part of +the Compilation Process, since the Compilation Process can be understood as +starting with the output of the generators or preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or with +other GPL-compatible software, or if it is done without using any work based +on GCC. For example, using non-GPL-compatible Software to optimize any GCC +intermediate representations would not qualify as an Eligible Compilation +Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by combining +the Runtime Library with Independent Modules, even if such propagation +would otherwise violate the terms of GPLv3, provided that all Target Code +was generated by Eligible Compilation Processes. You may then convey such +a combination under terms of your choice, consistent with the licensing of +the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general presumption +that third-party software is unaffected by the copyleft requirements of the +license of GCC. diff --git a/LICENSES/GPL-3.0-or-later.txt b/LICENSES/GPL-3.0-or-later.txt new file mode 100644 index 000000000000..f288702d2fa1 --- /dev/null +++ b/LICENSES/GPL-3.0-or-later.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/REUSE.toml b/REUSE.toml index f7696d992cb4..8504bfbb1012 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -122,3 +122,11 @@ SPDX-License-Identifier = "((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause)" SPDX-FileComment = "Used under the BSD-2-Clause option of its dual license. Only built when the gating option is enabled." Zephyr-Description = "FUSE Interface Definition Header File" Zephyr-Kconfig-Condition = "CONFIG_FUSE_CLIENT" + +[[annotations]] +path = "subsys/testsuite/coverage/coverage.h" +SPDX-License-Identifier = "GPL-3.0-or-later WITH GCC-exception-3.1" +SPDX-FileComment = "Linked into the firmware only when GCOV coverage is enabled; covered by the GCC Runtime Library Exception." +Zephyr-Description = "GCOV Coverage Header File" +Zephyr-Origin = "https://gcc.gnu.org/" +Zephyr-Kconfig-Condition = "CONFIG_COVERAGE_GCOV" From 00354d8a2b66c9cf48c79c634ab46ba7d200204f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:21 +0200 Subject: [PATCH 072/600] doc: licensing: document the getopt implementation files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The getopt()/getopt_long() implementation is imported from BSD under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 8504bfbb1012..438c7c9cb183 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -130,3 +130,15 @@ SPDX-FileComment = "Linked into the firmware only when GCOV coverage is enabled; Zephyr-Description = "GCOV Coverage Header File" Zephyr-Origin = "https://gcc.gnu.org/" Zephyr-Kconfig-Condition = "CONFIG_COVERAGE_GCOV" + +[[annotations]] +path = [ + "lib/utils/getopt/README", + "lib/utils/getopt/getopt.c", + "lib/utils/getopt/getopt_long.c", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Compiled into the firmware only when getopt() support is enabled." +Zephyr-Description = "getopt Command-line Parsing Files" +Zephyr-Origin = "https://www.netbsd.org/" +Zephyr-Kconfig-Condition = "CONFIG_GETOPT" From cb34b1f345d51a26d3da7a659e9ce0d3c27dfa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:21 +0200 Subject: [PATCH 073/600] doc: licensing: document the Godot documentation theme files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation theme CSS/JS is imported from the Godot docs under CC- BY-3.0. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/CC-BY-3.0.txt | 319 +++++++++++++++++++++++++++++++++++++++++ REUSE.toml | 12 ++ 2 files changed, 331 insertions(+) create mode 100644 LICENSES/CC-BY-3.0.txt diff --git a/LICENSES/CC-BY-3.0.txt b/LICENSES/CC-BY-3.0.txt new file mode 100644 index 000000000000..1a16e05564d2 --- /dev/null +++ b/LICENSES/CC-BY-3.0.txt @@ -0,0 +1,319 @@ +Creative Commons Legal Code + +Attribution 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR + DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(f) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + e. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + f. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + g. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + h. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + i. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(b), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(b), as requested. + b. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and (iv) , consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4 (b) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION +OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at https://creativecommons.org/. diff --git a/REUSE.toml b/REUSE.toml index 438c7c9cb183..45d0bacc3747 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -142,3 +142,15 @@ SPDX-FileComment = "Compiled into the firmware only when getopt() support is ena Zephyr-Description = "getopt Command-line Parsing Files" Zephyr-Origin = "https://www.netbsd.org/" Zephyr-Kconfig-Condition = "CONFIG_GETOPT" + +[[annotations]] +path = [ + "doc/_static/css/custom.css", + "doc/_static/css/dark.css", + "doc/_static/css/light.css", + "doc/_static/js/custom.js", +] +SPDX-License-Identifier = "CC-BY-3.0" +SPDX-FileComment = "Customize the Sphinx theme used to render the documentation and are never linked into a firmware." +Zephyr-Description = "Godot Documentation Theme Files" +Zephyr-Origin = "https://github.com/godotengine/godot-docs" From df8689b19dbb3f5cf7e7b0d9848006f274f69ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:21 +0200 Subject: [PATCH 074/600] doc: licensing: document the HTTP parser files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HTTP parser is imported from the Node.js http-parser project under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 45d0bacc3747..805b2d046318 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -154,3 +154,17 @@ SPDX-License-Identifier = "CC-BY-3.0" SPDX-FileComment = "Customize the Sphinx theme used to render the documentation and are never linked into a firmware." Zephyr-Description = "Godot Documentation Theme Files" Zephyr-Origin = "https://github.com/godotengine/godot-docs" + +[[annotations]] +path = [ + "include/zephyr/net/http/parser.h", + "include/zephyr/net/http/parser_state.h", + "include/zephyr/net/http/parser_url.h", + "subsys/net/lib/http/http_parser.c", + "subsys/net/lib/http/http_parser_url.c", +] +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Compiled into the firmware only when the HTTP parser is enabled." +Zephyr-Description = "HTTP Parser Files" +Zephyr-Origin = "https://github.com/nodejs/http-parser" +Zephyr-Kconfig-Condition = "CONFIG_HTTP_PARSER" From 3cc93aa6c87ffc272c2b41e34fc36cc648f2a5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:22 +0200 Subject: [PATCH 075/600] doc: licensing: document the Kconfiglib library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kconfiglib and its menuconfig/guiconfig front-ends are imported under ISC. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/ISC.txt | 15 +++++++++++++++ REUSE.toml | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 LICENSES/ISC.txt diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt new file mode 100644 index 000000000000..b42456020f29 --- /dev/null +++ b/LICENSES/ISC.txt @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) [year] [fullname] + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/REUSE.toml b/REUSE.toml index 805b2d046318..67084f6f71fd 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -168,3 +168,15 @@ SPDX-FileComment = "Compiled into the firmware only when the HTTP parser is enab Zephyr-Description = "HTTP Parser Files" Zephyr-Origin = "https://github.com/nodejs/http-parser" Zephyr-Kconfig-Condition = "CONFIG_HTTP_PARSER" + +[[annotations]] +path = [ + "scripts/kconfig/guiconfig.py", + "scripts/kconfig/kconfig.py", + "scripts/kconfig/kconfiglib.py", + "scripts/kconfig/menuconfig.py", +] +SPDX-License-Identifier = "ISC" +SPDX-FileComment = "Used to process Kconfig files at build time and never linked into the firmware." +Zephyr-Description = "Kconfiglib Configuration Library" +Zephyr-Origin = "https://github.com/ulfalizer/Kconfiglib" From eaab11105aad74c6fa98bd5566a5faa09ec9bb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:22 +0200 Subject: [PATCH 076/600] doc: licensing: document the littlefs configuration files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The littlefs glue files are derived from littlefs and carry its BSD-3-Clause license. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 67084f6f71fd..a5036d6b02cb 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -180,3 +180,14 @@ SPDX-License-Identifier = "ISC" SPDX-FileComment = "Used to process Kconfig files at build time and never linked into the firmware." Zephyr-Description = "Kconfiglib Configuration Library" Zephyr-Origin = "https://github.com/ulfalizer/Kconfiglib" + +[[annotations]] +path = [ + "modules/littlefs/zephyr_lfs_config.h", + "modules/littlefs/zephyr_lfs_crc.c", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Compiled into the firmware only when the littlefs file system is enabled." +Zephyr-Description = "littlefs Configuration Files" +Zephyr-Origin = "https://github.com/littlefs-project/littlefs" +Zephyr-Kconfig-Condition = "CONFIG_FILE_SYSTEM_LITTLEFS" From 393faec5eb46c102c17223b46fcdac5f8c067ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:22 +0200 Subject: [PATCH 077/600] doc: licensing: document the minimal libc atoi file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The minimal C library's atoi() is imported from musl under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index a5036d6b02cb..7c4444e4c81b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -191,3 +191,11 @@ SPDX-FileComment = "Compiled into the firmware only when the littlefs file syste Zephyr-Description = "littlefs Configuration Files" Zephyr-Origin = "https://github.com/littlefs-project/littlefs" Zephyr-Kconfig-Condition = "CONFIG_FILE_SYSTEM_LITTLEFS" + +[[annotations]] +path = "lib/libc/minimal/source/stdlib/atoi.c" +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Compiled into the firmware only when the minimal C library is selected." +Zephyr-Description = "Minimal libc atoi File" +Zephyr-Origin = "https://musl.libc.org/" +Zephyr-Kconfig-Condition = "CONFIG_MINIMAL_LIBC" From bbc32918fc17f8961b2fae28597ad1972551c3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:22 +0200 Subject: [PATCH 078/600] doc: licensing: document the minimal libc BSD files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several minimal C library string and conversion functions are imported from BSD under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 7c4444e4c81b..34f09486b269 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -199,3 +199,16 @@ SPDX-FileComment = "Compiled into the firmware only when the minimal C library i Zephyr-Description = "Minimal libc atoi File" Zephyr-Origin = "https://musl.libc.org/" Zephyr-Kconfig-Condition = "CONFIG_MINIMAL_LIBC" + +[[annotations]] +path = [ + "lib/libc/minimal/source/stdlib/strtol.c", + "lib/libc/minimal/source/stdlib/strtoll.c", + "lib/libc/minimal/source/stdlib/strtoul.c", + "lib/libc/minimal/source/stdlib/strtoull.c", + "lib/libc/minimal/source/string/strstr.c", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Compiled into the firmware only when the minimal C library is selected." +Zephyr-Description = "Minimal libc BSD String and Conversion Files" +Zephyr-Kconfig-Condition = "CONFIG_MINIMAL_LIBC" From ad02dbc5b9090ca8d49b33d0684805b7b6a16daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:23 +0200 Subject: [PATCH 079/600] doc: licensing: document the noUiSlider library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The noUiSlider library powers the board catalog range sliders under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 34f09486b269..fe9d2a23d9e8 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -212,3 +212,13 @@ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Compiled into the firmware only when the minimal C library is selected." Zephyr-Description = "Minimal libc BSD String and Conversion Files" Zephyr-Kconfig-Condition = "CONFIG_MINIMAL_LIBC" + +[[annotations]] +path = [ + "doc/_extensions/zephyr/domain/static/css/nouislider.min.css", + "doc/_extensions/zephyr/domain/static/js/nouislider.min.js", +] +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Provides the flash/RAM range sliders in the board catalog and is never linked into a firmware." +Zephyr-Description = "noUiSlider Library" +Zephyr-Origin = "https://refreshless.com/nouislider/" From 9a0b5fe09e07333bab65dff2e7c0c406989401f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:23 +0200 Subject: [PATCH 080/600] doc: licensing: document the OP-TEE interface headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OP-TEE message interface headers are imported from OP-TEE under BSD-2-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index fe9d2a23d9e8..3cbc0017df04 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -222,3 +222,15 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Provides the flash/RAM range sliders in the board catalog and is never linked into a firmware." Zephyr-Description = "noUiSlider Library" Zephyr-Origin = "https://refreshless.com/nouislider/" + +[[annotations]] +path = [ + "drivers/tee/optee/optee_msg.h", + "drivers/tee/optee/optee_rpc_cmd.h", + "drivers/tee/optee/optee_smc.h", +] +SPDX-License-Identifier = "BSD-2-Clause" +SPDX-FileComment = "Compiled into the firmware only when the OP-TEE driver is enabled." +Zephyr-Description = "OP-TEE Message Interface Headers" +Zephyr-Origin = "https://www.op-tee.org/" +Zephyr-Kconfig-Condition = "CONFIG_OPTEE" From 1404125e269a7cb31517f9799bce3027fa6c2589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:23 +0200 Subject: [PATCH 081/600] doc: licensing: document the OpenThread Spinel files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Spinel HDLC RCP host interface files are imported from OpenThread under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 3cbc0017df04..9a5afc2e34cc 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -234,3 +234,15 @@ SPDX-FileComment = "Compiled into the firmware only when the OP-TEE driver is en Zephyr-Description = "OP-TEE Message Interface Headers" Zephyr-Origin = "https://www.op-tee.org/" Zephyr-Kconfig-Condition = "CONFIG_OPTEE" + +[[annotations]] +path = [ + "modules/openthread/platform/hdlc_interface.cpp", + "modules/openthread/platform/hdlc_interface.hpp", + "modules/openthread/platform/radio_spinel.cpp", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Linked into the firmware only when the Spinel HDLC RCP host interface is enabled." +Zephyr-Description = "OpenThread Spinel HDLC RCP Host Interface Files" +Zephyr-Origin = "https://openthread.io/" +Zephyr-Kconfig-Condition = "CONFIG_HDLC_RCP_IF" From 21f4633e06449c4dacb7dba81164a58001ab021d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:23 +0200 Subject: [PATCH 082/600] doc: licensing: document the Popper.js library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Popper.js positions the documentation's Doxygen tooltips under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 9a5afc2e34cc..cd3de103c30b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -246,3 +246,10 @@ SPDX-FileComment = "Linked into the firmware only when the Spinel HDLC RCP host Zephyr-Description = "OpenThread Spinel HDLC RCP Host Interface Files" Zephyr-Origin = "https://openthread.io/" Zephyr-Kconfig-Condition = "CONFIG_HDLC_RCP_IF" + +[[annotations]] +path = "doc/_extensions/zephyr/doxytooltip/static/tippy/popper.min.js" +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Used by Tippy.js to position the documentation's Doxygen tooltips and is never linked into a firmware." +Zephyr-Description = "Popper.js Library" +Zephyr-Origin = "https://popper.js.org/" From 15ba0e2cb71aab5ba01192f822759ab61b80b8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:23 +0200 Subject: [PATCH 083/600] doc: licensing: document the POSIX fnmatch files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The POSIX fnmatch() implementation is imported from BSD under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index cd3de103c30b..edb4fa4e3725 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -253,3 +253,13 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Used by Tippy.js to position the documentation's Doxygen tooltips and is never linked into a firmware." Zephyr-Description = "Popper.js Library" Zephyr-Origin = "https://popper.js.org/" + +[[annotations]] +path = [ + "include/zephyr/posix/fnmatch.h", + "subsys/portability/posix/c_lib_ext/fnmatch.c", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Compiled into the firmware only when the POSIX C library extensions are enabled." +Zephyr-Description = "POSIX fnmatch Files" +Zephyr-Kconfig-Condition = "CONFIG_POSIX_C_LIB_EXT" From 2f0625ba1fe3f2fbe2008ad86c09a4597e0ea604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:24 +0200 Subject: [PATCH 084/600] doc: licensing: document the POSIX sys/stat.h header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The POSIX sys/stat.h header is imported from BSD under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index edb4fa4e3725..88a5f53e417e 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -263,3 +263,9 @@ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Compiled into the firmware only when the POSIX C library extensions are enabled." Zephyr-Description = "POSIX fnmatch Files" Zephyr-Kconfig-Condition = "CONFIG_POSIX_C_LIB_EXT" + +[[annotations]] +path = "include/zephyr/posix/sys/stat.h" +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Provides the POSIX file-status definitions; compiled into the firmware wherever it is included." +Zephyr-Description = "POSIX sys/stat.h Header" From ac531023e94a09d78a044a1a252224d7bf716106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:24 +0200 Subject: [PATCH 085/600] doc: licensing: document the python-devicetree library files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The python-devicetree library sources and their test fixtures are dual- licensed BSD-3-Clause for reuse outside Zephyr. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 88a5f53e417e..e821788541a9 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -269,3 +269,19 @@ path = "include/zephyr/posix/sys/stat.h" SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Provides the POSIX file-status definitions; compiled into the firmware wherever it is included." Zephyr-Description = "POSIX sys/stat.h Header" + +[[annotations]] +path = [ + "scripts/dts/gen_defines.py", + "scripts/dts/python-devicetree/src/devicetree/_private.py", + "scripts/dts/python-devicetree/src/devicetree/dtlib.py", + "scripts/dts/python-devicetree/src/devicetree/edtlib.py", + "scripts/dts/python-devicetree/tests/test-multidir.dts", + "scripts/dts/python-devicetree/tests/test.dts", + "scripts/dts/python-devicetree/tests/test_dtlib.py", + "scripts/dts/python-devicetree/tests/test_edtlib.py", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Parse devicetree sources at build time and are never linked into the firmware." +Zephyr-Description = "Python Devicetree Library Files" +Zephyr-Origin = "https://github.com/zephyrproject-rtos/python-devicetree" From e2a891b72e775197f8f09f3d5b595e957a818e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:24 +0200 Subject: [PATCH 086/600] doc: licensing: document the python-devicetree test files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The python-devicetree test fixtures are imported under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index e821788541a9..fec000858562 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -285,3 +285,10 @@ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Parse devicetree sources at build time and are never linked into the firmware." Zephyr-Description = "Python Devicetree Library Files" Zephyr-Origin = "https://github.com/zephyrproject-rtos/python-devicetree" + +[[annotations]] +path = "scripts/dts/python-devicetree/tests/**/*.yaml" +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Used only when testing the python-devicetree library and never linked into the firmware." +Zephyr-Description = "Python Devicetree Library Test Files" +Zephyr-Origin = "https://github.com/zephyrproject-rtos/python-devicetree" From 4b0d24b88ecfb567b0b8822412cc4c582b6ff87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:24 +0200 Subject: [PATCH 087/600] doc: licensing: document the RISC-V CSR header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RISC-V CSR access header is available under SHL-0.51 (and Apache-2.0). Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/SHL-0.51.txt | 194 ++++++++++++++++++++++++++++++++++++++++++ REUSE.toml | 7 ++ 2 files changed, 201 insertions(+) create mode 100644 LICENSES/SHL-0.51.txt diff --git a/LICENSES/SHL-0.51.txt b/LICENSES/SHL-0.51.txt new file mode 100644 index 000000000000..62ea22cb3cb8 --- /dev/null +++ b/LICENSES/SHL-0.51.txt @@ -0,0 +1,194 @@ +SOLDERPAD HARDWARE LICENSE version 0.51 + +This license is based closely on the Apache License Version 2.0, but is not +approved or endorsed by the Apache Foundation. A copy of the non-modified +Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0. + +As this license is not currently OSI or FSF approved, the Licensor permits +any Work licensed under this License, at the option of the Licensee, to be +treated as licensed under the Apache License Version 2.0 (which is so approved). + +This License is licensed under the terms of this License and in particular +clause 7 below (Disclaimer of Warranties) applies in relation to its use. + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the Rights owner or entity authorized by the Rights +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control with that +entity. For the purposes of this definition, "control" means (i) the power, +direct or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Rights" means copyright and any similar right including design right +(whether registered or unregistered), semiconductor topography (mask) rights +and database rights (but excluding Patents and Trademarks). + +"Source" form shall mean the preferred form for making modifications, +including but not limited to source code, net lists, board layouts, CAD files, +documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object +code, generated documentation, the instantiation of a hardware design and +conversions to other media types, including intermediate forms such as +bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask works). + +"Work" shall mean the work of authorship, whether in Source form or other +Object form, made available under the License, as indicated by a Rights +notice that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, as +a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, or +merely link (or bind by name) or physically connect to or interoperate with +the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any design or work of authorship, including the +original version of the Work and any modifications or additions to that Work +or Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the Rights owner or by an individual or Legal +Entity authorized to submit on behalf of the Rights owner. For the purposes +of this definition, "submitted" means any form of electronic, verbal, +or written communication sent to the Licensor or its representatives, +including but not limited to communication on electronic mailing lists, +source code control systems, and issue tracking systems that are managed by, +or on behalf of, the Licensor for the purpose of discussing and improving the +Work, but excluding communication that is conspicuously marked or otherwise +designated in writing by the Rights owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on +behalf of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable license under the Rights to reproduce, +prepare Derivative Works of, publicly display, publicly perform, sublicense, +and distribute the Work and such Derivative Works in Source or Object form +and do anything in relation to the Work as if the Rights did not exist. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, and +otherwise transfer the Work, where such license applies only to those patent +claims licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the +Work to which such Contribution(s) was submitted. If You institute patent +litigation against any entity (including a cross-claim or counterclaim in +a lawsuit) alleging that the Work or a Contribution incorporated within the +Work constitutes direct or contributory patent infringement, then any patent +licenses granted to You under this License for that Work shall terminate as +of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work +or Derivative Works thereof in any medium, with or without modifications, +and in Source or Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works a + copy of this License; and + + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + + 3. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices + from the Source form of the Work, excluding those notices that do not + pertain to any part of the Derivative Works; and + + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable copy + of the attribution notices contained within such NOTICE file, excluding + those notices that do not pertain to any part of the Derivative Works, in + at least one of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or documentation, + if provided along with the Derivative Works; or, within a display generated + by the Derivative Works, if and wherever such third-party notices normally + appear. The contents of the NOTICE file are for informational purposes + only and do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside or as an + addendum to the NOTICE text from the Work, provided that such additional + attribution notices cannot be construed as modifying the License. You may + add Your own copyright statement to Your modifications and may provide + additional or different license terms and conditions for use, reproduction, + or distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of the Work + otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work by You +to the Licensor shall be under the terms and conditions of this License, +without any additional terms or conditions. Notwithstanding the above, +nothing herein shall supersede or modify the terms of any separate license +agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, except +as required for reasonable and customary use in describing the origin of +the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +ANY KIND, either express or implied, including, without limitation, any +warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or +FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining +the appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of +any character arising as a result of this License or out of the use or +inability to use the Work (including but not limited to damages for loss +of goodwill, work stoppage, computer failure or malfunction, or any and +all other commercial damages or losses), even if such Contributor has been +advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, and +charge a fee for, acceptance of support, warranty, indemnity, or other +liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on +Your sole responsibility, not on behalf of any other Contributor, and only +if You agree to indemnify, defend, and hold each Contributor harmless for +any liability incurred by, or claims asserted against, such Contributor by +reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply this license to your work + +To apply this license to your work, attach the following boilerplate notice, +with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in the +appropriate comment syntax for the file format. We also recommend that a file +or class name and description of purpose be included on the same "printed page" +as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] Copyright and related rights are +licensed under the Solderpad Hardware License, Version 0.51 (the "License"); +you may not use this file except in compliance with the License. You may +obtain a copy of the License at http://solderpad.org/licenses/SHL-0.51. Unless +required by applicable law or agreed to in writing, software, hardware and +materials distributed under this License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See +the License for the specific language governing permissions and limitations +under the License. diff --git a/REUSE.toml b/REUSE.toml index fec000858562..bb9cb1b1ce3b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -292,3 +292,10 @@ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Used only when testing the python-devicetree library and never linked into the firmware." Zephyr-Description = "Python Devicetree Library Test Files" Zephyr-Origin = "https://github.com/zephyrproject-rtos/python-devicetree" + +[[annotations]] +path = "include/zephyr/arch/riscv/csr.h" +SPDX-License-Identifier = "SHL-0.51" +SPDX-FileComment = "RISC-V control and status register access helpers, also available under Apache-2.0; compiled into the firmware only on RISC-V targets." +Zephyr-Description = "RISC-V Control and Status Register Header" +Zephyr-Kconfig-Condition = "CONFIG_RISCV" From f18a8a19c9421ab102bd335cca04933a114a1fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:25 +0200 Subject: [PATCH 088/600] doc: licensing: document the RV32M1 VEGA OpenOCD config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rv32m1_vega board OpenOCD configuration is imported from NXP under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index bb9cb1b1ce3b..bdadec2eb5af 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -299,3 +299,12 @@ SPDX-License-Identifier = "SHL-0.51" SPDX-FileComment = "RISC-V control and status register access helpers, also available under Apache-2.0; compiled into the firmware only on RISC-V targets." Zephyr-Description = "RISC-V Control and Status Register Header" Zephyr-Kconfig-Condition = "CONFIG_RISCV" + +[[annotations]] +path = [ + "boards/openisa/rv32m1_vega/support/openocd_rv32m1_vega_ri5cy.cfg", + "boards/openisa/rv32m1_vega/support/openocd_rv32m1_vega_zero_riscy.cfg", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Used by OpenOCD when programming and debugging the rv32m1_vega board. Never linked into the firmware." +Zephyr-Description = "RV32M1 VEGA Board OpenOCD Configuration" From ebb04ada9de3c86cf4d6dc9d530fc4c392423258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:25 +0200 Subject: [PATCH 089/600] doc: licensing: document the TF-M secure partition sample files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TF-M secure partition sample manifests are imported from Trusted Firmware-M under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index bdadec2eb5af..3dff73154605 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -308,3 +308,13 @@ path = [ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Used by OpenOCD when programming and debugging the rv32m1_vega board. Never linked into the firmware." Zephyr-Description = "RV32M1 VEGA Board OpenOCD Configuration" + +[[annotations]] +path = [ + "samples/tfm_integration/tfm_secure_partition/dummy_partition/tfm_dummy_partition.yaml.in", + "samples/tfm_integration/tfm_secure_partition/dummy_partition/tfm_manifest_list.yaml.in", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Used only when building the TF-M secure partition integration sample." +Zephyr-Description = "TF-M Secure Partition Sample Files" +Zephyr-Origin = "https://www.trustedfirmware.org/projects/tf-m/" From 7b0e7876674e8c3f1402794bd026dabbbf81052e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:25 +0200 Subject: [PATCH 090/600] doc: licensing: document the Thread-Metric test files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Thread-Metric RTOS test suite is imported from ThreadX under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 3dff73154605..bae8c7a3412e 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -318,3 +318,14 @@ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Used only when building the TF-M secure partition integration sample." Zephyr-Description = "TF-M Secure Partition Sample Files" Zephyr-Origin = "https://www.trustedfirmware.org/projects/tf-m/" + +[[annotations]] +path = [ + "tests/benchmarks/thread_metric/src/*.c", + "tests/benchmarks/thread_metric/src/*.h", + "tests/benchmarks/thread_metric/thread_metric_readme.txt", +] +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Linked only into the Thread-Metric RTOS Test Suite test firmware." +Zephyr-Description = "Thread-Metric RTOS Test Suite Source Files" +Zephyr-Origin = "https://github.com/eclipse-threadx/threadx" From b2274d7bfb2f372d9141b05faceb1edd051a7c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:25 +0200 Subject: [PATCH 091/600] doc: licensing: document the Tippy.js library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tippy.js renders the documentation's Doxygen tooltips under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index bae8c7a3412e..3b243ff5087e 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -329,3 +329,10 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Linked only into the Thread-Metric RTOS Test Suite test firmware." Zephyr-Description = "Thread-Metric RTOS Test Suite Source Files" Zephyr-Origin = "https://github.com/eclipse-threadx/threadx" + +[[annotations]] +path = "doc/_extensions/zephyr/doxytooltip/static/tippy/tippy-bundle.umd.min.js" +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Renders the tooltips of the documentation's Doxygen tooltip extension and is never linked into a firmware." +Zephyr-Description = "Tippy.js Library" +Zephyr-Origin = "https://atomiks.github.io/tippyjs/" From 67e862c37abb9f1a223082f3f1fea0145720aa48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:25 +0200 Subject: [PATCH 092/600] doc: licensing: document the UF2 conversion script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UF2 conversion script is imported from Microsoft's uf2 project under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 3b243ff5087e..658a6ad5d88f 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -336,3 +336,10 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Renders the tooltips of the documentation's Doxygen tooltip extension and is never linked into a firmware." Zephyr-Description = "Tippy.js Library" Zephyr-Origin = "https://atomiks.github.io/tippyjs/" + +[[annotations]] +path = "scripts/build/uf2conv.py" +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Converts firmware images to UF2 at build time and is never linked into the firmware." +Zephyr-Description = "UF2 Conversion Script" +Zephyr-Origin = "https://github.com/microsoft/uf2" From dfc378892112b9b59ccda9a8ddb3f18a1a7776ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:26 +0200 Subject: [PATCH 093/600] doc: licensing: document the USB device stack headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parts of the legacy USB device stack headers are imported under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 658a6ad5d88f..93cc7d96c3c3 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -343,3 +343,13 @@ SPDX-License-Identifier = "MIT" SPDX-FileComment = "Converts firmware images to UF2 at build time and is never linked into the firmware." Zephyr-Description = "UF2 Conversion Script" Zephyr-Origin = "https://github.com/microsoft/uf2" + +[[annotations]] +path = [ + "include/zephyr/usb/class/usb_dfu.h", + "include/zephyr/usb/usb_device.h", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Part of the legacy USB device stack, compiled into the firmware only when it is enabled." +Zephyr-Description = "USB Device Stack Headers" +Zephyr-Kconfig-Condition = "CONFIG_USB_DEVICE_STACK" From 526c4bc59b7a72ddc52bd27511e9028ba358d009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:26 +0200 Subject: [PATCH 094/600] doc: licensing: document the WireGuard files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WireGuard crypto files are imported from wireguard-lwip under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 93cc7d96c3c3..edd586930c1e 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -353,3 +353,15 @@ SPDX-License-Identifier = "BSD-3-Clause" SPDX-FileComment = "Part of the legacy USB device stack, compiled into the firmware only when it is enabled." Zephyr-Description = "USB Device Stack Headers" Zephyr-Kconfig-Condition = "CONFIG_USB_DEVICE_STACK" + +[[annotations]] +path = [ + "subsys/net/lib/wireguard/crypto/**/*.c", + "subsys/net/lib/wireguard/crypto/**/*.h", + "subsys/net/lib/wireguard/wg_crypto.c", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Compiled into the firmware only when WireGuard is enabled." +Zephyr-Description = "WireGuard VPN Files" +Zephyr-Origin = "https://github.com/smartalock/wireguard-lwip" +Zephyr-Kconfig-Condition = "CONFIG_WIREGUARD" From 19eaa0c87a0049a9541d5515941b2423d44c7b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:26 +0200 Subject: [PATCH 095/600] doc: licensing: document the Xen interface files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Xen grant-table driver is imported from the Xen Project under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index edd586930c1e..6ee306755e8c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -365,3 +365,11 @@ SPDX-FileComment = "Compiled into the firmware only when WireGuard is enabled." Zephyr-Description = "WireGuard VPN Files" Zephyr-Origin = "https://github.com/smartalock/wireguard-lwip" Zephyr-Kconfig-Condition = "CONFIG_WIREGUARD" + +[[annotations]] +path = "drivers/xen/gnttab.c" +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Compiled into the firmware only when Xen guest support is enabled." +Zephyr-Description = "Xen Hypervisor Interface Files" +Zephyr-Origin = "https://xenproject.org/" +Zephyr-Kconfig-Condition = "CONFIG_XEN" From b3a0922e669c34a94f255b4f44613730d0acd346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:26 +0200 Subject: [PATCH 096/600] doc: licensing: document the xoshiro128++ PRNG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xoshiro128++ PRNG is imported under CC0-1.0 (public domain dedication). Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- LICENSES/CC0-1.0.txt | 121 +++++++++++++++++++++++++++++++++++++++++++ REUSE.toml | 8 +++ 2 files changed, 129 insertions(+) create mode 100644 LICENSES/CC0-1.0.txt diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 000000000000..0e259d42c996 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/REUSE.toml b/REUSE.toml index 6ee306755e8c..e9324f079620 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -373,3 +373,11 @@ SPDX-FileComment = "Compiled into the firmware only when Xen guest support is en Zephyr-Description = "Xen Hypervisor Interface Files" Zephyr-Origin = "https://xenproject.org/" Zephyr-Kconfig-Condition = "CONFIG_XEN" + +[[annotations]] +path = "subsys/random/random_xoshiro128.c" +SPDX-License-Identifier = "CC0-1.0" +SPDX-FileComment = "Compiled into the firmware only when the xoshiro128++ PRNG is selected." +Zephyr-Description = "xoshiro128++ PRNG" +Zephyr-Origin = "https://prng.di.unimi.it/" +Zephyr-Kconfig-Condition = "CONFIG_XOSHIRO_RANDOM_GENERATOR" From 32ffb3c41400c78589665a9fbb81040920375937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:27 +0200 Subject: [PATCH 097/600] doc: licensing: document the Xtensa startup assembly files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Xtensa startup assembly files are imported from Cadence under MIT. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- REUSE.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index e9324f079620..febffc9dfe8c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -381,3 +381,13 @@ SPDX-FileComment = "Compiled into the firmware only when the xoshiro128++ PRNG i Zephyr-Description = "xoshiro128++ PRNG" Zephyr-Origin = "https://prng.di.unimi.it/" Zephyr-Kconfig-Condition = "CONFIG_XOSHIRO_RANDOM_GENERATOR" + +[[annotations]] +path = [ + "arch/xtensa/core/startup/memctl_default.S", + "arch/xtensa/core/startup/memerror_vector.S", +] +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Compiled into the firmware only when the Xtensa reset vector is enabled." +Zephyr-Description = "Xtensa Startup Assembly Files" +Zephyr-Kconfig-Condition = "CONFIG_XTENSA_RESET_VECTOR" From 1394a9a5ae605d504806554c3e1b8703fef9a161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 27 Aug 2026 14:29:20 +0200 Subject: [PATCH 098/600] doc: licensing: document the Modern CMake Sphinx domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CMake Sphinx domain used to render the documentation is imported from Kitware under BSD-3-Clause. Add a REUSE.toml exception so the component appears on the licensing page and is accepted by the compliance guard. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- REUSE.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index febffc9dfe8c..32d7245bf721 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -213,6 +213,17 @@ SPDX-FileComment = "Compiled into the firmware only when the minimal C library i Zephyr-Description = "Minimal libc BSD String and Conversion Files" Zephyr-Kconfig-Condition = "CONFIG_MINIMAL_LIBC" +[[annotations]] +path = [ + "doc/_extensions/moderncmakedomain/__init__.py", + "doc/_extensions/moderncmakedomain/cmake.py", + "doc/_extensions/moderncmakedomain/colors.py", +] +SPDX-License-Identifier = "BSD-3-Clause" +SPDX-FileComment = "Provide the CMake domain used to render the documentation and are never linked into a firmware." +Zephyr-Description = "Modern CMake Sphinx Domain" +Zephyr-Origin = "https://gitlab.kitware.com/cmake/cmake/-/tree/master/Utilities/Sphinx" + [[annotations]] path = [ "doc/_extensions/zephyr/domain/static/css/nouislider.min.css", From 794b2ef3f2a1197e800f51471fe7a96eed89b282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 27 Jul 2026 17:13:32 +0200 Subject: [PATCH 099/600] scripts: tests: ci: test_test_plan_v2: add reuse ignore tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file isn't rely carrying BSD-3-Clause license, it's test data. Flag as reuse ignore. Signed-off-by: Benjamin Cabé --- scripts/tests/ci/test_test_plan_v2.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/tests/ci/test_test_plan_v2.py b/scripts/tests/ci/test_test_plan_v2.py index c36fceb419e3..c765cda4bd24 100644 --- a/scripts/tests/ci/test_test_plan_v2.py +++ b/scripts/tests/ci/test_test_plan_v2.py @@ -422,6 +422,7 @@ def _fake_diff(*args, **kwargs): # ------------------------------------------------------------------ def test_pure_spdx_change(self): + # REUSE-IgnoreStart diff = textwrap.dedent("""\ --- a/drivers/foo/foo.c +++ b/drivers/foo/foo.c @@ -429,6 +430,7 @@ def test_pure_spdx_change(self): -// SPDX-License-Identifier: BSD-3-Clause +// SPDX-License-Identifier: Apache-2.0 """) + # REUSE-IgnoreEnd s = tp.BoilerplateFilter() assert s._all_changes_boilerplate(diff) is True @@ -516,6 +518,7 @@ def test_whitespace_only_file_consumed(self): assert "drivers/foo/foo.c" in handled def test_boilerplate_file_consumed(self): + # REUSE-IgnoreStart spdx_diff = textwrap.dedent("""\ --- a/drivers/foo/foo.c +++ b/drivers/foo/foo.c @@ -523,6 +526,7 @@ def test_boilerplate_file_consumed(self): -// SPDX-License-Identifier: BSD-3-Clause +// SPDX-License-Identifier: Apache-2.0 """) + # REUSE-IgnoreEnd # ws_only diff is non-empty (SPDX is not whitespace), full diff has only boilerplate s = self._strategy({"drivers/foo/foo.c": (spdx_diff, spdx_diff)}) _, handled = s.analyze(["drivers/foo/foo.c"]) From 0d94fc917d355ac21f8c06516d77b472b74bc8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:28 +0200 Subject: [PATCH 100/600] ci: doc-build: install reuse with the custom_properties API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The zephyr.licensing docs extension needs reuse's custom_properties API, which is not yet in a released reuse. Install it from the reuse-tool repo as a temporary measure until a new release makes it to PyPi. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- doc/requirements.in | 4 ++++ doc/requirements.txt | 22 +++++++++++++++++++++- scripts/requirements-actions.in | 3 ++- scripts/requirements-actions.txt | 5 ++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/doc/requirements.in b/doc/requirements.in index 9b89a6a9cbe7..42c7bc719b6d 100644 --- a/doc/requirements.in +++ b/doc/requirements.in @@ -37,3 +37,7 @@ python-dotenv # Used to export requirements from the reqmgmt module (StrictDoc) strictdoc>=0.16.2 + +# Used by the licensing extension +reuse @ https://codeberg.org/fsfe/reuse-tool/archive/f94ae0b1bb1d4d2ea91f49df88f3184c5d781a1d.tar.gz \ + --hash=sha256:33014f72d8bd184974ec1d61d9f81a6bfff3ba8f582a58d6ee6881a49e6819cd diff --git a/doc/requirements.txt b/doc/requirements.txt index 5f2b83317a91..8c19562cbb8b 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -31,6 +31,7 @@ attrs==26.1.0 \ --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via # outcome + # reuse # trio babel==2.18.0 \ --hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \ @@ -255,6 +256,7 @@ click==8.4.2 \ --hash=sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6 \ --hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76 # via + # reuse # spdx-tools # uvicorn colorama==0.4.6 \ @@ -382,6 +384,7 @@ jinja2==3.1.6 \ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # via # reqif + # reuse # sphinx # sphinxcontrib-mermaid # strictdoc @@ -392,7 +395,9 @@ lark==1.3.1 \ license-expression==30.4.4 \ --hash=sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4 \ --hash=sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd - # via spdx-tools + # via + # reuse + # spdx-tools lxml==6.1.1 \ --hash=sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2 \ --hash=sha256:07a4a68e286ee7a1ed7dfb8af83e615757c0ccfe9f18c6b4ea6771388d9ba8c9 \ @@ -1009,6 +1014,10 @@ python-dateutil==2.9.0.post0 \ # via # pandas # pykwalify +python-debian==1.1.1 \ + --hash=sha256:f98ae013e8e5310e49041cc3860a7105df73af73d4ff1d8afb474770d328a6ad \ + --hash=sha256:fe4fc3dc798dbf1f0ef5865e2b1b4f7cc0352b6a511b25ab7594906c64a73629 + # via reuse python-dotenv==1.2.2 \ --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 @@ -1016,6 +1025,10 @@ python-dotenv==1.2.2 \ # -r requirements.in # uvicorn # webdriver-manager +python-magic==0.4.27 \ + --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ + --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 + # via reuse python-multipart==0.0.32 \ --hash=sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e \ --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23 @@ -1114,6 +1127,9 @@ requests==2.34.2 \ # html2pdf4doc # sphinx # webdriver-manager +reuse @ https://codeberg.org/fsfe/reuse-tool/archive/f94ae0b1bb1d4d2ea91f49df88f3184c5d781a1d.tar.gz \ + --hash=sha256:33014f72d8bd184974ec1d61d9f81a6bfff3ba8f582a58d6ee6881a49e6819cd + # via -r requirements.in robotframework==7.4.2 \ --hash=sha256:1c934e7f43600de407860cd2bd2fdc41adad4a4a785d8b46b1ed485fdc0f6c9f \ --hash=sha256:6e80f84cdc997bdde2abb6b729ac3531457ecf6d2e41abfb87a541877ab367bf @@ -1276,6 +1292,10 @@ toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f # via strictdoc +tomlkit==0.15.1 \ + --hash=sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304 \ + --hash=sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97 + # via reuse tree-sitter==0.25.2 \ --hash=sha256:0628671f0de69bb279558ef6b640bcfc97864fe0026d840f872728a86cd6b6cd \ --hash=sha256:0c8b6682cac77e37cfe5cf7ec388844957f48b7bd8d6321d0ca2d852994e10d5 \ diff --git a/scripts/requirements-actions.in b/scripts/requirements-actions.in index bdee155cb4dc..496cc4c6b87a 100644 --- a/scripts/requirements-actions.in +++ b/scripts/requirements-actions.in @@ -27,7 +27,8 @@ python-dotenv python-magic-bin; sys_platform == "win32" python-magic; sys_platform != "win32" pyyaml>=5.4 -reuse +reuse @ https://codeberg.org/fsfe/reuse-tool/archive/f94ae0b1bb1d4d2ea91f49df88f3184c5d781a1d.tar.gz \ + --hash=sha256:33014f72d8bd184974ec1d61d9f81a6bfff3ba8f582a58d6ee6881a49e6819cd ruff==0.14.2 setuptools>=70.2.0 spdx-tools diff --git a/scripts/requirements-actions.txt b/scripts/requirements-actions.txt index 788ae4e31734..a09317d137be 100644 --- a/scripts/requirements-actions.txt +++ b/scripts/requirements-actions.txt @@ -1357,9 +1357,8 @@ requests==2.34.2 \ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed # via pygithub -reuse==6.2.0 \ - --hash=sha256:12b68549bb9d5f4957f06d726a83a9780628810008fb732bb0d0f21607f8c6d6 \ - --hash=sha256:4feae057a2334c9a513e6933cdb9be819d8b822f3b5b435a36138bd218897d23 +reuse @ https://codeberg.org/fsfe/reuse-tool/archive/f94ae0b1bb1d4d2ea91f49df88f3184c5d781a1d.tar.gz \ + --hash=sha256:33014f72d8bd184974ec1d61d9f81a6bfff3ba8f582a58d6ee6881a49e6819cd # via -r requirements-actions.in rpds-py==2026.6.3 \ --hash=sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5 \ From 3afe97fee4bd6cde30e612f77cb72826c3ecdb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:27 +0200 Subject: [PATCH 101/600] scripts: list files with an undocumented license MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spotting a file that carries a license other than the project defaults (Apache-2.0, or CC-BY-4.0 for docs) and is not yet described in REUSE.toml meant eyeballing raw "reuse lint" output. Add a small helper, built on the reuse library, that reports exactly those files, as a CLI and as an importable function reused by the docs licensing page and the CI compliance check. Identifiers that reuse extracts from tooling source or malformed comment banners but that SPDX does not recognize are ignored, so the report only lists real licenses. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- scripts/list_undocumented_licenses.py | 147 ++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100755 scripts/list_undocumented_licenses.py diff --git a/scripts/list_undocumented_licenses.py b/scripts/list_undocumented_licenses.py new file mode 100755 index 000000000000..e617ac3b2d8a --- /dev/null +++ b/scripts/list_undocumented_licenses.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +"""List files whose license is not allowed and not yet documented. + +Zephyr as a whole is Apache-2.0; per the project charter CC-BY-4.0 is additionally allowed for +documentation. Any other license (or CC-BY-4.0 on a non-documentation file) is a *licensing +exception* that must be described in the ``[[annotations]]`` blocks of ``REUSE.toml``. + +This script uses the ``reuse`` library to resolve the actual license of every file in the tree and +reports the ones that carry a non-allowed license without such an exception, i.e. the files that +still need a ``REUSE.toml`` entry. + +Run it standalone (the whole tree, or the paths given on the command line):: + + scripts/list_undocumented_licenses.py + scripts/list_undocumented_licenses.py subsys/foo/bar.c + +or import :func:`undocumented` to reuse the same logic (e.g. from the docs licensing extension or +the CI compliance check). Exit status is non-zero when anything is reported. + +SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +SPDX-License-Identifier: Apache-2.0 +""" + +from __future__ import annotations + +import argparse +import sys +from collections.abc import Iterable, Iterator +from pathlib import Path, PurePath + +from license_expression import get_spdx_licensing +from reuse.global_licensing import AnnotationsItem, ReuseTOML +from reuse.project import Project + +ZEPHYR_BASE = Path(__file__).resolve().parents[1] + +#: License(s) that apply to the tree as a whole and never need an exception. +DEFAULT_LICENSES = frozenset({"Apache-2.0"}) + +#: Extra license(s) allowed without an exception, keyed by file suffix +#: (CC-BY-4.0 is documentation-only per the project charter). +EXTRA_LICENSES = {".rst": frozenset({"CC-BY-4.0"})} + +#: A REUSE annotation is a documented exception only if it carries this key; +#: the licensing page is built from those annotations. +MARKER_KEY = "Zephyr-Description" + +#: SPDX license database, used to tell a real license from parser noise. +_SPDX_LICENSING = get_spdx_licensing() + + +def is_known_license(expr: str) -> bool: + """Return whether *expr* is a recognized SPDX license expression. + + ``reuse`` extracts every ``SPDX-License-Identifier`` fragment it finds, including ones embedded + in tooling source (files that generate or parse SPDX tags) or in malformed comment banners. + Those parse to junk symbols; ignoring anything SPDX does not recognize keeps the report to real + licenses. + """ + expr = expr.strip() + if not expr: + return False + try: + info = _SPDX_LICENSING.validate(expr) + except Exception: + return False + return not info.errors and not info.invalid_symbols + + +def allowed_licenses(path: str | PurePath) -> frozenset[str]: + """Return the licenses that need no exception for *path*, given its type.""" + return DEFAULT_LICENSES | EXTRA_LICENSES.get(PurePath(path).suffix, frozenset()) + + +def documented_exceptions(project: Project) -> list[AnnotationsItem]: + """Return the REUSE.toml annotations that document a licensing exception. + + An annotation is a documented exception when it has an explicitly set ``Zephyr-Description`` + and ``SPDX-FileComment`` justification.. + """ + toml = ReuseTOML.from_file(project.root / "REUSE.toml") + return [item for item in toml.annotations if MARKER_KEY in item.custom_properties] + + +def resolved_licenses(project: Project, path: Path) -> set[str]: + """Return the SPDX license expression(s) ``reuse`` resolves for *path*.""" + info = project.reuse_info_of(path) + return {str(expr) for item in info for expr in item.spdx_expressions} + + +def undocumented( + project: Project, + paths: Iterable[Path | str] | None = None, +) -> Iterator[tuple[str, set[str]]]: + """Yield ``(relative path, licenses)`` for files that still need an exception. + + A file is reported when ``reuse`` resolves a license for it that is not allowed for its type and + no documented exception matches it. *paths* may be tree-relative or absolute; it defaults to + every file in the tree. + """ + exceptions = documented_exceptions(project) + for path in project.all_files() if paths is None else paths: + abspath = project.root / path # an absolute *path* overrides project.root + rel = abspath.relative_to(project.root).as_posix() + licenses = {lic for lic in resolved_licenses(project, abspath) if is_known_license(lic)} + extra = licenses - allowed_licenses(rel) + if extra and not any(item.matches(rel) for item in exceptions): + yield rel, extra + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0], allow_abbrev=False) + parser.add_argument( + "paths", + nargs="*", + type=Path, + help="files to check (default: every file in the tree)", + ) + args = parser.parse_args() + + project = Project.from_directory(ZEPHYR_BASE) + paths = [p.resolve() for p in args.paths] or None + + found = False + for rel, licenses in sorted(undocumented(project, paths)): + print(f"{rel}: {', '.join(sorted(licenses))}") + found = True + + if found: + sys.stdout.flush() + print( + "\nThe files above carry a license other than the project defaults. Importing code " + "under another license has prerequisites; make sure the steps in " + "https://docs.zephyrproject.org/latest/contribute/guidelines.html" + "#components-using-other-licenses have been followed before documenting each component " + "as an [[annotations]] entry in REUSE.toml.", + file=sys.stderr, + ) + return 1 if found else 0 + + +if __name__ == "__main__": + sys.exit(main()) From 82b40dd39414021b43170afadb3090908cefe256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:27 +0200 Subject: [PATCH 102/600] doc: generate the licensing page from REUSE metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hand-maintained list of licensing exceptions kept drifting from reality (duplicated, incomplete and mislabelled entries). Generate the page instead from REUSE.toml with a new zephyr.licensing extension that resolves licenses through the reuse library and reads the Zephyr-specific annotation keys from its custom_properties. That API is not yet in a released reuse; the docs CI installs it from the reuse-tool pull request. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- doc/LICENSING.rst | 226 ++-------------------- doc/_extensions/zephyr/licensing.py | 281 ++++++++++++++++++++++++++++ doc/conf.py | 1 + doc/contribute/external.rst | 6 +- 4 files changed, 306 insertions(+), 208 deletions(-) create mode 100644 doc/_extensions/zephyr/licensing.py diff --git a/doc/LICENSING.rst b/doc/LICENSING.rst index e0a117053d57..15b018d334ad 100644 --- a/doc/LICENSING.rst +++ b/doc/LICENSING.rst @@ -5,218 +5,32 @@ Licensing of Zephyr Project components ###################################### -The Zephyr kernel tree imports or reuses packages, scripts and other files that -are not covered by the `Apache 2.0 License`_. In some places -there is no LICENSE file or way to put a LICENSE file there, so we describe the -licensing in this document. +Zephyr as a whole is licensed under the `Apache 2.0 License`_. It does, however, +import or reuse a small number of packages, scripts and other files that are +covered by other licenses. In some cases there is no way to add a license header +to those files, so their licensing is declared centrally, in a machine-readable +form, in the :zephyr_file:`REUSE.toml` file at the root of the repository +(following the `REUSE specification`_). -.. zephyr-keep-sorted-start re(^\w) ignorecase +The sections below are **generated automatically** from that metadata, so they +always reflect the actual state of the tree. To add, update or remove an entry, +edit the corresponding ``[[annotations]]`` block in :zephyr_file:`REUSE.toml` +rather than this page (see :ref:`external-contributions`). -Bootstrap JavaScript and CSS Files ----------------------------------- +.. note:: -* *Origin:* Bootstrap -* *Licensing:* `MIT License`_ -* *Impact:* These files are used in the :ref:`dashboard` tool and never linked into the firmware. -* *Files:* + This page lists licensing *exceptions* only. It does **not** define the + license of the Zephyr project itself, which is Apache 2.0 as specified in the + :zephyr_file:`LICENSE` file at the root of the repository. - * :zephyr_file:`scripts/dashboard/static/js/bootstrap-chop.js` - * :zephyr_file:`scripts/dashboard/static/css/bootstrap-chop.css` +.. contents:: Documented components + :local: + :depth: 1 -Coccinelle Scripts ------------------- - - * *Origin:* Coccinelle - * *Licensing:* `GPLv2 License`_ - * *Impact:* These files are used by `Coccinelle`_, a tool for transforming C-code, and never linked - into the firmware. - * *Files:* - - * :zephyr_file:`scripts/coccicheck` - * :zephyr_file:`scripts/coccinelle/array_size.cocci` - * :zephyr_file:`scripts/coccinelle/deref_null.cocci` - * :zephyr_file:`scripts/coccinelle/deref_null.cocci` - * :zephyr_file:`scripts/coccinelle/deref_null.cocci` - * :zephyr_file:`scripts/coccinelle/mini_lock.cocci` - * :zephyr_file:`scripts/coccinelle/mini_lock.cocci` - * :zephyr_file:`scripts/coccinelle/mini_lock.cocci` - * :zephyr_file:`scripts/coccinelle/noderef.cocci` - * :zephyr_file:`scripts/coccinelle/noderef.cocci` - * :zephyr_file:`scripts/coccinelle/returnvar.cocci` - * :zephyr_file:`scripts/coccinelle/semicolon.cocci` - -Continuous Integration Scripts ------------------------------- - -* *Origin:* Linux Kernel -* *Licensing:* `GPLv2 License`_ -* *Impact:* These files are used in Continuous Integration (CI) and never linked into the firmware. -* *Files:* - - * :zephyr_file:`scripts/checkpatch.pl` - * :zephyr_file:`scripts/checkstack.pl` - * :zephyr_file:`scripts/spelling.txt` - -ENE KB1200_EVB Board OpenOCD Configuration ------------------------------------------- - -* *Licensing:* `GPLv2 License`_ -* *Impact:* This file is used by `OpenOCD`_ when programming and debugging the - :zephyr:board:`kb1200_evb` board. It is never linked into the firmware. -* *Files:* - - * :zephyr_file:`boards/ene/kb1200_evb/support/openocd.cfg` - -FUSE Interface Definition Header File --------------------------------------- - -* *Licensing:* `BSD-2-clause`_ -* *Impact:* This header is used in Zephyr build only if :kconfig:option:`CONFIG_FUSE_CLIENT` is enabled. -* *Files*: - - * :zephyr_file:`subsys/fs/fuse_client/fuse_abi.h` - -GCOV Coverage Header File -------------------------- - -* *Origin:* GCC, the GNU Compiler Collection -* *Licensing:* `GPLv2 License`_ with Runtime Library Exception -* *Impact:* This file is only linked into the firmware if :kconfig:option:`CONFIG_COVERAGE_GCOV` is - enabled. -* *Files:* - - * :zephyr_file:`subsys/testsuite/coverage/coverage.h` - -Godot Documentation Theme Files -------------------------------- - -* *Origin:* `Godot Engine documentation `_ -* *Licensing:* `CC-BY-3.0`_ -* *Impact:* These files customize the Sphinx Read the Docs theme used to render the documentation - and were used as a starting point for Zephyr's own theme. They are never linked into a firmware. -* *Files:* - - * :zephyr_file:`doc/_static/css/custom.css` - * :zephyr_file:`doc/_static/css/dark.css` - * :zephyr_file:`doc/_static/css/light.css` - * :zephyr_file:`doc/_static/js/custom.js` - -noUiSlider Library ------------------- - -* *Origin:* `noUiSlider `_ (Léon Gersen and contributors) -* *Licensing:* `MIT License`_ -* *Impact:* These files are used in the documentation to provide the flash/RAM range sliders in the - board catalog and are never linked into a firmware. -* *Files:* - - * :zephyr_file:`doc/_extensions/zephyr/domain/static/js/nouislider.min.js` - * :zephyr_file:`doc/_extensions/zephyr/domain/static/css/nouislider.min.css` - -OpenThread Spinel HDLC RCP Host Interface Files ------------------------------------------------ - -* *Origin:* OpenThread -* *Licensing:* `BSD-3-clause`_ -* *Impact:* These files are only linked into the firmware if :kconfig:option:`CONFIG_HDLC_RCP_IF` is - enabled. -* *Files*: - - * :zephyr_file:`modules/openthread/platform/hdlc_interface.hpp` - * :zephyr_file:`modules/openthread/platform/radio_spinel.cpp` - * :zephyr_file:`modules/openthread/platform/hdlc_interface.cpp` - -Popper.js Library ------------------ - -* *Origin:* `Popper.js `_ (Federico Zivolo and contributors) -* *Licensing:* `MIT License`_ -* *Impact:* This file is used by Tippy.js to position tooltips in the documentation's Doxygen - tooltip extension and is never linked into a firmware. -* *Files:* - - * :zephyr_file:`doc/_extensions/zephyr/doxytooltip/static/tippy/popper.min.js` - -Python Devicetree library test files ------------------------------------- - -* *Licensing:* `BSD-3-clause`_ -* *Impact:* These are only used for testing and never linked with the firmware. -* *Files*: - - * Various yaml files under ``scripts/dts/python-devicetree/tests`` - -Thread-Metric RTOS Test Suite Source Files ------------------------------------------- - -* *Origin:* ThreadX -* *Licensing:* `MIT License`_ -* *Impact:* These files are only linked into the Thread-Metric RTOS Test Suite test firmware. -* *Files:* - - * :zephyr_file:`tests/benchmarks/thread_metric/thread_metric_readme.txt` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_api.h` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_basic_processing_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_cooperative_scheduling_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_interrupt_preemption_processing_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_interrupt_processing_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_memory_allocation_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_message_processing_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_porting_layer.h` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_porting_layer_zephyr.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_preemptive_scheduling_test.c` - * :zephyr_file:`tests/benchmarks/thread_metric/src/tm_synchronization_processing_test.c` - -Tippy.js Library ----------------- - -* *Origin:* `Tippy.js `_ (atomiks) -* *Licensing:* `MIT License`_ -* *Impact:* This file is used by the documentation's Doxygen tooltip extension to render tooltips - and is never linked into a firmware. -* *Files:* - - * :zephyr_file:`doc/_extensions/zephyr/doxytooltip/static/tippy/tippy-bundle.umd.min.js` - -WireGuard VPN Files -------------------- - -* *Origin:* wireguard-lwip -* *Licensing:* `BSD-3-clause`_ -* *Impact:* These files are only linked into the firmware if :kconfig:option:`CONFIG_WIREGUARD` - is enabled. -* *Files with BSD-3-clause license*: - - * :zephyr_file:`subsys/net/lib/wireguard/wg_crypto.c` - * :zephyr_file:`subsys/net/lib/wireguard/crypto/crypto.h` - * :zephyr_file:`subsys/net/lib/wireguard/crypto/crypto.c` - * :zephyr_file:`subsys/net/lib/wireguard/crypto/refc/blake2s.h` - * :zephyr_file:`subsys/net/lib/wireguard/crypto/refc/blake2s.c` - * :zephyr_file:`subsys/net/lib/wireguard/crypto/refc/hchacha20.h` - * :zephyr_file:`subsys/net/lib/wireguard/crypto/refc/hchacha20.c` - -.. zephyr-keep-sorted-stop +.. zephyr-licensing-exceptions:: .. _Apache 2.0 License: https://github.com/zephyrproject-rtos/zephyr/blob/main/LICENSE -.. _GPLv2 License: - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/COPYING - -.. _MIT License: - https://opensource.org/licenses/MIT - -.. _BSD-3-clause: - https://opensource.org/license/bsd-3-clause - -.. _BSD-2-clause: - https://opensource.org/license/bsd-2-clause - -.. _CC-BY-3.0: - https://creativecommons.org/licenses/by/3.0/ - -.. _Coccinelle: - https://coccinelle.gitlabpages.inria.fr/website/ - -.. _OpenOCD: - https://openocd.org +.. _REUSE specification: + https://reuse.software/spec/ diff --git a/doc/_extensions/zephyr/licensing.py b/doc/_extensions/zephyr/licensing.py new file mode 100644 index 000000000000..ef732e79e524 --- /dev/null +++ b/doc/_extensions/zephyr/licensing.py @@ -0,0 +1,281 @@ +""" +SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +SPDX-License-Identifier: Apache-2.0 + +Licensing exceptions page generator +==================================== + +This extension renders the :ref:`Zephyr_Licensing` page directly from the machine-readable metadata +that already lives in the repository's ``REUSE.toml`` file, so the page can never drift from the +actual licensing of the tree. + +Rationale +********* + +Zephyr as a whole is licensed under Apache-2.0, but it imports or reuses a handful of components +that use other licenses. Those *deviations* have to be listed on the licensing page (see +:ref:`external-contributions`). Maintaining that list by hand is error prone: entries get +duplicated, forgotten, or go stale as files are added or removed. + +Instead of a hand-written list, every deviation is described once, as a standard `REUSE`_ +annotation, and this extension turns those annotations into the documentation page. + +The licensing/copyright facts are parsed and resolved with the ``reuse`` Python library itself (the +very same tool used to check REUSE compliance in CI), so the license and copyright shown for each +component are guaranteed to match what the tooling sees. + +Metadata format +*************** + +The licensing/copyright facts stay 100% standard REUSE/SPDX. Each deviation is a single +``[[annotations]]`` block in ``REUSE.toml`` that uses the standard keys plus, at most, three small +Zephyr-specific keys to *explain* the deviation (the `REUSE`_ tool ignores unknown keys): + +.. REUSE-IgnoreStart + +.. code-block:: toml + + [[annotations]] + path = "subsys/net/lib/wireguard/crypto/**" + SPDX-License-Identifier = "BSD-3-Clause" + SPDX-FileCopyrightText = "Copyright (c) 2021 Daniel Hope (www.floorsense.nz)" + SPDX-FileComment = "Only compiled into the firmware when the feature is enabled." + Zephyr-Description = "WireGuard VPN" + Zephyr-Origin = "wireguard-lwip " + Zephyr-Kconfig-Condition = "CONFIG_WIREGUARD" + +.. REUSE-IgnoreEnd + +Standard keys (the legally meaningful metadata): + +``SPDX-License-Identifier`` + The license of the component. This is what is rendered as the component's license and is + validated by the ``reuse`` tool. +``SPDX-FileCopyrightText`` + Copyright notice(s). +``SPDX-FileComment`` + Free-text justification explaining *why* the deviation is acceptable (rendered as the "Impact" + of the component). This is a standard SPDX tag. + +``reuse`` interprets none of the following keys but preserves them on the annotation's +``custom_properties``. They are all Zephyr-specific: SPDX has no non-deprecated *file*-level field +for an upstream URL (the ``ArtifactOf*`` file tags were deprecated in favour of relationships, which +REUSE.toml cannot express), and its ``Package*`` fields are not valid on files. + +``Zephyr-Description`` + Short human description of the component, used as its section title on the page. Its presence + is what turns a plain REUSE annotation into a documented licensing exception. +``Zephyr-Origin`` + URL of the upstream project the files come from, rendered as the component's "Origin" link. +``Zephyr-Kconfig-Condition`` + Space/comma separated Kconfig option(s) that must be enabled for the files to end up in a build. + Rendered with a :rst:role:`kconfig:option` cross reference. + +The list of files for each component is expanded from the ``path`` globs against the actual work +tree, so it is always accurate and never needs to be kept in sync by hand. + +Usage +***** + +Add the directive to a document (it takes no options):: + + .. zephyr-licensing-exceptions:: + +""" + +from __future__ import annotations + +import re +import sys +from functools import lru_cache +from pathlib import Path +from typing import Any, Final + +from docutils import nodes +from docutils.statemachine import StringList +from reuse.global_licensing import ReuseTOML +from reuse.project import Project +from sphinx.application import Sphinx +from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective + +__version__ = "0.1.0" + +ZEPHYR_BASE: Final[Path] = Path(__file__).parents[3] +REUSE_TOML: Final[Path] = ZEPHYR_BASE / "REUSE.toml" + +# Reuse the licensing-policy helpers shared with the CI compliance check: the marker key that turns +# a REUSE annotation into a documented exception, and license resolution through the reuse library. +sys.path.append(str(ZEPHYR_BASE / "scripts")) +from list_undocumented_licenses import MARKER_KEY, resolved_licenses # noqa: E402 + +logger = logging.getLogger(__name__) + + +@lru_cache(maxsize=1) +def _project() -> Project: + """The reuse project, used to resolve the *actual* license of a file.""" + return Project.from_directory(ZEPHYR_BASE) + + +def _spdx_url(license_expr: str) -> str | None: + """Return the canonical SPDX page for a *simple* license id, else ``None``. + + License *expressions* (containing ``WITH``/``OR``/``AND`` or parentheses) + have no single page, so they are rendered as plain text. + """ + if re.fullmatch(r"[A-Za-z0-9.\-+]+", license_expr): + return f"https://spdx.org/licenses/{license_expr}.html" + return None + + +class LicensingException: + """A single documented licensing deviation, resolved against the tree.""" + + def __init__(self, annotation: Any): + extra = annotation.custom_properties + self.title: str = extra[MARKER_KEY] + self.licenses: set[str] = {str(e) for e in annotation.spdx_expressions} + self.license: str = " / ".join(sorted(self.licenses)) + self.impact: str = extra.get("SPDX-FileComment", "") + self.origin: str = extra.get("Zephyr-Origin", "") + self.condition: str = extra.get("Zephyr-Kconfig-Condition", "") + + self._annotation = annotation + self.files: list[str] = self._resolve_files(annotation.paths) + + def _resolve_files(self, patterns: set[str]) -> list[str]: + found: set[str] = set() + for pattern in patterns: + matches = [p for p in ZEPHYR_BASE.glob(pattern) if p.is_file()] + if not matches: + logger.warning( + "licensing: pattern '%s' for component '%s' matches no files", + pattern, + self.title, + type="licensing", + ) + for match in matches: + found.add(match.relative_to(ZEPHYR_BASE).as_posix()) + return sorted(found) + + def validate(self) -> None: + """Warn if the declared license is not what ``reuse`` resolves. + + This cross-checks the documented license against the license the + ``reuse`` tool actually computes for each file (from its SPDX header + and/or ``REUSE.toml``), catching silent drift. + """ + for rel in self.files: + resolved = resolved_licenses(_project(), ZEPHYR_BASE / rel) + # Only flag when reuse resolved *something* that shares no license expression with what + # is documented (empty resolution is reported separately by the LicenseAndCopyrightCheck + # compliance test). + if resolved and not self.licenses & resolved: + logger.warning( + "licensing: '%s' is documented as '%s' but reuse resolves it to %s", + rel, + self.license, + ", ".join(sorted(resolved)), + type="licensing", + ) + + +def load_exceptions() -> list[LicensingException]: + """Parse ``REUSE.toml`` and return the documented licensing exceptions. + + An annotation is a documented exception when it has an explicitly set + ``Zephyr-Description`` and ``SPDX-FileComment`` justification. + """ + reuse_toml = ReuseTOML.from_file(REUSE_TOML) + exceptions = [ + LicensingException(annotation) + for annotation in reuse_toml.annotations + if MARKER_KEY in annotation.custom_properties + ] + exceptions.sort(key=lambda e: e.title.lower()) + return exceptions + + +class ZephyrLicensingExceptions(SphinxDirective): + """Render the licensing exceptions table from ``REUSE.toml``.""" + + has_content = False + + def _body_rst(self, exc: LicensingException) -> list[str]: + """reStructuredText for a component's body (everything but its title). + + Emitting RST for the body (rather than raw doctree nodes) lets the existing + ``:zephyr_file:`` and ``:kconfig:option:`` roles do the heavy lifting, so the page reuses + the theme's styling and needs no new CSS. + """ + lines: list[str] = [] + + # Metadata as a reST field list (rendered as a compact definition table + # by the RTD theme, no custom CSS required). + if exc.origin: + lines.append(f":Origin: {_format_origin(exc.origin)}") + + url = _spdx_url(exc.license) + license_field = f"`{exc.license} <{url}>`__" if url else f"``{exc.license}``" + lines.append(f":License: {license_field}") + + if exc.condition: + options = re.split(r"[,\s]+", exc.condition.strip()) + refs = ", ".join(f":kconfig:option:`{opt}`" for opt in options if opt) + lines.append(f":Enabled by: {refs}") + lines.append("") + + if exc.impact: + lines.append(exc.impact) + lines.append("") + + lines.append(f"Files ({len(exc.files)}):") + lines.append("") + for rel in exc.files: + lines.append(f"* :zephyr_file:`{rel}`") + lines.append("") + + return lines + + def _build_section(self, exc: LicensingException) -> nodes.section: + section = nodes.section() + section += nodes.title(text=exc.title) + section["ids"] = [nodes.make_id(exc.title)] + self.state.document.note_implicit_target(section, section) + + content = StringList(self._body_rst(exc), source="zephyr-licensing-exceptions") + self.state.nested_parse(content, self.content_offset, section) + return section + + def run(self) -> list[nodes.Node]: + try: + exceptions = load_exceptions() + except (OSError, ValueError, KeyError) as err: + logger.warning("licensing: could not read REUSE.toml: %s", err, type="licensing") + return [nodes.paragraph(text=f"Unable to generate licensing page: {err}")] + + if not exceptions: + return [nodes.paragraph(text="No licensing exceptions are currently declared.")] + + result: list[nodes.Node] = [] + for exc in exceptions: + exc.validate() + result.append(self._build_section(exc)) + return result + + +def _format_origin(url: str) -> str: + """Render an origin URL as a reST link labelled with its host and path.""" + label = re.sub(r"^https?://", "", url).rstrip("/") + return f"`{label} <{url}>`__" + + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_directive("zephyr-licensing-exceptions", ZephyrLicensingExceptions) + + return { + "version": __version__, + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/doc/conf.py b/doc/conf.py index db3b2e73f337..52858719ed06 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -111,6 +111,7 @@ "zephyr.doxytooltip", "zephyr.doxyxref", "zephyr.gh_utils", + "zephyr.licensing", "zephyr.manifest_projects_table", "notfound.extension", "sphinx_copybutton", diff --git a/doc/contribute/external.rst b/doc/contribute/external.rst index 44880fa1b92b..26cfc16bc204 100644 --- a/doc/contribute/external.rst +++ b/doc/contribute/external.rst @@ -87,8 +87,10 @@ automatically implies that the imported source code becomes part of the - The code is subject to the same checks and verification requirements as the rest of the code in the main tree, including static analysis - All files contain an SPDX tag if not already present -- If the source is not Apache 2.0 licensed, - an entry is added to the :ref:`licensing page `. +- If the source is not Apache 2.0 licensed, an ``[[annotations]]`` entry + describing the component is added to the :zephyr_file:`REUSE.toml` file at the + root of the repository. This is what the :ref:`licensing page + ` is generated from. This mode of integration can be applicable to both small and large external codebases, but it is typically used more commonly with the former. From 7235648f399f9c0d2b8ca30f4aabeb27a6fc0c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 9 Jul 2026 20:43:27 +0200 Subject: [PATCH 103/600] ci: check_compliance: require an exception for non-Apache-2.0 files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the generated licensing page complete: a non-Apache-2.0 file added to the main tree without a documented exception (a REUSE.toml annotation with a Zephyr-Description key) is now flagged. CC-BY-4.0 is accepted without an exception on .rst sources only, per the project charter. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-4.8 --- scripts/ci/check_compliance.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 62c88f878e1d..9d9e8f93d7f5 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -61,6 +61,7 @@ import list_boards import list_hardware from get_maintainer import Maintainers, MaintainersError +from list_undocumented_licenses import undocumented sys.path.insert( 0, str(Path(__file__).resolve().parents[2] / "scripts" / "dts" / "python-devicetree" / "src") @@ -1973,6 +1974,39 @@ def run(self) -> None: ), ) + self._check_documented_exceptions(project, changed_files) + + def _check_documented_exceptions(self, project: Project, changed_files: Iterable) -> None: + """Flag non-Apache-2.0 files that are not documented as an exception. + + Zephyr is Apache-2.0 as a whole; per the project charter CC-BY-4.0 is + allowed for documentation only. Any other license, or CC-BY-4.0 on a + non-documentation file, must be listed on the + :ref:`licensing page `. That page is generated from + the ``[[annotations]]`` blocks in ``REUSE.toml`` that carry a + ``Zephyr-Description`` key, so such a file is considered documented if and + only if it is matched by one of those blocks. The detection itself lives + in ``scripts/list_undocumented_licenses.py`` and is shared with that + tool and the docs licensing page. + """ + for file, licenses in undocumented(project, changed_files): + self.fmtd_failure( + "error", + "Undocumented license", + file, + line=1, + desc=( + f"File is licensed as {', '.join(sorted(licenses))}, which is not " + "Apache-2.0. Importing code under another license has prerequisites; make " + "sure the steps in " + "https://docs.zephyrproject.org/latest/contribute/guidelines.html" + "#components-using-other-licenses have been followed. Then document the " + "component as an [[annotations]] entry with a 'Zephyr-Description' key in " + "REUSE.toml, so it is listed on the licensing page " + "(https://docs.zephyrproject.org/latest/LICENSING.html)." + ), + ) + class GitLint(ComplianceTest): """ From de3012255ec884826db108c521c6b2c520c1c4ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 11:22:12 +0200 Subject: [PATCH 104/600] ci: doc-build: force a reinstall of the pinned reuse snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The requirements files pin an unreleased reuse-tool change (custom_properties) needed by the zephyr-licensing-exceptions:: directive. That snapshot reports the same version (6.2.0) as the release the doc build container already ships, so pip considers the requirement satisfied and silently keeps the PyPI build, which breaks the directive. Uninstall reuse first so that pip actually installs the snapshot. Both this and the git pin itself go away once an official reuse release on PyPI carries the custom_properties API. Assisted-by: Claude:opus-5 Signed-off-by: Benjamin Cabé --- .github/workflows/doc-build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index c1aaa13115d3..e0276cb7c69f 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -113,6 +113,13 @@ jobs: - name: Install Python packages required for documentation build run: | + # FIXME: The requirements files pin an unreleased reuse-tool change + # (custom_properties) needed by the zephyr-licensing-exceptions:: + # directive. The snapshot reports the same version as the + # release the CI image ships, so pip skips it unless reuse is + # uninstalled first. Drop this and the pin once the API is in a + # reuse release. + pip uninstall -y reuse pip install -r scripts/requirements-actions.txt --require-hashes pip install -r doc/requirements.txt --require-hashes From 882582e9045875be6603e1a29c0e7700d7115caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 17 Jul 2026 23:36:24 +0200 Subject: [PATCH 105/600] scripts: ci: check_compliance: explain KconfigHWMv2 undefined symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The KconfigHWMv2 check loads only the board and SoC Kconfig trees, so references to symbols defined elsewhere in Zephyr surface as bare kconfiglib "undefined symbol" warnings, which do not make the root cause (a misplaced symbol reference) clear to developers. Append probable causes and a fix suggestion to the failure message of that check, and link its documentation to the board porting guide. Fixes: #69838 Assisted-by: Claude:claude-fable-5 Signed-off-by: Benjamin Cabé --- scripts/ci/check_compliance.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 9d9e8f93d7f5..47164527f187 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -832,6 +832,11 @@ class KconfigCheck(ComplianceTest): # Kconfig symbol prefix/namespace. CONFIG_ = "CONFIG_" + # Additional guidance appended to the "Undefined Kconfig symbols" failure + # message. Subclasses can override this to describe probable causes + # specific to the Kconfig tree being checked. + UNDEF_SYMBOL_HINT = "" + def run(self): kconf = self.parse_kconfig() @@ -1514,7 +1519,10 @@ def is_allowed(warning): ) if undef_ref_warnings: - self.failure(f"Undefined Kconfig symbols:\n\n {undef_ref_warnings}") + msg = f"Undefined Kconfig symbols:\n\n {undef_ref_warnings}" + if self.UNDEF_SYMBOL_HINT: + msg += f"\n\n{self.UNDEF_SYMBOL_HINT}" + self.failure(msg) def check_soc_name_sync(self, kconf): root_args = argparse.Namespace(**{'soc_roots': [ZEPHYR_BASE]}) @@ -1720,11 +1728,24 @@ class KconfigHWMv2Check(KconfigBasicCheck): """ name = "KconfigHWMv2" + doc = zephyr_doc_detail_builder("/hardware/porting/board_porting.html#write-kconfig-files") # Use dedicated Kconfig board / soc v2 scheme file. # This file sources only v2 scheme tree. FILENAME = os.path.join(os.path.dirname(__file__), "Kconfig.board.v2") + UNDEF_SYMBOL_HINT = """\ +This check loads only the board and SoC Kconfig trees (Kconfig. and +Kconfig.soc files) without the rest of the Zephyr Kconfig tree, because those +files must be loadable standalone; for example, sysbuild also loads them. + +Probable causes of the undefined symbol warnings above: +- A Kconfig. or Kconfig.soc file references a symbol defined outside + the board and SoC Kconfig trees, for example a driver or subsystem symbol. + Move the reference to the Kconfig or Kconfig.defconfig file of the board or + SoC instead, as those files are only loaded in the full Zephyr Kconfig tree. +- The symbol name is misspelled, or the file defining it is not sourced.""" + class SysbuildKconfigCheck(KconfigCheck): """ From 69bf56625fb8c6d86c11abb3413a9134eed07ae8 Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Mon, 20 Jul 2026 17:54:31 +0800 Subject: [PATCH 106/600] bluetooth: classic: obex: use assertion in transport registration The function `bt_obex_reg_transport()` is used internally by the Bluetooth Classic Host, and its parameter should not be NULL unless there is a programming error. Convert `bt_obex_reg_transport()` from returning error codes to using runtime assertions. Replace if-condition NULL pointer checks with `__ASSERT()` to catch invalid parameters during development. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/goep.c | 14 ++------------ subsys/bluetooth/host/classic/obex.c | 8 ++------ subsys/bluetooth/host/classic/obex_internal.h | 2 +- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/subsys/bluetooth/host/classic/goep.c b/subsys/bluetooth/host/classic/goep.c index 798942531c70..532acc965d1a 100644 --- a/subsys/bluetooth/host/classic/goep.c +++ b/subsys/bluetooth/host/classic/goep.c @@ -175,7 +175,6 @@ static int goep_rfcomm_init(struct bt_conn *conn, struct bt_goep *goep) struct bt_goep_transport_v1 *goep_transport_v1 = goep->v1; uint32_t mtu; uint32_t hdr_size; - int err; hdr_size = BT_L2CAP_HDR_SIZE + BT_RFCOMM_OVERHEAD_SIZE; @@ -190,11 +189,7 @@ static int goep_rfcomm_init(struct bt_conn *conn, struct bt_goep *goep) return -EINVAL; } - err = bt_obex_reg_transport(&goep->obex, &goep_rfcomm_transport_ops); - if (err != 0) { - LOG_ERR("Fail to reg transport ops"); - return err; - } + bt_obex_reg_transport(&goep->obex, &goep_rfcomm_transport_ops); goep->_acl = conn; goep_transport_v1->goep = goep; @@ -475,7 +470,6 @@ static int goep_l2cap_init(struct bt_conn *conn, struct bt_goep *goep) struct bt_goep_transport_v2 *goep_transport_v2 = goep->v2; uint32_t mtu; uint32_t hdr_size; - int err; hdr_size = sizeof(struct bt_l2cap_hdr); @@ -490,11 +484,7 @@ static int goep_l2cap_init(struct bt_conn *conn, struct bt_goep *goep) return -EINVAL; } - err = bt_obex_reg_transport(&goep->obex, &goep_l2cap_transport_ops); - if (err != 0) { - LOG_ERR("Fail to reg transport ops"); - return err; - } + bt_obex_reg_transport(&goep->obex, &goep_l2cap_transport_ops); goep->_acl = conn; goep_transport_v2->goep = goep; diff --git a/subsys/bluetooth/host/classic/obex.c b/subsys/bluetooth/host/classic/obex.c index 0e31923724fa..0096905f2a86 100644 --- a/subsys/bluetooth/host/classic/obex.c +++ b/subsys/bluetooth/host/classic/obex.c @@ -1693,15 +1693,11 @@ int bt_obex_transport_disconnected(struct bt_obex *obex) return 0; } -int bt_obex_reg_transport(struct bt_obex *obex, const struct bt_obex_transport_ops *ops) +void bt_obex_reg_transport(struct bt_obex *obex, const struct bt_obex_transport_ops *ops) { - if (obex == NULL || ops == NULL) { - LOG_WRN("Invalid parameter"); - return -EINVAL; - } + __ASSERT(obex != NULL && ops != NULL, "Invalid obex instance or transport ops"); obex->_transport_ops = ops; - return 0; } int bt_obex_recv(struct bt_obex *obex, struct net_buf *buf) diff --git a/subsys/bluetooth/host/classic/obex_internal.h b/subsys/bluetooth/host/classic/obex_internal.h index 09ad1dce40ef..192fcbdf3213 100644 --- a/subsys/bluetooth/host/classic/obex_internal.h +++ b/subsys/bluetooth/host/classic/obex_internal.h @@ -52,7 +52,7 @@ struct bt_obex_setpath_req_hdr { } __packed; /* OBEX initialization */ -int bt_obex_reg_transport(struct bt_obex *obex, const struct bt_obex_transport_ops *ops); +void bt_obex_reg_transport(struct bt_obex *obex, const struct bt_obex_transport_ops *ops); /* Process the received OBEX packet */ int bt_obex_recv(struct bt_obex *obex, struct net_buf *buf); From a336abd250d49caca5f56d6f932b06dc9ee195d6 Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Mon, 20 Jul 2026 18:55:15 +0800 Subject: [PATCH 107/600] bluetooth: classic: goep: use assertions in transport initialization The `goep_rfcomm_init()` and `goep_l2cap_init()` functions are used internally during transport setup, and their error conditions represent programming errors. Convert both initialization functions from returning error codes to using runtime assertions. The change treats transport initialization failures as programming errors that should be caught during development. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/goep.c | 46 ++++++---------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/subsys/bluetooth/host/classic/goep.c b/subsys/bluetooth/host/classic/goep.c index 532acc965d1a..a7af7ba3559e 100644 --- a/subsys/bluetooth/host/classic/goep.c +++ b/subsys/bluetooth/host/classic/goep.c @@ -170,7 +170,7 @@ static const struct bt_obex_transport_ops goep_rfcomm_transport_ops = { .disconnect = goep_rfcomm_disconnect, }; -static int goep_rfcomm_init(struct bt_conn *conn, struct bt_goep *goep) +static void goep_rfcomm_init(struct bt_conn *conn, struct bt_goep *goep) { struct bt_goep_transport_v1 *goep_transport_v1 = goep->v1; uint32_t mtu; @@ -183,11 +183,8 @@ static int goep_rfcomm_init(struct bt_conn *conn, struct bt_goep *goep) /* Set the default MTU to the largest value that the configuration can support */ goep->obex.rx.mtu = mtu; - if (goep->obex.rx.mtu < GOEP_MIN_MTU) { - LOG_ERR("GOEP RFCOMM MTU less than minimum size (%d < %d)", goep->obex.rx.mtu, - GOEP_MIN_MTU); - return -EINVAL; - } + __ASSERT(goep->obex.rx.mtu >= GOEP_MIN_MTU, "GOEP RFCOMM MTU less than minimum size " + "(%d < %d)", goep->obex.rx.mtu, GOEP_MIN_MTU); bt_obex_reg_transport(&goep->obex, &goep_rfcomm_transport_ops); @@ -196,8 +193,6 @@ static int goep_rfcomm_init(struct bt_conn *conn, struct bt_goep *goep) goep_transport_v1->dlc.mtu = goep->obex.rx.mtu; goep_transport_v1->dlc.ops = &goep_rfcomm_ops; goep_transport_v1->dlc.required_sec_level = BT_SECURITY_L2; - - return 0; } static int goep_rfcomm_accept(struct bt_conn *conn, struct bt_rfcomm_server *server, @@ -225,11 +220,7 @@ static int goep_rfcomm_accept(struct bt_conn *conn, struct bt_rfcomm_server *ser return -EINVAL; } - err = goep_rfcomm_init(conn, goep); - if (err != 0) { - LOG_ERR("Fail to init goep"); - return err; - } + goep_rfcomm_init(conn, goep); *dlc = &goep->v1->dlc; @@ -275,11 +266,7 @@ int bt_goep_transport_rfcomm_connect(struct bt_conn *conn, struct bt_goep *goep, return -EINVAL; } - err = goep_rfcomm_init(conn, goep); - if (err != 0) { - LOG_ERR("Fail to init goep"); - return err; - } + goep_rfcomm_init(conn, goep); err = bt_rfcomm_dlc_connect(conn, &goep->v1->dlc, channel); if (err != 0) { @@ -465,7 +452,7 @@ static const struct bt_obex_transport_ops goep_l2cap_transport_ops = { .disconnect = goep_l2cap_disconnect, }; -static int goep_l2cap_init(struct bt_conn *conn, struct bt_goep *goep) +static void goep_l2cap_init(struct bt_conn *conn, struct bt_goep *goep) { struct bt_goep_transport_v2 *goep_transport_v2 = goep->v2; uint32_t mtu; @@ -478,11 +465,8 @@ static int goep_l2cap_init(struct bt_conn *conn, struct bt_goep *goep) /* Set the default MTU to the largest value that the configuration can support */ goep->obex.rx.mtu = mtu; - if (goep->obex.rx.mtu < GOEP_MIN_MTU) { - LOG_ERR("GOEP L2CAP MTU less than minimum size (%d < %d)", goep->obex.rx.mtu, - GOEP_MIN_MTU); - return -EINVAL; - } + __ASSERT(goep->obex.rx.mtu >= GOEP_MIN_MTU, "GOEP L2CAP MTU less than minimum size " + "(%d < %d)", goep->obex.rx.mtu, GOEP_MIN_MTU); bt_obex_reg_transport(&goep->obex, &goep_l2cap_transport_ops); @@ -503,8 +487,6 @@ static int goep_l2cap_init(struct bt_conn *conn, struct bt_goep *goep) goep_transport_v2->chan.rx.fcs = BT_L2CAP_BR_FCS_16BIT; goep_transport_v2->chan.chan.ops = &goep_l2cap_ops; goep_transport_v2->chan.required_sec_level = BT_SECURITY_L2; - - return 0; } static int goep_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server, @@ -532,11 +514,7 @@ static int goep_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *serve return -EINVAL; } - err = goep_l2cap_init(conn, goep); - if (err != 0) { - LOG_ERR("Fail to init goep"); - return err; - } + goep_l2cap_init(conn, goep); *chan = &goep->v2->chan.chan; atomic_set(&goep->_state, BT_GOEP_TRANSPORT_CONNECTING); @@ -588,11 +566,7 @@ int bt_goep_transport_l2cap_connect(struct bt_conn *conn, struct bt_goep *goep, return -EBUSY; } - err = goep_l2cap_init(conn, goep); - if (err != 0) { - LOG_ERR("Fail to init goep"); - return err; - } + goep_l2cap_init(conn, goep); err = bt_l2cap_chan_connect(conn, &goep->v2->chan.chan, psm); if (err != 0) { From 5dd0aa9271c9097eee078aec80f3ae38874481fa Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Mon, 20 Jul 2026 18:56:37 +0800 Subject: [PATCH 108/600] bluetooth: classic: goep: use assertions in accept callbacks In original implementation, there is a corner case that the instance has been allocated by upper layer and passed it through `accept()` callbacks. And normally the allocated instance will be destroyed after the connection broken. While in the post-accept, due to some errors, it is possible that the connection may not be accepted by `goep_rfcomm_accept()` or `goep_l2cap_accept()` functions. And all of these errors are programming errors. Replace programming error checking with runtime assertions in `goep_rfcomm_accept()` and `goep_l2cap_accept()` functions. The parameter validation and initialization error checks are converted to `__ASSERT()` calls since these conditions indicate programming errors rather than runtime failures. The change is used to treat invalid instance parameters as programming errors that should be caught during development. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/goep.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/host/classic/goep.c b/subsys/bluetooth/host/classic/goep.c index a7af7ba3559e..eb563c3ddf3f 100644 --- a/subsys/bluetooth/host/classic/goep.c +++ b/subsys/bluetooth/host/classic/goep.c @@ -199,7 +199,7 @@ static int goep_rfcomm_accept(struct bt_conn *conn, struct bt_rfcomm_server *ser struct bt_rfcomm_dlc **dlc) { struct bt_goep_transport_rfcomm_server *rfcomm_server; - struct bt_goep *goep; + struct bt_goep *goep = NULL; int err; rfcomm_server = CONTAINER_OF(server, struct bt_goep_transport_rfcomm_server, rfcomm); @@ -215,10 +215,8 @@ static int goep_rfcomm_accept(struct bt_conn *conn, struct bt_rfcomm_server *ser return err; } - if (goep == NULL || goep->v1 == NULL || goep->v2 != NULL || goep->transport_ops == NULL) { - LOG_DBG("Invalid parameter"); - return -EINVAL; - } + __ASSERT(goep != NULL && goep->v1 != NULL && goep->v2 == NULL && + goep->transport_ops != NULL, "Invalid parameter of GOEP RFCOMM transport"); goep_rfcomm_init(conn, goep); @@ -493,7 +491,7 @@ static int goep_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *serve struct bt_l2cap_chan **chan) { struct bt_goep_transport_l2cap_server *l2cap_server; - struct bt_goep *goep; + struct bt_goep *goep = NULL; int err; l2cap_server = CONTAINER_OF(server, struct bt_goep_transport_l2cap_server, l2cap); @@ -509,10 +507,8 @@ static int goep_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *serve return err; } - if (goep == NULL || goep->v2 == NULL || goep->v1 != NULL || goep->transport_ops == NULL) { - LOG_DBG("Invalid parameter"); - return -EINVAL; - } + __ASSERT(goep != NULL && goep->v2 != NULL && goep->v1 == NULL && + goep->transport_ops != NULL, "Invalid parameter of GOEP L2CAP transport"); goep_l2cap_init(conn, goep); From 4121776b92ea7fba89b78f39a82bc8dc15ca21ca Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Mon, 20 Jul 2026 16:23:43 +0800 Subject: [PATCH 109/600] bluetooth: classic: pbap: use assertions in accept callbacks Replace programming errors with runtime assertions in `pbap_pse_rfcomm_accept()` and `pbap_pse_l2cap_accept()` functions. The pbap_pse instance validation is converted to `__ASSERT()` calls since a NULL instance indicates a programming error in the upper layer's accept callback implementation. Initialize pbap_pse to NULL to ensure the assertion can properly detect uninitialized instances passed from the accept callbacks. The change aligns with the error handling approach used in the GOEP layer's accept callbacks, treating invalid instance parameters as programming errors that should be caught during development. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/pbap.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/host/classic/pbap.c b/subsys/bluetooth/host/classic/pbap.c index 5bc39d6c1764..0625b6398a74 100644 --- a/subsys/bluetooth/host/classic/pbap.c +++ b/subsys/bluetooth/host/classic/pbap.c @@ -1233,7 +1233,7 @@ static int pbap_pse_rfcomm_accept(struct bt_conn *conn, struct bt_goep **goep) { struct bt_pbap_pse_rfcomm *pbap_pse_rfcomm; - struct bt_pbap_pse *pbap_pse; + struct bt_pbap_pse *pbap_pse = NULL; int err; pbap_pse_rfcomm = CONTAINER_OF(server, struct bt_pbap_pse_rfcomm, server); @@ -1246,10 +1246,7 @@ static int pbap_pse_rfcomm_accept(struct bt_conn *conn, return err; } - if (pbap_pse == NULL) { - LOG_WRN("Invalid parameter"); - return -EINVAL; - } + __ASSERT(pbap_pse != NULL, "Invalid pbap pse instance"); pbap_pse->_goep.transport_ops = &pse_rfcomm_transport_ops; BT_GOEP_INIT_V1(&pbap_pse->_goep, &pbap_pse->_goep_transport.v1); @@ -1265,7 +1262,7 @@ static int pbap_pse_l2cap_accept(struct bt_conn *conn, struct bt_goep **goep) { struct bt_pbap_pse_l2cap *pbap_pse_l2cap; - struct bt_pbap_pse *pbap_pse; + struct bt_pbap_pse *pbap_pse = NULL; int err; pbap_pse_l2cap = CONTAINER_OF(server, struct bt_pbap_pse_l2cap, server); @@ -1278,10 +1275,7 @@ static int pbap_pse_l2cap_accept(struct bt_conn *conn, return err; } - if (pbap_pse == NULL) { - LOG_WRN("Invalid parameter"); - return -EINVAL; - } + __ASSERT(pbap_pse != NULL, "Invalid pbap pse instance"); pbap_pse->_goep.transport_ops = &pse_l2cap_transport_ops; BT_GOEP_INIT_V2(&pbap_pse->_goep, &pbap_pse->_goep_transport.v2); From 701147c38a0249c47e059941e8835af3a4b84b62 Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Mon, 20 Jul 2026 16:30:45 +0800 Subject: [PATCH 110/600] bluetooth: classic: bip: use assertions in accept callbacks Replace programming error checking with runtime assertions in `bip_rfcomm_accept()` and `bip_l2cap_accept()` functions. The bip instance validation is converted to `__ASSERT()` calls since a NULL instance indicates a programming error in the upper layer's accept callback implementation rather than a runtime failure. Initialize bip to NULL to ensure the assertion can properly detect uninitialized instances passed from the accept callbacks. The change aligns with the error handling approach used in the GOEP layers' accept callbacks, treating invalid instance parameters as programming errors that should be caught during development. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/bip.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/host/classic/bip.c b/subsys/bluetooth/host/classic/bip.c index 6c14fbd3bd62..5bea9a07e723 100644 --- a/subsys/bluetooth/host/classic/bip.c +++ b/subsys/bluetooth/host/classic/bip.c @@ -105,7 +105,7 @@ static int bip_rfcomm_accept(struct bt_conn *conn, struct bt_goep_transport_rfco struct bt_goep **goep) { struct bt_bip_rfcomm_server *bip_server = BIP_RFDCOMM_SERVER(server); - struct bt_bip *bip; + struct bt_bip *bip = NULL; int err; if (bip_server->accept == NULL) { @@ -118,10 +118,7 @@ static int bip_rfcomm_accept(struct bt_conn *conn, struct bt_goep_transport_rfco return err; } - if (bip == NULL || bip->ops == NULL) { - LOG_ERR("Invalid bip instance"); - return -EINVAL; - } + __ASSERT(bip != NULL && bip->ops != NULL, "Invalid bip instance"); bip->role = BT_BIP_ROLE_RESPONDER; bip->goep.transport_ops = &bip_rfcomm_ops; @@ -247,7 +244,7 @@ static int bip_l2cap_accept(struct bt_conn *conn, struct bt_goep_transport_l2cap struct bt_goep **goep) { struct bt_bip_l2cap_server *bip_server = BIP_L2CAP_SERVER(server); - struct bt_bip *bip; + struct bt_bip *bip = NULL; int err; if (bip_server->accept == NULL) { @@ -260,10 +257,7 @@ static int bip_l2cap_accept(struct bt_conn *conn, struct bt_goep_transport_l2cap return err; } - if (bip == NULL || bip->ops == NULL) { - LOG_WRN("Invalid parameter"); - return -EINVAL; - } + __ASSERT(bip != NULL && bip->ops != NULL, "Invalid bip instance"); bip->role = BT_BIP_ROLE_RESPONDER; bip->goep.transport_ops = &bip_l2cap_ops; From 3ccfa74317cc8ecf1fcd10a53491a2a4c7555015 Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Mon, 20 Jul 2026 16:47:43 +0800 Subject: [PATCH 111/600] bluetooth: classic: map: use assertions in accept callbacks Replace programming error checking with runtime assertions in `mce_mns_accept()` and `mse_mas_accept()` functions. The instance and callback validation checks are converted to `__ASSERT()` calls since these conditions indicate programming errors rather than runtime failures. Initialize mce_mns and mse_mas to NULL to ensure the assertions can properly detect uninitialized instances passed from the accept callbacks. The change aligns with the error handling approach used in the GOEP layers' accept callbacks, treating invalid instance parameters and initialization failures as programming errors that should be caught during development. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/map.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/host/classic/map.c b/subsys/bluetooth/host/classic/map.c index cff4b5fd60b9..9f3dde48c766 100644 --- a/subsys/bluetooth/host/classic/map.c +++ b/subsys/bluetooth/host/classic/map.c @@ -1317,7 +1317,7 @@ int bt_map_mce_mns_register(struct bt_map_mce_mns *mce_mns, const struct bt_map_ #define MNS_L2CAP_SERVER(server) CONTAINER_OF(server, struct bt_map_mce_mns_l2cap_server, server) static int mce_mns_accept(struct bt_conn *conn, void *server, uint8_t type, struct bt_goep **goep) { - struct bt_map_mce_mns *mce_mns; + struct bt_map_mce_mns *mce_mns = NULL; int err; LOG_DBG("MCE MNS %s accept", type == MAP_TRANSPORT_TYPE_RFCOMM ? "RFCOMM" : "L2CAP"); @@ -1345,10 +1345,7 @@ static int mce_mns_accept(struct bt_conn *conn, void *server, uint8_t type, stru return err; } - if (mce_mns == NULL || mce_mns->_cb == NULL) { - LOG_WRN("Invalid parameter"); - return -EINVAL; - } + __ASSERT(mce_mns != NULL && mce_mns->_cb != NULL, "Invalid mce_mns instance or callback"); mce_mns->goep.transport_ops = &mce_mns_transport_ops; if (type == MAP_TRANSPORT_TYPE_RFCOMM) { @@ -1922,7 +1919,7 @@ int bt_map_mse_mas_register(struct bt_map_mse_mas *mse_mas, const struct bt_map_ #define MAS_L2CAP_SERVER(server) CONTAINER_OF(server, struct bt_map_mse_mas_l2cap_server, server) static int mse_mas_accept(struct bt_conn *conn, void *server, uint8_t type, struct bt_goep **goep) { - struct bt_map_mse_mas *mse_mas; + struct bt_map_mse_mas *mse_mas = NULL; int err; LOG_DBG("MSE MAS %s accept", type == MAP_TRANSPORT_TYPE_RFCOMM ? "RFCOMM" : "L2CAP"); @@ -1950,10 +1947,7 @@ static int mse_mas_accept(struct bt_conn *conn, void *server, uint8_t type, stru return err; } - if (mse_mas == NULL || mse_mas->_cb == NULL) { - LOG_WRN("Invalid parameter"); - return -EINVAL; - } + __ASSERT(mse_mas != NULL && mse_mas->_cb != NULL, "Invalid mse_mas instance or callback"); mse_mas->goep.transport_ops = &mse_mas_transport_ops; if (type == MAP_TRANSPORT_TYPE_RFCOMM) { From 8a46234e74e51ce30b986b4545531cba7f7d12a1 Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 21:56:00 -0300 Subject: [PATCH 112/600] dts: espressif: describe the esp32p4 mipi-dsi peripheral Add bindings for the ESP32-P4 MIPI DSI host and for the DPI scanout controller behind it, and the nodes describing them. The bridge streams a framebuffer to the host DPI port rather than being a peripheral of its own, so it is a child of the host and carries no registers. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- .../display/espressif,esp-dsi-display.yaml | 32 ++++++++++++++++++ .../mipi-dsi/espressif,esp-mipi-dsi.yaml | 33 +++++++++++++++++++ .../espressif/esp32p4/esp32p4_common.dtsi | 27 ++++++++++----- 3 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 dts/bindings/display/espressif,esp-dsi-display.yaml create mode 100644 dts/bindings/mipi-dsi/espressif,esp-mipi-dsi.yaml diff --git a/dts/bindings/display/espressif,esp-dsi-display.yaml b/dts/bindings/display/espressif,esp-dsi-display.yaml new file mode 100644 index 000000000000..33efe2789577 --- /dev/null +++ b/dts/bindings/display/espressif,esp-dsi-display.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + Espressif ESP32 MIPI DSI DPI scanout controller. + + The DSI bridge streams a framebuffer to the DSI host DPI port, so it is + the display controller for a video-mode panel attached to that host. It + owns the framebuffers and is the node "zephyr,display" should point at, + while the panel node only performs its own initialization. + + This is a role of the MIPI DSI peripheral rather than a separate block, + so the node is a child of the DSI host and carries no registers of its + own. + +compatible: "espressif,esp-dsi-display" + +include: [lcd-controller.yaml] + +properties: + interrupts: + required: true + + dma-channel: + type: int + required: true + enum: [0, 1, 2, 3] + description: | + 2D-DMA channel that streams the framebuffer to the DSI bridge. diff --git a/dts/bindings/mipi-dsi/espressif,esp-mipi-dsi.yaml b/dts/bindings/mipi-dsi/espressif,esp-mipi-dsi.yaml new file mode 100644 index 000000000000..fa0c5d4794a9 --- /dev/null +++ b/dts/bindings/mipi-dsi/espressif,esp-mipi-dsi.yaml @@ -0,0 +1,33 @@ +# +# Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: Espressif ESP32 MIPI DSI host controller + +compatible: "espressif,esp-mipi-dsi" + +include: mipi-dsi-host.yaml + +properties: + reg: + required: true + + data-lanes: + type: int + required: true + min: 1 + max: 2 + description: | + Number of MIPI DSI data lanes. The D-PHY is brought up before a panel + attaches, so the count is needed here as well as on the panel node, + and the two have to agree. + + dpi-clock-frequency: + type: int + required: true + description: | + DPI pixel clock frequency in Hz. It is derived from the fixed 240 MHz + source through an integer divider, so a value that does not divide it + evenly is rounded to the nearest achievable rate. diff --git a/dts/riscv/espressif/esp32p4/esp32p4_common.dtsi b/dts/riscv/espressif/esp32p4/esp32p4_common.dtsi index 4c25422a0ce9..45eeeaa7f548 100644 --- a/dts/riscv/espressif/esp32p4/esp32p4_common.dtsi +++ b/dts/riscv/espressif/esp32p4/esp32p4_common.dtsi @@ -653,16 +653,27 @@ status = "okay"; }; - mipi_dsi: mipi-dsi@500a0000 { - compatible = "espressif,esp-mipi-dsi"; - reg = <0x500a0000 0x800 0x500a0800 0x800>; - reg-names = "host", "bridge"; + dsi_display: display-controller@500a0800 { + compatible = "espressif,esp-dsi-display"; + reg = <0x500a0800 0x800>; + interrupts = ; + interrupt-parent = <&intc>; + dma-channel = <0>; #address-cells = <1>; - #size-cells = <0>; - data-lanes = <2>; - lane-bit-rate-mbps = <1000>; - dpi-clock-freq-mhz = <48>; + #size-cells = <1>; status = "disabled"; + + mipi_dsi: mipi-dsi@500a0000 { + compatible = "espressif,esp-mipi-dsi"; + reg = <0x500a0000 0x800 0x500a0800 0x800>; + reg-names = "host", "bridge"; + #address-cells = <1>; + #size-cells = <0>; + data-lanes = <2>; + phy-clock = <1000000000>; + dpi-clock-frequency = <48000000>; + status = "disabled"; + }; }; sdhc: sdhc@50083000 { From 2b71bd5f708767f823a2f98c82e58957019b29ad Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 21:56:13 -0300 Subject: [PATCH 113/600] drivers: display: add the esp32p4 dsi scanout controller Add the display driver for the DSI bridge, which streams a framebuffer to the host DPI port. It owns the framebuffers, presents a completed frame at the next frame boundary so an update is tear-free, and reports frame events. Reading back returns the buffer on screen, and blanking is forwarded to the panel, which owns the backlight. The MIPI DSI host starts and stops it, since the framebuffer size depends on the pixel format the panel reports when it attaches. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- drivers/display/CMakeLists.txt | 1 + drivers/display/Kconfig | 1 + drivers/display/Kconfig.esp32_dsi | 35 + drivers/display/display_esp32_dsi.c | 893 ++++++++++++++++++ .../display/espressif,esp-dsi-display.yaml | 8 +- include/zephyr/drivers/display/esp32_dsi.h | 65 ++ west.yml | 2 +- 7 files changed, 1001 insertions(+), 4 deletions(-) create mode 100644 drivers/display/Kconfig.esp32_dsi create mode 100644 drivers/display/display_esp32_dsi.c create mode 100644 include/zephyr/drivers/display/esp32_dsi.h diff --git a/drivers/display/CMakeLists.txt b/drivers/display/CMakeLists.txt index 7ce259eede22..0c60da3bfd4b 100644 --- a/drivers/display/CMakeLists.txt +++ b/drivers/display/CMakeLists.txt @@ -12,6 +12,7 @@ zephyr_library_sources_ifdef(CONFIG_AC057TC1 display_ac057tc1.c) zephyr_library_sources_ifdef(CONFIG_CO5300 display_co5300.c) zephyr_library_sources_ifdef(CONFIG_DISPLAY_CHARLIEPLEX_LED_MATRIX display_charlieplex_led_matrix.c) zephyr_library_sources_ifdef(CONFIG_DISPLAY_COLOR_DITHER color_dither.c) +zephyr_library_sources_ifdef(CONFIG_DISPLAY_ESP32_DSI display_esp32_dsi.c) zephyr_library_sources_ifdef(CONFIG_DISPLAY_MCUX_DCNANO_LCDIF display_mcux_dcnano_lcdif.c) zephyr_library_sources_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF display_mcux_elcdif.c) zephyr_library_sources_ifdef(CONFIG_DISPLAY_MCUX_LCDIFV2 display_mcux_lcdifv2.c) diff --git a/drivers/display/Kconfig b/drivers/display/Kconfig index 2c097b591daf..6e360bf12a79 100644 --- a/drivers/display/Kconfig +++ b/drivers/display/Kconfig @@ -41,6 +41,7 @@ source "drivers/display/Kconfig.co5300" source "drivers/display/Kconfig.dummy" source "drivers/display/Kconfig.ed2208_gca" source "drivers/display/Kconfig.ek79007" +source "drivers/display/Kconfig.esp32_dsi" source "drivers/display/Kconfig.gc9x01x" source "drivers/display/Kconfig.hub12" source "drivers/display/Kconfig.hx8353e" diff --git a/drivers/display/Kconfig.esp32_dsi b/drivers/display/Kconfig.esp32_dsi new file mode 100644 index 000000000000..a100c907865b --- /dev/null +++ b/drivers/display/Kconfig.esp32_dsi @@ -0,0 +1,35 @@ +# Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +menuconfig DISPLAY_ESP32_DSI + bool "ESP32 MIPI DSI display controller" + default y + depends on DT_HAS_ESPRESSIF_ESP_DSI_DISPLAY_ENABLED + depends on SOC_SERIES_ESP32P4 + select ESP_SPIRAM + select SHARED_MULTI_HEAP + select MIPI_DSI + help + Enable driver for the ESP32 MIPI DSI display controller, which + streams the framebuffer to a video mode panel. The framebuffers are + megabytes in size, so they are allocated from external memory. + +if DISPLAY_ESP32_DSI + +config DISPLAY_ESP32_DSI_FB_NUM + int "Framebuffers to allocate in the driver" + default 2 + range 1 2 + help + Two framebuffers let the driver present a finished frame while the + next one is drawn, so an update is tear-free. + +config DISPLAY_ESP32_DSI_INIT_PRIORITY + int "ESP32 MIPI DSI display controller init priority" + default DISPLAY_INIT_PRIORITY + help + Initialization priority of the controller. The host starts the + scanout once a panel attaches, so this only has to place the + controller before the panel. + +endif # DISPLAY_ESP32_DSI diff --git a/drivers/display/display_esp32_dsi.c b/drivers/display/display_esp32_dsi.c new file mode 100644 index 000000000000..0b46d4f9a9d6 --- /dev/null +++ b/drivers/display/display_esp32_dsi.c @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT espressif_esp_dsi_display + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "display_esp32_dsi.h" + +LOG_MODULE_REGISTER(display_esp32_dsi, CONFIG_DISPLAY_LOG_LEVEL); + +struct display_esp32_dsi_config { + const struct device *panel; + uint8_t dma_channel; + uint8_t irq_source; + uint8_t irq_priority; + uint8_t irq_flags; + uint16_t width; + uint16_t height; + uint8_t pixel_format; +}; + +struct display_esp32_dsi_data { + const struct device *dev; + uint8_t dma_channel; + uint8_t *fb[CONFIG_DISPLAY_ESP32_DSI_FB_NUM]; + uint32_t fb_size; + uint8_t fb_count; + uint8_t bytes_per_pixel; + uint8_t draw_fb; + uint8_t last_fb; + bool have_last_fb; + bool fb_seeded; + bool started; + uint32_t writers; + void *prev_buf; + intr_handle_t dma_intr; + struct k_spinlock lock; + atomic_t dma_err_count; + atomic_t frame_count; + uint8_t active_fb; + int8_t pending_fb; + uint8_t *ext_fb; + struct k_sem frame_sem; + display_event_cb_t event_cb; + void *event_user_data; + uint32_t event_mask; + dw_gdma_link_list_item_t lli[CONFIG_DISPLAY_ESP32_DSI_FB_NUM + 1] + __aligned(DW_GDMA_LL_LINK_LIST_ALIGNMENT); +}; + +static int display_esp32_dsi_writer_enter(struct display_esp32_dsi_data *data) +{ + k_spinlock_key_t key = k_spin_lock(&data->lock); + + if (!data->started) { + k_spin_unlock(&data->lock, key); + return -EAGAIN; + } + + data->writers++; + k_spin_unlock(&data->lock, key); + + return 0; +} + +static void display_esp32_dsi_writer_exit(struct display_esp32_dsi_data *data) +{ + k_spinlock_key_t key = k_spin_lock(&data->lock); + + data->writers--; + k_spin_unlock(&data->lock, key); + + k_sem_give(&data->frame_sem); +} + +static int display_esp32_dsi_flip(struct display_esp32_dsi_data *data, uint32_t index) +{ + k_spinlock_key_t key; + + if (index >= data->fb_count) { + return -EINVAL; + } + + key = k_spin_lock(&data->lock); + data->pending_fb = (int8_t)index; + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int8_t display_esp32_dsi_index_of(struct display_esp32_dsi_data *data, const void *buf) +{ + for (uint8_t i = 0; i < data->fb_count; i++) { + if (buf == data->fb[i]) { + return (int8_t)i; + } + } + + return -1; +} + +static void display_esp32_dsi_present(struct display_esp32_dsi_data *data, int8_t index) +{ + k_spinlock_key_t key = k_spin_lock(&data->lock); + + data->pending_fb = index; + k_spin_unlock(&data->lock, key); +} + +static int display_esp32_dsi_wait_buffer_free(struct display_esp32_dsi_data *data, const void *buf, + k_timeout_t timeout) +{ + int8_t index = -1; + + for (uint8_t i = 0; i < data->fb_count; i++) { + if (buf == data->fb[i]) { + index = (int8_t)i; + break; + } + } + + if (index < 0 && buf == data->ext_fb) { + index = (int8_t)data->fb_count; + } + + if (index < 0) { + return -EINVAL; + } + + k_timepoint_t end = sys_timepoint_calc(timeout); + + while (true) { + k_spinlock_key_t key = k_spin_lock(&data->lock); + bool busy = (index == (int8_t)data->active_fb) || (index == data->pending_fb); + + k_spin_unlock(&data->lock, key); + + if (!busy) { + break; + } + + /* A give from an earlier frame only costs an extra iteration, + * since the locked check above decides when the buffer is free. + */ + if (k_sem_take(&data->frame_sem, sys_timepoint_timeout(end)) != 0 && + sys_timepoint_expired(end)) { + return -EAGAIN; + } + } + + return 0; +} + +static void display_esp32_dsi_dma_isr(void *arg) +{ + struct display_esp32_dsi_data *data = arg; + dw_gdma_dev_t *dma = DW_GDMA_LL_GET_HW(0); + uint8_t ch = data->dma_channel; + + uint32_t status = dw_gdma_ll_channel_get_intr_status(dma, ch); + + dw_gdma_ll_channel_clear_intr(dma, ch, status); + + if (status & DW_GDMA_LL_CHANNEL_EVENT_DMA_TFR_DONE) { + k_spinlock_key_t key = k_spin_lock(&data->lock); + display_event_cb_t cb = data->event_cb; + uint32_t mask = data->event_mask; + void *user_data = data->event_user_data; + int8_t pending = data->pending_fb; + uint8_t shown = data->active_fb; + bool started = data->started; + bool skip_flip = false; + + k_spin_unlock(&data->lock, key); + + if (!started) { + return; + } + + if (cb != NULL) { + struct display_event_data evt = { + .info = {.buffer_id = (int)shown}, + }; + + if (mask & DISPLAY_EVENT_VSYNC) { + (void)cb(data->dev, DISPLAY_EVENT_VSYNC, &evt, user_data); + } + /* Only frame done gates the flip, since presenting the + * pending buffer is what this driver does by default for + * that event. + */ + if (mask & DISPLAY_EVENT_FRAME_DONE) { + skip_flip = cb(data->dev, DISPLAY_EVENT_FRAME_DONE, &evt, + user_data) == DISPLAY_EVENT_RESULT_HANDLED; + } + } + + key = k_spin_lock(&data->lock); + if (!skip_flip && pending >= 0 && data->pending_fb == pending) { + data->active_fb = (uint8_t)pending; + data->pending_fb = -1; + } + shown = data->active_fb; + k_spin_unlock(&data->lock, key); + + dw_gdma_link_list_item_t *lli = &data->lli[shown]; + + dw_gdma_ll_lli_set_block_markers(lli, true, true, true); + sys_cache_data_flush_range(lli, sizeof(*lli)); + dw_gdma_ll_channel_set_link_list_head_addr(dma, ch, (uint32_t)lli); + dw_gdma_ll_channel_enable(dma, ch, true); + + atomic_inc(&data->frame_count); + + k_sem_give(&data->frame_sem); + } + + if (status & DW_GDMA_LL_CHANNEL_EVENT_SHADOWREG_OR_LLI_INVALID_ERR) { + atomic_inc(&data->dma_err_count); + } +} + +static int display_esp32_dsi_dma_setup(const struct device *dev) +{ + const struct display_esp32_dsi_config *config = dev->config; + struct display_esp32_dsi_data *data = dev->data; + dw_gdma_dev_t *dma = DW_GDMA_LL_GET_HW(0); + uint8_t ch = data->dma_channel; + + dw_gdma_ll_enable_bus_clock(0, true); + dw_gdma_ll_reset(dma); + + dw_gdma_ll_enable_controller(dma, true); + dw_gdma_ll_enable_intr_global(dma, true); + + dw_gdma_ll_channel_enable(dma, ch, false); + + dw_gdma_ll_channel_set_trans_flow(dma, ch, DW_GDMA_ROLE_MEM, DW_GDMA_ROLE_PERIPH_DSI, + DW_GDMA_FLOW_CTRL_SELF); + + dw_gdma_ll_channel_set_src_multi_block_type(dma, ch, DW_GDMA_BLOCK_TRANSFER_LIST); + dw_gdma_ll_channel_set_dst_multi_block_type(dma, ch, DW_GDMA_BLOCK_TRANSFER_LIST); + + dw_gdma_ll_channel_set_src_handshake_interface(dma, ch, DW_GDMA_HANDSHAKE_HW); + dw_gdma_ll_channel_set_dst_handshake_interface(dma, ch, DW_GDMA_HANDSHAKE_HW); + dw_gdma_ll_channel_set_dst_handshake_periph(dma, ch, DW_GDMA_ROLE_PERIPH_DSI); + + dw_gdma_ll_channel_set_src_outstanding_limit(dma, ch, 5); + dw_gdma_ll_channel_set_dst_outstanding_limit(dma, ch, 2); + + dw_gdma_ll_channel_set_priority(dma, ch, 1); + + for (uint8_t i = 0; i < data->fb_count; i++) { + dw_gdma_link_list_item_t *lli = &data->lli[i]; + + memset(lli, 0, sizeof(*lli)); + + dw_gdma_ll_lli_set_src_addr(lli, (uint32_t)data->fb[i]); + dw_gdma_ll_lli_set_src_master_port(lli, (intptr_t)data->fb[i]); + dw_gdma_ll_lli_set_src_burst_mode(lli, DW_GDMA_BURST_MODE_INCREMENT); + dw_gdma_ll_lli_set_src_trans_width(lli, DW_GDMA_TRANS_WIDTH_64); + dw_gdma_ll_lli_set_src_burst_items(lli, DW_GDMA_BURST_ITEMS_512); + dw_gdma_ll_lli_set_src_burst_len(lli, 16); + + dw_gdma_ll_lli_set_dst_addr(lli, MIPI_DSI_BRG_MEM_BASE); + dw_gdma_ll_lli_set_dst_master_port(lli, MIPI_DSI_BRG_MEM_BASE); + dw_gdma_ll_lli_set_dst_burst_mode(lli, DW_GDMA_BURST_MODE_FIXED); + dw_gdma_ll_lli_set_dst_trans_width(lli, DW_GDMA_TRANS_WIDTH_64); + dw_gdma_ll_lli_set_dst_burst_items(lli, DW_GDMA_BURST_ITEMS_256); + dw_gdma_ll_lli_set_dst_burst_len(lli, 16); + + dw_gdma_ll_lli_set_trans_block_size(lli, data->fb_size / 8); + + dw_gdma_ll_lli_set_block_markers(lli, true, true, true); + dw_gdma_ll_lli_set_next_item_addr(lli, 0); + dw_gdma_ll_lli_set_link_list_master_port(lli, DW_GDMA_LL_MASTER_PORT_MEMORY); + } + + /* The spare item is re-pointed at a caller-owned frame when one is + * presented, so it starts as a copy of the first framebuffer's item. + */ + memcpy(&data->lli[data->fb_count], &data->lli[0], sizeof(data->lli[0])); + + sys_cache_data_flush_range(data->lli, sizeof(data->lli[0]) * (data->fb_count + 1)); + + data->active_fb = 0; + data->pending_fb = -1; + data->ext_fb = NULL; + + dw_gdma_ll_channel_enable_intr_generation(dma, ch, UINT32_MAX, true); + dw_gdma_ll_channel_enable_intr_propagation( + dma, ch, + DW_GDMA_LL_CHANNEL_EVENT_DMA_TFR_DONE | + DW_GDMA_LL_CHANNEL_EVENT_SHADOWREG_OR_LLI_INVALID_ERR, + true); + + if (data->dma_intr == NULL) { + int err = esp_intr_alloc_intrstatus( + config->irq_source, + ESP_INTR_FLAG_SHARED | ESP_PRIO_TO_FLAGS(config->irq_priority) | + ESP_INT_FLAGS_CHECK(config->irq_flags), + (uint32_t)dw_gdma_ll_get_intr_status_reg(dma), + DW_GDMA_LL_CHANNEL_EVENT_MASK(ch), display_esp32_dsi_dma_isr, data, + &data->dma_intr); + if (err != 0) { + LOG_ERR("Failed to allocate DMA interrupt (%d)", err); + dw_gdma_ll_channel_enable(dma, ch, false); + return -EIO; + } + } + + dw_gdma_ll_channel_set_link_list_head_addr(dma, ch, (uint32_t)&data->lli[data->active_fb]); + dw_gdma_ll_channel_set_link_list_master_port(dma, ch, DW_GDMA_LL_MASTER_PORT_MEMORY); + dw_gdma_ll_channel_enable(dma, ch, true); + + LOG_INF("DMA streaming started: %u fb(s), size=%u", data->fb_count, data->fb_size); + + return 0; +} + +int display_esp32_dsi_start(const struct device *dev, uint32_t bits_per_pixel) +{ + struct display_esp32_dsi_data *data = dev->data; + const struct display_esp32_dsi_config *config = dev->config; + + if (data->started) { + return 0; + } + + /* The panel reports the format it was attached with, while the + * capabilities come from this node. They describe the same pixels, so + * refuse to stream rather than stride the framebuffer one way and + * report it another. + */ + if (bits_per_pixel != DISPLAY_BITS_PER_PIXEL(config->pixel_format)) { + LOG_ERR("Panel uses %u bpp, the controller is configured for %u", bits_per_pixel, + DISPLAY_BITS_PER_PIXEL(config->pixel_format)); + return -EINVAL; + } + + data->bytes_per_pixel = bits_per_pixel / 8; + data->fb_size = config->width * config->height * data->bytes_per_pixel; + data->fb_size = ROUND_UP(data->fb_size, CONFIG_ESP32_CACHE_L2_LINE_SIZE); + data->fb_count = CONFIG_DISPLAY_ESP32_DSI_FB_NUM; + + for (uint8_t i = 0; i < data->fb_count; i++) { + data->fb[i] = shared_multi_heap_aligned_alloc( + SMH_REG_ATTR_EXTERNAL, CONFIG_ESP32_CACHE_L2_LINE_SIZE, data->fb_size); + if (data->fb[i] == NULL) { + LOG_ERR("Failed to allocate framebuffer %u (%u bytes)", i, data->fb_size); + for (uint8_t j = 0; j < i; j++) { + shared_multi_heap_free(data->fb[j]); + data->fb[j] = NULL; + } + data->fb_count = 0; + return -ENOMEM; + } + memset(data->fb[i], 0, data->fb_size); + sys_cache_data_flush_range(data->fb[i], data->fb_size); + } + + int err = display_esp32_dsi_dma_setup(dev); + + if (err != 0) { + for (uint8_t i = 0; i < data->fb_count; i++) { + shared_multi_heap_free(data->fb[i]); + data->fb[i] = NULL; + } + data->fb_count = 0; + return err; + } + + data->draw_fb = (data->fb_count > 1) ? 1 : 0; + data->started = true; + + return 0; +} + +int display_esp32_dsi_stop(const struct device *dev) +{ + struct display_esp32_dsi_data *data = dev->data; + dw_gdma_dev_t *dma = DW_GDMA_LL_GET_HW(0); + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + if (!data->started) { + k_spin_unlock(&data->lock, key); + return 0; + } + + /* Retire the device before the hardware is touched, so a caller that + * has not entered yet is turned away and the handler returns early if + * it runs on the other core. + */ + data->started = false; + data->event_cb = NULL; + data->event_mask = 0; + data->event_user_data = NULL; + k_spin_unlock(&data->lock, key); + + /* Silence the source before the abort. Aborting only guarantees the + * channel is idle, not that a handler already latched on the other + * core has finished re-arming it. + */ + if (data->dma_intr != NULL) { + esp_intr_disable(data->dma_intr); + } + + dw_gdma_ll_channel_enable_intr_propagation( + dma, data->dma_channel, + DW_GDMA_LL_CHANNEL_EVENT_DMA_TFR_DONE | + DW_GDMA_LL_CHANNEL_EVENT_SHADOWREG_OR_LLI_INVALID_ERR, + false); + dw_gdma_ll_channel_abort(dma, data->dma_channel); + + if (data->dma_intr != NULL) { + esp_intr_free(data->dma_intr); + data->dma_intr = NULL; + } + + /* Release a writer blocked on a frame that will never be presented, + * then wait for every one of them to leave before the buffers they + * are still copying into are handed back to the heap. + */ + k_sem_give(&data->frame_sem); + + while (true) { + key = k_spin_lock(&data->lock); + bool busy = data->writers > 0; + + k_spin_unlock(&data->lock, key); + + if (!busy) { + break; + } + + k_sleep(K_MSEC(1)); + } + + for (uint8_t i = 0; i < data->fb_count; i++) { + if (data->fb[i] != NULL) { + shared_multi_heap_free(data->fb[i]); + data->fb[i] = NULL; + } + } + + data->fb_count = 0; + data->have_last_fb = false; + data->fb_seeded = false; + data->prev_buf = NULL; + + return 0; +} + +static int display_esp32_dsi_write_locked(const struct device *dev, const uint16_t x, + const uint16_t y, + const struct display_buffer_descriptor *desc, + const void *buf) +{ + const struct display_esp32_dsi_config *config = dev->config; + struct display_esp32_dsi_data *data = dev->data; + uint16_t write_w = desc->width; + uint16_t write_h = desc->height; + uint8_t bpp = data->bytes_per_pixel; + uint32_t src_pitch = desc->pitch * bpp; + uint32_t dst_pitch = config->width * bpp; + bool multi = data->fb_count > 1; + + if (buf == NULL) { + return -EINVAL; + } + + if ((uint32_t)x + write_w > config->width || (uint32_t)y + write_h > config->height) { + LOG_ERR("Write %ux%u at (%u,%u) exceeds %ux%u panel", write_w, write_h, x, y, + config->width, config->height); + return -EINVAL; + } + + if (desc->pitch < write_w) { + LOG_ERR("Pitch %u is smaller than the width %u", desc->pitch, write_w); + return -EINVAL; + } + + if ((size_t)src_pitch * write_h > desc->buf_size) { + LOG_ERR("Buffer of %u bytes is too small for %ux%u", desc->buf_size, write_w, + write_h); + return -EINVAL; + } + + if (x == 0 && y == 0 && write_w == config->width && write_h == config->height && + desc->pitch == config->width) { + int8_t index = display_esp32_dsi_index_of(data, buf); + + /* A caller that renders into its own full-size frame can have it + * scanned out directly, by pointing the spare link list item at + * it instead of copying it into a driver framebuffer. + */ + if (index < 0 && desc->buf_size >= data->fb_size && + ((uintptr_t)buf % CONFIG_ESP32_CACHE_L2_LINE_SIZE) == 0) { + dw_gdma_link_list_item_t *lli = &data->lli[data->fb_count]; + + sys_cache_data_flush_range((void *)buf, data->fb_size); + + dw_gdma_ll_lli_set_src_addr(lli, (uint32_t)buf); + dw_gdma_ll_lli_set_src_master_port(lli, (intptr_t)buf); + sys_cache_data_flush_range(lli, sizeof(*lli)); + + data->ext_fb = (uint8_t *)buf; + index = (int8_t)data->fb_count; + } + + if (index >= 0) { + /* Write the buffer back before it is published, so the + * frame the DMA picks up is never the stale one still + * sitting in the cache. + */ + sys_cache_data_flush_range((void *)buf, data->fb_size); + display_esp32_dsi_present(data, index); + + if (multi && data->prev_buf != NULL && data->prev_buf != buf) { + int err = display_esp32_dsi_wait_buffer_free(data, data->prev_buf, + K_MSEC(100)); + + if (err != 0) { + LOG_DBG("Timed out waiting for buffer %p", data->prev_buf); + return err; + } + } + data->prev_buf = (void *)buf; + + return 0; + } + } + + uint8_t *fb = data->fb[data->draw_fb]; + + if (fb == NULL) { + return -EAGAIN; + } + + if (multi && !data->fb_seeded) { + if (data->have_last_fb && data->last_fb != data->draw_fb) { + memcpy(fb, data->fb[data->last_fb], data->fb_size); + sys_cache_data_flush_range(fb, data->fb_size); + } + data->fb_seeded = true; + } + + for (uint16_t row = 0; row < write_h; row++) { + uint32_t dst_offset = (y + row) * dst_pitch + (uint32_t)x * bpp; + uint32_t src_offset = row * src_pitch; + + memcpy(fb + dst_offset, (const uint8_t *)buf + src_offset, (size_t)write_w * bpp); + } + + sys_cache_data_flush_range(fb + (uint32_t)y * dst_pitch, (size_t)write_h * dst_pitch); + + if (multi && !desc->frame_incomplete) { + display_esp32_dsi_flip(data, data->draw_fb); + data->last_fb = data->draw_fb; + data->have_last_fb = true; + data->draw_fb = (data->draw_fb + 1) % data->fb_count; + data->fb_seeded = false; + } + + return 0; +} + +static int display_esp32_dsi_write(const struct device *dev, const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, const void *buf) +{ + struct display_esp32_dsi_data *data = dev->data; + int err = display_esp32_dsi_writer_enter(data); + + if (err != 0) { + return err; + } + + err = display_esp32_dsi_write_locked(dev, x, y, desc, buf); + + display_esp32_dsi_writer_exit(data); + + return err; +} + +static int display_esp32_dsi_read_locked(const struct device *dev, const uint16_t x, + const uint16_t y, + const struct display_buffer_descriptor *desc, void *buf) +{ + const struct display_esp32_dsi_config *config = dev->config; + struct display_esp32_dsi_data *data = dev->data; + uint8_t bpp = data->bytes_per_pixel; + uint32_t src_pitch = config->width * bpp; + uint32_t dst_pitch = desc->pitch * bpp; + k_spinlock_key_t key; + const uint8_t *fb; + + if (buf == NULL) { + return -EINVAL; + } + + if ((uint32_t)x + desc->width > config->width || + (uint32_t)y + desc->height > config->height) { + LOG_ERR("Read %ux%u at (%u,%u) exceeds %ux%u panel", desc->width, desc->height, x, + y, config->width, config->height); + return -EINVAL; + } + + if (desc->pitch < desc->width) { + LOG_ERR("Pitch %u is smaller than the width %u", desc->pitch, desc->width); + return -EINVAL; + } + + if ((size_t)dst_pitch * desc->height > desc->buf_size) { + LOG_ERR("Buffer of %u bytes is too small for %ux%u", desc->buf_size, desc->width, + desc->height); + return -EINVAL; + } + + /* Read back what is on screen rather than the buffer being drawn. The + * frame on screen may be a caller-owned one, which lives outside the + * driver's framebuffer array. + */ + key = k_spin_lock(&data->lock); + fb = (data->active_fb < data->fb_count) ? data->fb[data->active_fb] : data->ext_fb; + k_spin_unlock(&data->lock, key); + + if (fb == NULL) { + return -EAGAIN; + } + + for (uint16_t row = 0; row < desc->height; row++) { + memcpy((uint8_t *)buf + (size_t)row * dst_pitch, + fb + (size_t)(y + row) * src_pitch + (size_t)x * bpp, + (size_t)desc->width * bpp); + } + + return 0; +} + +static int display_esp32_dsi_read(const struct device *dev, const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, void *buf) +{ + struct display_esp32_dsi_data *data = dev->data; + int err = display_esp32_dsi_writer_enter(data); + + if (err != 0) { + return err; + } + + err = display_esp32_dsi_read_locked(dev, x, y, desc, buf); + + display_esp32_dsi_writer_exit(data); + + return err; +} + +static int display_esp32_dsi_blanking_on(const struct device *dev) +{ + const struct display_esp32_dsi_config *config = dev->config; + + if (config->panel == NULL) { + return -ENOSYS; + } + + if (!device_is_ready(config->panel)) { + return -ENODEV; + } + + return display_blanking_on(config->panel); +} + +static int display_esp32_dsi_blanking_off(const struct device *dev) +{ + const struct display_esp32_dsi_config *config = dev->config; + + if (config->panel == NULL) { + return -ENOSYS; + } + + if (!device_is_ready(config->panel)) { + return -ENODEV; + } + + return display_blanking_off(config->panel); +} + +static void *display_esp32_dsi_get_framebuffer(const struct device *dev) +{ + struct display_esp32_dsi_data *data = dev->data; + k_spinlock_key_t key = k_spin_lock(&data->lock); + void *fb = data->started ? data->fb[data->draw_fb] : NULL; + + k_spin_unlock(&data->lock, key); + + return fb; +} + +void *display_esp32_dsi_get_framebuffer_by_index(const struct device *dev, uint32_t index) +{ + struct display_esp32_dsi_data *data = dev->data; + k_spinlock_key_t key = k_spin_lock(&data->lock); + void *fb = (index < data->fb_count) ? data->fb[index] : NULL; + + k_spin_unlock(&data->lock, key); + + return fb; +} + +static void display_esp32_dsi_get_capabilities(const struct device *dev, + struct display_capabilities *capabilities) +{ + const struct display_esp32_dsi_config *config = dev->config; + + memset(capabilities, 0, sizeof(struct display_capabilities)); + capabilities->x_resolution = config->width; + capabilities->y_resolution = config->height; + capabilities->supported_pixel_formats = config->pixel_format; + capabilities->current_pixel_format = config->pixel_format; + capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; +} + +static int display_esp32_dsi_set_pixel_format(const struct device *dev, + const enum display_pixel_format pixel_format) +{ + const struct display_esp32_dsi_config *config = dev->config; + + if (pixel_format == config->pixel_format) { + return 0; + } + + return -ENOTSUP; +} + +static int display_esp32_dsi_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + const struct display_esp32_dsi_config *config = dev->config; + + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + return 0; + } + + /* The scanout streams the framebuffer as it is laid out, so a rotation + * can only be done by a panel that supports one itself. + */ + if (config->panel == NULL) { + return -ENOTSUP; + } + + if (!device_is_ready(config->panel)) { + return -ENODEV; + } + + return display_set_orientation(config->panel, orientation); +} + +static int display_esp32_dsi_register_event_cb(const struct device *dev, display_event_cb_t cb, + void *user_data, uint32_t event_mask, bool in_isr, + uint32_t *out_reg_handle) +{ + struct display_esp32_dsi_data *data = dev->data; + k_spinlock_key_t key; + + if (out_reg_handle == NULL) { + return -EINVAL; + } + if (!in_isr) { + return -ENOTSUP; + } + if (event_mask & ~(DISPLAY_EVENT_VSYNC | DISPLAY_EVENT_FRAME_DONE)) { + return -ENOTSUP; + } + + key = k_spin_lock(&data->lock); + if (data->event_cb != NULL) { + k_spin_unlock(&data->lock, key); + return -EBUSY; + } + data->event_user_data = user_data; + data->event_mask = event_mask; + data->event_cb = cb; + k_spin_unlock(&data->lock, key); + + *out_reg_handle = 1; + + return 0; +} + +static int display_esp32_dsi_unregister_event_cb(const struct device *dev, uint32_t reg_handle) +{ + struct display_esp32_dsi_data *data = dev->data; + k_spinlock_key_t key; + + if (reg_handle != 1) { + return -EINVAL; + } + + key = k_spin_lock(&data->lock); + if (data->event_cb == NULL) { + k_spin_unlock(&data->lock, key); + return -EPERM; + } + data->event_cb = NULL; + data->event_mask = 0; + data->event_user_data = NULL; + k_spin_unlock(&data->lock, key); + + return 0; +} + +static DEVICE_API(display, display_esp32_dsi_api) = { + .blanking_on = display_esp32_dsi_blanking_on, + .blanking_off = display_esp32_dsi_blanking_off, + .write = display_esp32_dsi_write, + .read = display_esp32_dsi_read, + .get_framebuffer = display_esp32_dsi_get_framebuffer, + .get_capabilities = display_esp32_dsi_get_capabilities, + .set_pixel_format = display_esp32_dsi_set_pixel_format, + .set_orientation = display_esp32_dsi_set_orientation, + .register_event_cb = display_esp32_dsi_register_event_cb, + .unregister_event_cb = display_esp32_dsi_unregister_event_cb, +}; + +static int display_esp32_dsi_init(const struct device *dev) +{ + const struct display_esp32_dsi_config *config = dev->config; + struct display_esp32_dsi_data *data = dev->data; + + data->dev = dev; + data->dma_channel = config->dma_channel; + data->pending_fb = -1; + k_sem_init(&data->frame_sem, 0, K_SEM_MAX_LIMIT); + + return 0; +} + +/* The panel sits on the DSI host this controller owns. It is resolved here + * only so blanking can be forwarded to it; the panel still initializes after + * this controller, which starts the scanout. + */ +#define DISPLAY_ESP32_DSI_PANEL_OF(node) DEVICE_DT_GET(node), + +/* The panels hang off the DSI host, which is this controller's child, so they + * are reached one level below rather than as siblings. The host is matched by + * compatible so the lookup does not depend on a board node label. + */ +#define DISPLAY_ESP32_DSI_HOST_PANELS(node, fn) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(node, espressif_esp_mipi_dsi), \ + (DT_FOREACH_CHILD_STATUS_OKAY(node, fn))) + +#define DISPLAY_ESP32_DSI_PANEL(inst) \ + DT_FOREACH_CHILD_STATUS_OKAY_VARGS(DT_DRV_INST(inst), DISPLAY_ESP32_DSI_HOST_PANELS, \ + DISPLAY_ESP32_DSI_PANEL_OF) + +#define DISPLAY_ESP32_DSI_DEVICE(inst) \ + static struct display_esp32_dsi_data display_esp32_dsi_data_##inst; \ + static const struct device *const display_esp32_dsi_panels_##inst[] = { \ + DISPLAY_ESP32_DSI_PANEL(inst)}; \ + static const struct display_esp32_dsi_config display_esp32_dsi_config_##inst = { \ + .dma_channel = DT_INST_PROP(inst, dma_channel), \ + .irq_source = DT_INST_IRQ_BY_IDX(inst, 0, irq), \ + .irq_priority = DT_INST_IRQ_BY_IDX(inst, 0, priority), \ + .irq_flags = DT_INST_IRQ_BY_IDX(inst, 0, flags), \ + .panel = ARRAY_SIZE(display_esp32_dsi_panels_##inst) \ + ? display_esp32_dsi_panels_##inst[0] \ + : NULL, \ + .width = DT_INST_PROP(inst, width), \ + .height = DT_INST_PROP(inst, height), \ + .pixel_format = DT_INST_PROP(inst, pixel_format), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, display_esp32_dsi_init, NULL, &display_esp32_dsi_data_##inst, \ + &display_esp32_dsi_config_##inst, POST_KERNEL, \ + CONFIG_DISPLAY_ESP32_DSI_INIT_PRIORITY, &display_esp32_dsi_api); + +DT_INST_FOREACH_STATUS_OKAY(DISPLAY_ESP32_DSI_DEVICE) diff --git a/dts/bindings/display/espressif,esp-dsi-display.yaml b/dts/bindings/display/espressif,esp-dsi-display.yaml index 33efe2789577..5ca171fdf76f 100644 --- a/dts/bindings/display/espressif,esp-dsi-display.yaml +++ b/dts/bindings/display/espressif,esp-dsi-display.yaml @@ -10,11 +10,12 @@ description: | The DSI bridge streams a framebuffer to the DSI host DPI port, so it is the display controller for a video-mode panel attached to that host. It owns the framebuffers and is the node "zephyr,display" should point at, - while the panel node only performs its own initialization. + while the panel controller only performs its own initialization. This is a role of the MIPI DSI peripheral rather than a separate block, so the node is a child of the DSI host and carries no registers of its - own. + own. It is therefore the one child of that bus without a unit address, + which keeps the addressed range free for the panels attached to the bus. compatible: "espressif,esp-dsi-display" @@ -27,6 +28,7 @@ properties: dma-channel: type: int required: true - enum: [0, 1, 2, 3] + min: 0 + max: 3 description: | 2D-DMA channel that streams the framebuffer to the DSI bridge. diff --git a/include/zephyr/drivers/display/esp32_dsi.h b/include/zephyr/drivers/display/esp32_dsi.h new file mode 100644 index 000000000000..34180d6c1ca5 --- /dev/null +++ b/include/zephyr/drivers/display/esp32_dsi.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Internal interface between the ESP32 MIPI DSI host and its scanout + * controller driver. + */ + +#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ESP32_DSI_H_ +#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ESP32_DSI_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Start streaming the framebuffer to the DSI bridge. + * + * Called by the MIPI DSI host once a panel has attached, since the + * framebuffer size depends on the pixel format the panel reports. + * + * @param dev DSI scanout controller device. + * @param bits_per_pixel Bits per pixel the panel attached with. + * + * @retval 0 on success. + * @retval -EINVAL if the panel format does not match the controller. + * @retval -ENOMEM if a framebuffer could not be allocated. + * @retval -EIO if the scanout DMA could not be set up. + */ +int display_esp32_dsi_start(const struct device *dev, uint32_t bits_per_pixel); + +/** + * @brief Stop the scanout and release the framebuffers. + * + * @param dev DSI scanout controller device. + * + * @retval 0 on success. + */ +int display_esp32_dsi_stop(const struct device *dev); + +/** + * @brief Address a fixed framebuffer by index. + * + * Unlike display_get_framebuffer(), which follows the buffer being drawn + * into, this addresses one specific buffer. A renderer that draws one frame + * while another is on screen needs the pair to stay put. + * + * @param dev DSI scanout controller device. + * @param index Framebuffer index. + * + * @retval Pointer to the framebuffer, or NULL if the index is out of range. + */ +void *display_esp32_dsi_get_framebuffer_by_index(const struct device *dev, uint32_t index); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ESP32_DSI_H_ */ diff --git a/west.yml b/west.yml index a09fdd394cca..7298a5a3c0d7 100644 --- a/west.yml +++ b/west.yml @@ -172,7 +172,7 @@ manifest: groups: - hal - name: hal_espressif - revision: ff69c3aa59685598bcfea5e8e08ba34a5f3a4de2 + revision: 5e3b702250c3e05de5a4cb3a53f74fc3cd8094cf path: modules/hal/espressif west-commands: west/west-commands.yml groups: From 03b5cf71a64ceac4d4cd216e0e38deb31734ca70 Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 21:56:22 -0300 Subject: [PATCH 114/600] drivers: mipi_dsi: add the esp32p4 mipi-dsi host Add the ESP32-P4 MIPI DSI host, which brings up the D-PHY, sends DCS and generic packets, and programs the DPI timings a panel reports when it attaches. Attaching also starts the scanout controller described under the host, once the pixel format is known, and detaching stops it. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- drivers/mipi_dsi/CMakeLists.txt | 1 + drivers/mipi_dsi/Kconfig | 1 + drivers/mipi_dsi/Kconfig.esp32 | 11 + drivers/mipi_dsi/dsi_esp32.c | 416 ++++++++++++++++++++++++++++++++ 4 files changed, 429 insertions(+) create mode 100644 drivers/mipi_dsi/Kconfig.esp32 create mode 100644 drivers/mipi_dsi/dsi_esp32.c diff --git a/drivers/mipi_dsi/CMakeLists.txt b/drivers/mipi_dsi/CMakeLists.txt index a513726ca6f9..a993adced097 100644 --- a/drivers/mipi_dsi/CMakeLists.txt +++ b/drivers/mipi_dsi/CMakeLists.txt @@ -1,6 +1,7 @@ zephyr_sources_ifdef(CONFIG_MIPI_DSI mipi_dsi.c) # zephyr-keep-sorted-start +zephyr_sources_ifdef(CONFIG_MIPI_DSI_ESP32 dsi_esp32.c) zephyr_sources_ifdef(CONFIG_MIPI_DSI_MCUX dsi_mcux.c) zephyr_sources_ifdef(CONFIG_MIPI_DSI_MCUX_2L dsi_mcux_2l.c) zephyr_sources_ifdef(CONFIG_MIPI_DSI_NXP_DWC dsi_nxp_dwc.c) diff --git a/drivers/mipi_dsi/Kconfig b/drivers/mipi_dsi/Kconfig index 3034fa660904..912e89cb0a7c 100644 --- a/drivers/mipi_dsi/Kconfig +++ b/drivers/mipi_dsi/Kconfig @@ -22,6 +22,7 @@ config MIPI_DSI_INIT_PRIORITY MIPI-DSI Host Controllers initialization priority. # zephyr-keep-sorted-start +source "drivers/mipi_dsi/Kconfig.esp32" source "drivers/mipi_dsi/Kconfig.mcux" source "drivers/mipi_dsi/Kconfig.renesas_ra" source "drivers/mipi_dsi/Kconfig.stm32" diff --git a/drivers/mipi_dsi/Kconfig.esp32 b/drivers/mipi_dsi/Kconfig.esp32 new file mode 100644 index 000000000000..51c58c658cfb --- /dev/null +++ b/drivers/mipi_dsi/Kconfig.esp32 @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +config MIPI_DSI_ESP32 + bool "ESP32 MIPI DSI host controller" + default y + depends on DT_HAS_ESPRESSIF_ESP_MIPI_DSI_ENABLED + depends on SOC_SERIES_ESP32P4 + depends on DISPLAY_ESP32_DSI + help + Enable driver for the ESP32 MIPI DSI host controller. diff --git a/drivers/mipi_dsi/dsi_esp32.c b/drivers/mipi_dsi/dsi_esp32.c new file mode 100644 index 000000000000..22e6bbd28cdc --- /dev/null +++ b/drivers/mipi_dsi/dsi_esp32.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT espressif_esp_mipi_dsi + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../display/display_esp32_dsi.h" + +LOG_MODULE_REGISTER(dsi_esp32, CONFIG_MIPI_DSI_LOG_LEVEL); + +#define MIPI_DSI_DEFAULT_TIMEOUT_CLK_FREQ_MHZ 10 +#define MIPI_DSI_DEFAULT_ESCAPE_CLK_FREQ_MHZ 18 +#define MIPI_DSI_PHY_PLL_REF_CLK_FREQ_HZ 40000000 + +/* The PHY PLL only has settings for this range, and a rate outside it is + * programmed with the wrong one rather than being rejected. + */ +#define MIPI_DSI_LANE_BIT_RATE_MIN_MBPS 80 +#define MIPI_DSI_LANE_BIT_RATE_MAX_MBPS 1500 + +/* Both are polled in 100 us steps. */ +#define MIPI_DSI_PLL_LOCK_TIMEOUT_US 10000 +#define MIPI_DSI_LANE_STOP_TIMEOUT_US 10000 +#define MIPI_DSI_POLL_INTERVAL_US 100 + +struct mipi_dsi_esp32_config { + uintptr_t host_reg; + uintptr_t bridge_reg; + uint8_t bus_id; + uint8_t num_data_lanes; + uint32_t lane_bit_rate_mbps; + uint32_t dpi_clock_freq_hz; + const struct device *display; +}; + +struct mipi_dsi_esp32_data { + const struct device *dev; + mipi_dsi_hal_context_t hal; +}; + +/* The panel is configured over commands before video starts, so the command + * speed is set to LP first and revisited once a panel attaches and reports + * whether it wants low power or high speed transfers. + */ +static void mipi_dsi_esp32_set_command_speed(mipi_dsi_hal_context_t *hal, bool low_power) +{ + mipi_dsi_ll_trans_speed_mode_t speed = + low_power ? MIPI_DSI_LL_TRANS_SPEED_LP : MIPI_DSI_LL_TRANS_SPEED_HS; + + for (uint8_t vcid = 0; vcid < 3; vcid++) { + mipi_dsi_host_ll_set_gen_short_wr_speed_mode(hal->host, vcid, speed); + mipi_dsi_host_ll_set_gen_short_rd_speed_mode(hal->host, vcid, speed); + } + + mipi_dsi_host_ll_set_gen_long_wr_speed_mode(hal->host, speed); + mipi_dsi_host_ll_set_dcs_short_wr_speed_mode(hal->host, 0, speed); + mipi_dsi_host_ll_set_dcs_short_wr_speed_mode(hal->host, 1, speed); + mipi_dsi_host_ll_set_dcs_long_wr_speed_mode(hal->host, speed); + mipi_dsi_host_ll_set_dcs_short_rd_speed_mode(hal->host, 0, speed); + mipi_dsi_host_ll_set_mrps_speed_mode(hal->host, speed); +} + +static int mipi_dsi_esp32_attach(const struct device *dev, uint8_t channel, + const struct mipi_dsi_device *mdev) +{ + struct mipi_dsi_esp32_data *data = dev->data; + const struct mipi_dsi_esp32_config *config = dev->config; + mipi_dsi_hal_context_t *hal = &data->hal; + lcd_color_format_t color_fmt; + uint32_t bits_per_pixel; + + switch (mdev->pixfmt) { + case MIPI_DSI_PIXFMT_RGB888: + color_fmt = LCD_COLOR_FMT_RGB888; + bits_per_pixel = 24; + break; + case MIPI_DSI_PIXFMT_RGB565: + color_fmt = LCD_COLOR_FMT_RGB565; + bits_per_pixel = 16; + break; + default: + LOG_ERR("Unsupported pixel format %u", mdev->pixfmt); + return -ENOTSUP; + } + + if (!(mdev->mode_flags & MIPI_DSI_MODE_VIDEO)) { + LOG_ERR("Only video mode is supported"); + return -ENOTSUP; + } + + /* The D-PHY was brought up from the host node's lane count before any + * panel existed, so a panel asking for a different one would be driven + * over lanes that were never configured for it. + */ + if (mdev->data_lanes != config->num_data_lanes) { + LOG_ERR("Panel requests %u lanes, the host is configured for %u", mdev->data_lanes, + config->num_data_lanes); + return -EINVAL; + } + + if (!device_is_ready(config->display)) { + LOG_ERR("DSI display controller not ready"); + return -ENODEV; + } + + bool low_power_cmds = (mdev->mode_flags & MIPI_DSI_MODE_LPM) != 0; + + mipi_dsi_esp32_set_command_speed(hal, low_power_cmds); + + mipi_dsi_host_ll_dpi_set_vcid(hal->host, channel); + mipi_dsi_host_ll_dpi_set_color_coding(hal->host, color_fmt, 0); + mipi_dsi_host_ll_dpi_set_timing_polarity(hal->host, false, false, false, false, false); + + mipi_dsi_host_ll_dpi_set_pattern_type(hal->host, MIPI_DSI_PATTERN_NONE); + + mipi_dsi_host_ll_dpi_enable_lp_horizontal_timing(hal->host, true, true); + mipi_dsi_host_ll_dpi_enable_lp_vertical_timing(hal->host, true, true, true, true); + mipi_dsi_host_ll_dpi_enable_lp_command(hal->host, low_power_cmds); + mipi_dsi_host_ll_dpi_enable_frame_ack(hal->host, true); + + mipi_dsi_host_ll_dpi_set_video_burst_type(hal->host, + MIPI_DSI_LL_VIDEO_BURST_WITH_SYNC_PULSES); + + mipi_dsi_host_ll_dpi_set_video_packet_pixel_num(hal->host, mdev->timings.hactive); + mipi_dsi_host_ll_dpi_set_trunks_num(hal->host, 0); + mipi_dsi_host_ll_dpi_set_null_packet_size(hal->host, 0); + + float dpi_clk_src_freq_mhz = 240.0f; + uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider( + hal, dpi_clk_src_freq_mhz, (float)config->dpi_clock_freq_hz / 1000000.0f); + + mipi_dsi_ll_set_dpi_clock_source(0, MIPI_DSI_DPI_CLK_SRC_PLL_F240M); + mipi_dsi_ll_set_dpi_clock_div(0, dpi_div); + mipi_dsi_ll_enable_dpi_clock(0, true); + + mipi_dsi_hal_host_dpi_set_horizontal_timing(hal, mdev->timings.hsync, mdev->timings.hbp, + mdev->timings.hactive, mdev->timings.hfp); + mipi_dsi_hal_host_dpi_set_vertical_timing(hal, mdev->timings.vsync, mdev->timings.vbp, + mdev->timings.vactive, mdev->timings.vfp); + + mipi_dsi_brg_ll_force_enable_reg_clock(hal->bridge, true); + + mipi_dsi_brg_ll_set_num_pixel_bits( + hal->bridge, mdev->timings.hactive * mdev->timings.vactive * bits_per_pixel); + mipi_dsi_brg_ll_set_underrun_discard_count(hal->bridge, mdev->timings.hactive); + mipi_dsi_brg_ll_set_input_color_format(hal->bridge, color_fmt); + mipi_dsi_brg_ll_set_output_color_format(hal->bridge, color_fmt, 0); + + mipi_dsi_brg_ll_set_flow_controller(hal->bridge, MIPI_DSI_LL_FLOW_CONTROLLER_DMA); + mipi_dsi_brg_ll_set_multi_block_number(hal->bridge, 1); + mipi_dsi_brg_ll_set_burst_len(hal->bridge, 256); + mipi_dsi_brg_ll_set_empty_threshold(hal->bridge, 1024 - 256); + + mipi_dsi_brg_ll_enable(hal->bridge, true); + mipi_dsi_brg_ll_update_dpi_config(hal->bridge); + + int err = display_esp32_dsi_start(config->display, bits_per_pixel); + + if (err != 0) { + mipi_dsi_brg_ll_enable(hal->bridge, false); + mipi_dsi_brg_ll_force_enable_reg_clock(hal->bridge, false); + mipi_dsi_ll_enable_dpi_clock(0, false); + return err; + } + + mipi_dsi_host_ll_enable_video_mode(hal->host, true); + + mipi_dsi_host_ll_set_clock_lane_state(hal->host, MIPI_DSI_LL_CLOCK_LANE_STATE_AUTO); + + mipi_dsi_brg_ll_enable_dpi_output(hal->bridge, true); + mipi_dsi_brg_ll_update_dpi_config(hal->bridge); + + LOG_INF("MIPI DSI attached: %ux%u, %u bpp, channel %u", mdev->timings.hactive, + mdev->timings.vactive, bits_per_pixel, channel); + + return 0; +} + +static ssize_t mipi_dsi_esp32_transfer(const struct device *dev, uint8_t channel, + struct mipi_dsi_msg *msg) +{ + struct mipi_dsi_esp32_data *data = dev->data; + mipi_dsi_hal_context_t *hal = &data->hal; + + switch (msg->type) { + case MIPI_DSI_DCS_SHORT_WRITE: + mipi_dsi_hal_host_gen_write_dcs_command(hal, channel, msg->cmd, 1, NULL, 0); + return 0; + + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + mipi_dsi_hal_host_gen_write_dcs_command(hal, channel, msg->cmd, 1, msg->tx_buf, + msg->tx_len); + return msg->tx_len; + + case MIPI_DSI_DCS_LONG_WRITE: + mipi_dsi_hal_host_gen_write_dcs_command(hal, channel, msg->cmd, 1, msg->tx_buf, + msg->tx_len); + return msg->tx_len; + + case MIPI_DSI_DCS_READ: + if (msg->rx_buf == NULL || msg->rx_len == 0) { + return -EINVAL; + } + mipi_dsi_hal_host_gen_read_dcs_command(hal, channel, msg->cmd, 1, msg->rx_buf, + msg->rx_len); + return msg->rx_len; + + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + mipi_dsi_hal_host_gen_write_short_packet(hal, channel, + MIPI_DSI_DT_GENERIC_SHORT_WRITE_0, 0); + return msg->tx_len; + + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + if (msg->tx_buf == NULL || msg->tx_len < 1) { + return -EINVAL; + } + mipi_dsi_hal_host_gen_write_short_packet(hal, channel, + MIPI_DSI_DT_GENERIC_SHORT_WRITE_1, + ((const uint8_t *)msg->tx_buf)[0]); + return msg->tx_len; + + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + if (msg->tx_buf == NULL || msg->tx_len < 2) { + return -EINVAL; + } + mipi_dsi_hal_host_gen_write_short_packet( + hal, channel, MIPI_DSI_DT_GENERIC_SHORT_WRITE_2, + ((const uint8_t *)msg->tx_buf)[0] | + (((const uint8_t *)msg->tx_buf)[1] << 8)); + return msg->tx_len; + + case MIPI_DSI_GENERIC_LONG_WRITE: + mipi_dsi_hal_host_gen_write_long_packet( + hal, channel, MIPI_DSI_DT_GENERIC_LONG_WRITE, msg->tx_buf, msg->tx_len); + return msg->tx_len; + + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + if (msg->rx_buf == NULL || msg->rx_len == 0) { + return -EINVAL; + } + mipi_dsi_hal_host_gen_read_short_packet(hal, channel, + MIPI_DSI_DT_GENERIC_READ_REQUEST_0, 0, + msg->rx_buf, msg->rx_len); + return msg->rx_len; + + /* The parameter bytes would have to reach the wire for the panel to + * answer about the right register, so a read that silently drops them + * is refused rather than answered from the wrong one. + */ + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + LOG_ERR("Parameterized generic reads are not implemented"); + return -ENOTSUP; + + default: + LOG_ERR("Unsupported message type %u", msg->type); + return -ENOTSUP; + } +} + +static int mipi_dsi_esp32_detach(const struct device *dev, uint8_t channel, + const struct mipi_dsi_device *mdev) +{ + struct mipi_dsi_esp32_data *data = dev->data; + const struct mipi_dsi_esp32_config *config = dev->config; + mipi_dsi_hal_context_t *hal = &data->hal; + + mipi_dsi_brg_ll_enable_dpi_output(hal->bridge, false); + mipi_dsi_brg_ll_update_dpi_config(hal->bridge); + + display_esp32_dsi_stop(config->display); + + mipi_dsi_host_ll_enable_video_mode(hal->host, false); + mipi_dsi_brg_ll_enable(hal->bridge, false); + mipi_dsi_ll_enable_dpi_clock(0, false); + + return 0; +} + +static int mipi_dsi_esp32_init(const struct device *dev) +{ + const struct mipi_dsi_esp32_config *config = dev->config; + struct mipi_dsi_esp32_data *data = dev->data; + mipi_dsi_hal_context_t *hal = &data->hal; + + data->dev = dev; + + clk_gate_ll_ref_20m_clk_en(true); + clk_gate_ll_ref_240m_clk_en(true); + + mipi_dsi_ll_enable_bus_clock(0, true); + mipi_dsi_ll_reset_register(0); + + mipi_dsi_ll_set_phy_config_clock_source(0, MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT); + mipi_dsi_ll_enable_phy_config_clock(0, true); + + mipi_dsi_ll_set_phy_pllref_clock_source(0, MIPI_DSI_PHY_PLLREF_CLK_SRC_DEFAULT); + mipi_dsi_ll_set_phy_pll_ref_clock_div(0, 1); + mipi_dsi_ll_enable_phy_pllref_clock(0, true); + + mipi_dsi_hal_config_t hal_config = { + .bus_id = config->bus_id, + .lane_bit_rate_mbps = config->lane_bit_rate_mbps, + .num_data_lanes = config->num_data_lanes, + }; + mipi_dsi_hal_init(hal, &hal_config); + + /* The HAL addresses the peripheral by bus index. Check that it drives + * the registers the devicetree describes. + */ + if ((uintptr_t)hal->host != config->host_reg || + (uintptr_t)hal->bridge != config->bridge_reg) { + LOG_ERR("Devicetree registers do not match the MIPI DSI peripheral"); + return -ENODEV; + } + + mipi_dsi_hal_configure_phy_pll(hal, MIPI_DSI_PHY_PLL_REF_CLK_FREQ_HZ, + (float)config->lane_bit_rate_mbps); + + if (!WAIT_FOR(mipi_dsi_phy_ll_is_pll_locked(hal->host), MIPI_DSI_PLL_LOCK_TIMEOUT_US, + k_busy_wait(MIPI_DSI_POLL_INTERVAL_US))) { + LOG_ERR("PHY PLL lock timeout (phy_status=0x%08x)", hal->host->phy_status.val); + return -ETIMEDOUT; + } + + if (!WAIT_FOR(mipi_dsi_phy_ll_are_lanes_stopped(hal->host, config->num_data_lanes), + MIPI_DSI_LANE_STOP_TIMEOUT_US, k_busy_wait(MIPI_DSI_POLL_INTERVAL_US))) { + LOG_ERR("Lanes stop state timeout"); + return -ETIMEDOUT; + } + + mipi_dsi_host_ll_enable_video_mode(hal->host, false); + mipi_dsi_host_ll_set_clock_lane_state(hal->host, MIPI_DSI_LL_CLOCK_LANE_STATE_LP); + + mipi_dsi_host_ll_enable_cmd_ack(hal->host, true); + mipi_dsi_esp32_set_command_speed(hal, true); + + mipi_dsi_phy_ll_set_switch_time(hal->host, 50, 104, 46, 128); + + mipi_dsi_host_ll_enable_rx_crc(hal->host, true); + mipi_dsi_host_ll_enable_rx_ecc(hal->host, true); + mipi_dsi_host_ll_enable_tx_eotp(hal->host, true, false); + + uint32_t byte_clk_mhz = config->lane_bit_rate_mbps / 8; + + mipi_dsi_host_ll_set_timeout_clock_division( + hal->host, DIV_ROUND_CLOSEST(byte_clk_mhz, MIPI_DSI_DEFAULT_TIMEOUT_CLK_FREQ_MHZ)); + mipi_dsi_host_ll_set_escape_clock_division( + hal->host, DIV_ROUND_CLOSEST(byte_clk_mhz, MIPI_DSI_DEFAULT_ESCAPE_CLK_FREQ_MHZ)); + + mipi_dsi_host_ll_set_timeout_count(hal->host, 0, 0, 0, 0, 0, 0, 0); + mipi_dsi_phy_ll_set_max_read_time(hal->host, 6000); + mipi_dsi_phy_ll_set_stop_wait_time(hal->host, 0x3F); + + mipi_dsi_brg_ll_enable_ref_clock(hal->bridge, true); + + LOG_INF("MIPI DSI initialized: %u lanes, %u Mbps", config->num_data_lanes, + config->lane_bit_rate_mbps); + + return 0; +} + +static DEVICE_API(mipi_dsi, mipi_dsi_esp32_api) = { + .attach = mipi_dsi_esp32_attach, + .transfer = mipi_dsi_esp32_transfer, + .detach = mipi_dsi_esp32_detach, +}; + +/* A panel that asks for a different lane count is refused at attach time, so + * the mismatch is caught here instead, where it is a devicetree constant. A + * panel keeps the count in the first entry of the property, while the host + * node carries it directly. + */ +#define MIPI_DSI_ESP32_ASSERT_LANES(node) \ + IF_ENABLED(DT_NODE_HAS_PROP(node, data_lanes), \ + (BUILD_ASSERT(DT_PROP_BY_IDX(node, data_lanes, 0) == \ + DT_PROP(DT_PARENT(node), data_lanes), \ + "panel data-lanes must match the DSI host data-lanes");)) + +#define MIPI_DSI_ESP32_DEVICE(inst) \ + DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(inst), MIPI_DSI_ESP32_ASSERT_LANES) \ + BUILD_ASSERT(DT_INST_PROP(inst, phy_clock) / 1000000 >= MIPI_DSI_LANE_BIT_RATE_MIN_MBPS && \ + DT_INST_PROP(inst, phy_clock) / 1000000 <= \ + MIPI_DSI_LANE_BIT_RATE_MAX_MBPS, \ + "phy-clock is outside the range the D-PHY PLL can lock to"); \ + static struct mipi_dsi_esp32_data mipi_dsi_esp32_data_##inst; \ + static const struct mipi_dsi_esp32_config mipi_dsi_esp32_config_##inst = { \ + .host_reg = DT_INST_REG_ADDR_BY_NAME(inst, host), \ + .bridge_reg = DT_INST_REG_ADDR_BY_NAME(inst, bridge), \ + .bus_id = inst, \ + .num_data_lanes = DT_INST_PROP(inst, data_lanes), \ + .lane_bit_rate_mbps = DT_INST_PROP(inst, phy_clock) / 1000000, \ + .dpi_clock_freq_hz = DT_INST_PROP(inst, dpi_clock_frequency), \ + .display = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, mipi_dsi_esp32_init, NULL, &mipi_dsi_esp32_data_##inst, \ + &mipi_dsi_esp32_config_##inst, POST_KERNEL, \ + CONFIG_MIPI_DSI_INIT_PRIORITY, &mipi_dsi_esp32_api); + +DT_INST_FOREACH_STATUS_OKAY(MIPI_DSI_ESP32_DEVICE) From fb3c9e9e159964426b16cca97a3570cb5e61ae8d Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 22:38:12 -0300 Subject: [PATCH 115/600] boards: espressif: esp32p4: enable the mipi-dsi display Enable the DSI host and the scanout controller on both ESP32-P4 function EV boards, and describe the panel on the LCD adapter. The D-PHY draws from the internal LDO channel 3, so the boards turn that rail on at 2.5 V, without which the PLL never locks. Panel reset is on GPIO27 and the backlight on GPIO26, the pins both boards route to the LCD adapter. The DPI clock divides a fixed 240 MHz source, so it is set to a rate that divider reaches exactly. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- .../Kconfig.defconfig | 9 ++++ .../esp32p4_function_ev_board_hpcore.dts | 48 +++++++++++++++++++ .../esp32p4_function_ev_board_hpcore.yaml | 3 ++ .../Kconfig.defconfig | 9 ++++ .../esp32p4x_function_ev_board_hpcore.dts | 48 +++++++++++++++++++ .../esp32p4x_function_ev_board_hpcore.yaml | 2 + .../display/display_esp32_dsi.h | 0 7 files changed, 119 insertions(+) rename include/zephyr/drivers/display/esp32_dsi.h => drivers/display/display_esp32_dsi.h (100%) diff --git a/boards/espressif/esp32p4_function_ev_board/Kconfig.defconfig b/boards/espressif/esp32p4_function_ev_board/Kconfig.defconfig index 7ec4b080c9df..12f5fcaec958 100644 --- a/boards/espressif/esp32p4_function_ev_board/Kconfig.defconfig +++ b/boards/espressif/esp32p4_function_ev_board/Kconfig.defconfig @@ -8,4 +8,13 @@ if BOARD_ESP32P4_FUNCTION_EV_BOARD_ESP32P4_HPCORE configdefault ETH_DRIVER default y +# The 1024x600 panel needs 1.76 MB per framebuffer, and the driver allocates +# two of them for tear-free updates, so the stock heap does not fit them. +configdefault ESP_SPIRAM_HEAP_SIZE + default 5242880 if DISPLAY_ESP32_DSI + +# The controller has an interrupt line, so there is no reason to poll it. +configdefault INPUT_GT911_INTERRUPT + default y + endif # BOARD_ESP32P4_FUNCTION_EV_BOARD_ESP32P4_HPCORE diff --git a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts index 7e4a08f46ffc..45cb2185dbae 100644 --- a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts +++ b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts @@ -9,6 +9,8 @@ #include #include "esp32p4_function_ev_board_hpcore-pinctrl.dtsi" #include +#include +#include #include / { @@ -22,6 +24,7 @@ zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; zephyr,bt-hci = &esp_hosted_mcu_hci; + zephyr,display = &dsi_display; }; aliases { @@ -74,6 +77,7 @@ clock-frequency = ; pinctrl-0 = <&i2c0_default>; pinctrl-names = "default"; + }; &spi2 { @@ -119,6 +123,12 @@ regulator-always-on; }; + ldo3@3 { + regulator-init-microvolt = <2500000>; + regulator-boot-on; + regulator-always-on; + }; + ldo4@4 { regulator-init-microvolt = <3300000>; regulator-boot-on; @@ -188,6 +198,44 @@ }; }; +&mipi_dsi { + status = "okay"; + phy-clock = <900000000>; + dpi-clock-frequency = <48000000>; + + ek79007: panel-controller@0 { + compatible = "fitipower,ek79007"; + reg = <0>; + data-lanes = <2>; + pixel-format = ; + width = <1024>; + height = <600>; + reset-gpios = <&gpio0 27 GPIO_ACTIVE_LOW>; + bl-gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>; + + display-timings { + compatible = "zephyr,panel-timing"; + hsync-len = <10>; + hback-porch = <160>; + hfront-porch = <160>; + vsync-len = <1>; + vback-porch = <23>; + vfront-porch = <12>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; +}; + +&dsi_display { + status = "okay"; + pixel-format = ; + width = <1024>; + height = <600>; +}; + ð { status = "okay"; phy-handle = <&phy>; diff --git a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.yaml b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.yaml index fb250793f077..9e202ce4c185 100644 --- a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.yaml +++ b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.yaml @@ -10,10 +10,13 @@ testing: - heap supported: - counter + - display - dma - entropy - gpio - hwinfo + - i2c + - input - netif:eth - netif:wifi - sdhc diff --git a/boards/espressif/esp32p4x_function_ev_board/Kconfig.defconfig b/boards/espressif/esp32p4x_function_ev_board/Kconfig.defconfig index 9ffc4c2721c4..c8c5b461a334 100644 --- a/boards/espressif/esp32p4x_function_ev_board/Kconfig.defconfig +++ b/boards/espressif/esp32p4x_function_ev_board/Kconfig.defconfig @@ -8,4 +8,13 @@ if BOARD_ESP32P4X_FUNCTION_EV_BOARD_ESP32P4_HPCORE configdefault ETH_DRIVER default y +# The 1024x600 panel needs 1.76 MB per framebuffer, and the driver allocates +# two of them for tear-free updates, so the stock heap does not fit them. +configdefault ESP_SPIRAM_HEAP_SIZE + default 5242880 if DISPLAY_ESP32_DSI + +# The controller has an interrupt line, so there is no reason to poll it. +configdefault INPUT_GT911_INTERRUPT + default y + endif # BOARD_ESP32P4X_FUNCTION_EV_BOARD_ESP32P4_HPCORE diff --git a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts index df97710db1e8..7aba860cb5b0 100644 --- a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts +++ b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts @@ -9,6 +9,8 @@ #include #include "esp32p4x_function_ev_board_hpcore-pinctrl.dtsi" #include +#include +#include #include / { @@ -22,6 +24,7 @@ zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; zephyr,bt-hci = &esp_hosted_mcu_hci; + zephyr,display = &dsi_display; }; aliases { @@ -74,6 +77,7 @@ clock-frequency = ; pinctrl-0 = <&i2c0_default>; pinctrl-names = "default"; + }; &spi2 { @@ -119,6 +123,12 @@ regulator-always-on; }; + ldo3@3 { + regulator-init-microvolt = <2500000>; + regulator-boot-on; + regulator-always-on; + }; + ldo4@4 { regulator-init-microvolt = <3300000>; regulator-boot-on; @@ -194,3 +204,41 @@ pinctrl-0 = <&rmii_default>; pinctrl-names = "default"; }; + +&mipi_dsi { + status = "okay"; + phy-clock = <900000000>; + dpi-clock-frequency = <48000000>; + + ek79007: panel-controller@0 { + compatible = "fitipower,ek79007"; + reg = <0>; + data-lanes = <2>; + pixel-format = ; + width = <1024>; + height = <600>; + reset-gpios = <&gpio0 27 GPIO_ACTIVE_LOW>; + bl-gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>; + + display-timings { + compatible = "zephyr,panel-timing"; + hsync-len = <10>; + hback-porch = <160>; + hfront-porch = <160>; + vsync-len = <1>; + vback-porch = <23>; + vfront-porch = <12>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; +}; + +&dsi_display { + status = "okay"; + pixel-format = ; + width = <1024>; + height = <600>; +}; diff --git a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml index a8acee2097c8..f3344c638679 100644 --- a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml +++ b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml @@ -7,10 +7,12 @@ toolchain: - zephyr supported: - counter + - display - dma - entropy - gpio - hwinfo + - i2c - i2s - netif:eth - netif:wifi diff --git a/include/zephyr/drivers/display/esp32_dsi.h b/drivers/display/display_esp32_dsi.h similarity index 100% rename from include/zephyr/drivers/display/esp32_dsi.h rename to drivers/display/display_esp32_dsi.h From 2b9a7eafa695dc8883c03a5b810838eb5806b551 Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 22:38:20 -0300 Subject: [PATCH 116/600] boards: espressif: esp32p4: repin i2c0 and the spi2 clock Move I2C0 to GPIO7 and GPIO8, and the SPI2 clock from GPIO8 to the otherwise unused GPIO23, so the bus reaches the touch controller on the LCD sub-board. I2C0 and the SPI2 clock both wanted GPIO8, so the clock is the one that moves. This changes the pins for any application already using I2C0 or SPI2 on these boards. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- .../esp32p4_function_ev_board_hpcore-pinctrl.dtsi | 6 +++--- .../esp32p4x_function_ev_board_hpcore-pinctrl.dtsi | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore-pinctrl.dtsi b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore-pinctrl.dtsi index cf510602417a..d1070d7097ac 100644 --- a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore-pinctrl.dtsi +++ b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore-pinctrl.dtsi @@ -24,7 +24,7 @@ spim2_default: spim2_default { group1 { pinmux = , - , + , ; }; @@ -36,8 +36,8 @@ i2c0_default: i2c0_default { group1 { - pinmux = , - ; + pinmux = , + ; bias-pull-up; drive-open-drain; output-high; diff --git a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore-pinctrl.dtsi b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore-pinctrl.dtsi index 3086e76dbbb6..937a10666072 100644 --- a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore-pinctrl.dtsi +++ b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore-pinctrl.dtsi @@ -24,7 +24,7 @@ spim2_default: spim2_default { group1 { pinmux = , - , + , ; }; @@ -36,8 +36,8 @@ i2c0_default: i2c0_default { group1 { - pinmux = , - ; + pinmux = , + ; bias-pull-up; drive-open-drain; output-high; From 7c00e2e7ab9c4cb36767238452e9ae0c4b972f83 Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 22:38:29 -0300 Subject: [PATCH 117/600] boards: espressif: esp32p4: add gt911 touch controller Add the Goodix GT911 capacitive touch controller that sits on the LCD sub-board and select it as the chosen touch device. Its reset and interrupt lines are not routed on the stock boards and have to be wired to GPIO21 and GPIO22. The reported axes run opposite the panel, so both are inverted. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- .../esp32p4_function_ev_board_hpcore.dts | 12 ++++++++++++ .../esp32p4x_function_ev_board_hpcore.dts | 12 ++++++++++++ .../esp32p4x_function_ev_board_hpcore.yaml | 1 + 3 files changed, 25 insertions(+) diff --git a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts index 45cb2185dbae..18c6fc2ae551 100644 --- a/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts +++ b/boards/espressif/esp32p4_function_ev_board/esp32p4_function_ev_board_hpcore.dts @@ -25,6 +25,7 @@ zephyr,code-partition = &slot0_partition; zephyr,bt-hci = &esp_hosted_mcu_hci; zephyr,display = &dsi_display; + zephyr,touch = >911; }; aliases { @@ -78,6 +79,17 @@ pinctrl-0 = <&i2c0_default>; pinctrl-names = "default"; + gt911: touch-controller@5d { + compatible = "goodix,gt911"; + status = "okay"; + reg = <0x5d>; + reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + irq-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>; + screen-width = <1024>; + screen-height = <600>; + inverted-x; + inverted-y; + }; }; &spi2 { diff --git a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts index 7aba860cb5b0..0189bce50ec2 100644 --- a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts +++ b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.dts @@ -25,6 +25,7 @@ zephyr,code-partition = &slot0_partition; zephyr,bt-hci = &esp_hosted_mcu_hci; zephyr,display = &dsi_display; + zephyr,touch = >911; }; aliases { @@ -78,6 +79,17 @@ pinctrl-0 = <&i2c0_default>; pinctrl-names = "default"; + gt911: touch-controller@5d { + compatible = "goodix,gt911"; + status = "okay"; + reg = <0x5d>; + reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + irq-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>; + screen-width = <1024>; + screen-height = <600>; + inverted-x; + inverted-y; + }; }; &spi2 { diff --git a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml index f3344c638679..3d9f59cdc6fe 100644 --- a/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml +++ b/boards/espressif/esp32p4x_function_ev_board/esp32p4x_function_ev_board_hpcore.yaml @@ -14,6 +14,7 @@ supported: - hwinfo - i2c - i2s + - input - netif:eth - netif:wifi - pulse_io From c0f32e964a0f306d4e03da3fbb9d20af6e4f345e Mon Sep 17 00:00:00 2001 From: Sylvio Alves Date: Mon, 17 Aug 2026 23:37:54 -0300 Subject: [PATCH 118/600] samples: display: add esp32p4x function ev board support Give the display and LVGL demo samples the settings the ESP32-P4X function EV board needs. The display sample draws from the system heap, which is empty by default, and the panel needs a full row band of it. Assisted-by: Claude:opus-4-8 Signed-off-by: Sylvio Alves --- .../esp32p4x_function_ev_board_hpcore.conf | 13 ++++++++++++ .../esp32p4x_function_ev_board_hpcore.conf | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 samples/drivers/display/boards/esp32p4x_function_ev_board_hpcore.conf create mode 100644 samples/modules/lvgl/demos/boards/esp32p4x_function_ev_board_hpcore.conf diff --git a/samples/drivers/display/boards/esp32p4x_function_ev_board_hpcore.conf b/samples/drivers/display/boards/esp32p4x_function_ev_board_hpcore.conf new file mode 100644 index 000000000000..95c14703ddb3 --- /dev/null +++ b/samples/drivers/display/boards/esp32p4x_function_ev_board_hpcore.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# The sample draws into a buffer it allocates from the system heap, which is +# a full 1024 pixel row band on this panel. +CONFIG_HEAP_MEM_POOL_SIZE=393216 + +# The scanout DMA reads the framebuffer, so writes to it have to land on +# whole cache lines. +CONFIG_SAMPLE_BUFFER_ADDR_ALIGN=128 diff --git a/samples/modules/lvgl/demos/boards/esp32p4x_function_ev_board_hpcore.conf b/samples/modules/lvgl/demos/boards/esp32p4x_function_ev_board_hpcore.conf new file mode 100644 index 000000000000..41c9991be811 --- /dev/null +++ b/samples/modules/lvgl/demos/boards/esp32p4x_function_ev_board_hpcore.conf @@ -0,0 +1,21 @@ +# +# Copyright (c) 2026 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# The benchmark demo builds far more widgets than the sample default covers, +# and runs out of objects part way through the scene list. +CONFIG_LV_Z_MEM_POOL_SIZE=262144 + +# Flushing a frame is a copy this panel cannot do inside the render loop, so +# it runs on its own thread. +CONFIG_LV_Z_FLUSH_THREAD=y + +# Rendering a demo scene keeps the CPU busy for long stretches, and the input +# thread runs at the lowest priority by default, so it does not get to drain +# the queue until the frame is done and the touches are dropped by then. +CONFIG_INPUT_THREAD_PRIORITY_OVERRIDE=y +CONFIG_INPUT_THREAD_PRIORITY=-1 +CONFIG_INPUT_QUEUE_MAX_MSGS=64 +CONFIG_LV_Z_POINTER_INPUT_MSGQ_COUNT=64 From 8d50683aec31d28a7427ac7f57d3a886257bda12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 18 Aug 2026 17:46:21 +0200 Subject: [PATCH 119/600] net: if: move device_is_ready check on up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if the L2 does not support enable, we still want that a iface can not be up if the device is not ready. Signed-off-by: Fin Maaß --- subsys/net/ip/net_if.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index aaf559159862..15fa33295c8d 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -6599,6 +6599,7 @@ static void init_igmp(struct net_if *iface) int net_if_up(struct net_if *iface) { + const struct device *dev; int status = 0; NET_DBG("iface %d (%p)", net_if_get_by_iface(iface), iface); @@ -6610,30 +6611,19 @@ int net_if_up(struct net_if *iface) goto out; } + dev = net_if_get_device(iface); + NET_ASSERT(dev != NULL); + + /* If the device is not ready it is pointless trying to take it up. */ + if (!device_is_ready(dev)) { + NET_DBG("Device %s (%p) is not ready", dev->name, dev); + status = -ENXIO; + goto out; + } + /* If the L2 does not support enable just set the flag */ if (!net_if_l2(iface) || !net_if_l2(iface)->enable) { goto done; - } else { - /* If the L2 does not implement enable(), then the network - * device driver cannot implement start(), in which case - * we can do simple check here and not try to bring interface - * up as the device is not ready. - * - * If the network device driver does implement start(), then - * it could bring the interface up when the enable() is called - * few lines below. - */ - const struct device *dev; - - dev = net_if_get_device(iface); - NET_ASSERT(dev); - - /* If the device is not ready it is pointless trying to take it up. */ - if (!device_is_ready(dev)) { - NET_DBG("Device %s (%p) is not ready", dev->name, dev); - status = -ENXIO; - goto out; - } } /* Notify L2 to enable the interface. Note that the interface is still down From a1393efb10cf93209036f41bb89ebc4a2a162201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 18 Aug 2026 17:49:37 +0200 Subject: [PATCH 120/600] net: if: remove device_is_ready from update_operational_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit as we already check device_is_ready in net_if_up and the iface admin state will not be up if the device is not ready, we don't need to check in update_operational_state directly after checking the admin state. Signed-off-by: Fin Maaß --- subsys/net/ip/net_if.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 15fa33295c8d..6f0b1471f3a9 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -6533,11 +6533,6 @@ static void update_operational_state(struct net_if *iface) goto exit; } - if (!device_is_ready(net_if_get_device(iface))) { - new_state = NET_IF_OPER_LOWERLAYERDOWN; - goto exit; - } - if (!net_if_is_carrier_ok(iface)) { #if defined(CONFIG_NET_L2_VIRTUAL) if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { From 7a56a3f702012c9addd20d21599cdff94a84fa2d Mon Sep 17 00:00:00 2001 From: Christophe Guibout Date: Mon, 17 Aug 2026 15:54:26 +0200 Subject: [PATCH 121/600] drivers: dma: stm32: add support for STM32MP13X Add support for STM32MP13X series in the relevant drivers. Signed-off-by: Christophe Guibout --- drivers/dma/dma_stm32.c | 4 +++- drivers/dma/dma_stm32_v1.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dma_stm32.c b/drivers/dma/dma_stm32.c index 2a31835edd00..9b486f582ed2 100644 --- a/drivers/dma/dma_stm32.c +++ b/drivers/dma/dma_stm32.c @@ -484,7 +484,9 @@ static int dma_stm32_configure(const struct device *dev, DMA_InitStruct.PeriphBurst = stm32_dma_get_pburst(config, stream->source_periph); -#if !defined(CONFIG_SOC_SERIES_STM32H7X) && !defined(CONFIG_SOC_SERIES_STM32MP1X) +#if !defined(CONFIG_SOC_SERIES_STM32H7X) && \ + !defined(CONFIG_SOC_SERIES_STM32MP1X) && \ + !defined(CONFIG_SOC_SERIES_STM32MP13X) if (config->channel_direction != MEMORY_TO_MEMORY) { if (config->dma_slot >= 8) { LOG_ERR("dma slot error."); diff --git a/drivers/dma/dma_stm32_v1.c b/drivers/dma/dma_stm32_v1.c index 4b437d120116..b66a87f29172 100644 --- a/drivers/dma/dma_stm32_v1.c +++ b/drivers/dma/dma_stm32_v1.c @@ -35,7 +35,9 @@ uint32_t dma_stm32_id_to_stream(uint32_t id) return stream_nr[id]; } -#if !defined(CONFIG_SOC_SERIES_STM32H7X) && !defined(CONFIG_SOC_SERIES_STM32MP1X) +#if !defined(CONFIG_SOC_SERIES_STM32H7X) && \ + !defined(CONFIG_SOC_SERIES_STM32MP1X) && \ + !defined(CONFIG_SOC_SERIES_STM32MP13X) uint32_t dma_stm32_slot_to_channel(uint32_t slot) { static const uint32_t channel_nr[] = { From d749a77f8bf2b77b091ab90cf82dff590e75eac7 Mon Sep 17 00:00:00 2001 From: Christophe Guibout Date: Fri, 24 Jul 2026 15:33:15 +0200 Subject: [PATCH 122/600] dts: arm: st: add STM32MP13 DMA/DMAMUX nodes Add DMA support for STM32MP13 by defining DMA1, DMA2, and DMAMUX1 controller nodes in the SoC devicetree. Signed-off-by: Christophe Guibout --- dts/arm/st/mp13/stm32mp13.dtsi | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/dts/arm/st/mp13/stm32mp13.dtsi b/dts/arm/st/mp13/stm32mp13.dtsi index 39f26a2c0306..0fed868eaa0a 100644 --- a/dts/arm/st/mp13/stm32mp13.dtsi +++ b/dts/arm/st/mp13/stm32mp13.dtsi @@ -196,6 +196,55 @@ <12 1>, <13 1>, <14 1>, <15 1>; }; + dma1: dma@48000000 { + compatible = "st,stm32-dma-v1"; + #dma-cells = <4>; + reg = <0x48000000 0x400>; + clocks = <&rcc STM32_CLOCK(AHB2, 0)>; + st,mem2mem; + interrupts = , + , + , + , + , + , + , + ; + dma-offset = <0>; + dma-requests = <8>; + status = "disabled"; + }; + + dma2: dma@48001000 { + compatible = "st,stm32-dma-v1"; + #dma-cells = <4>; + reg = <0x48001000 0x400>; + clocks = <&rcc STM32_CLOCK(AHB2, 1)>; + st,mem2mem; + interrupts = , + , + , + , + , + , + , + ; + dma-offset = <8>; + dma-requests = <8>; + status = "disabled"; + }; + + dmamux1: dmamux@48002000 { + compatible = "st,stm32-dmamux"; + #dma-cells = <3>; + reg = <0x48002000 0x400>; + clocks = <&rcc STM32_CLOCK(AHB2, 2)>; + dma-channels = <16>; + dma-generators = <8>; + dma-requests = <128>; + status = "disabled"; + }; + spi1: spi@44004000 { compatible = "st,stm32h7-spi", "st,stm32-spi-fifo", "st,stm32-spi"; reg = <0x44004000 0x400>; From b971adc69b33b2c4de04f6ce4e3b3f077d4c522d Mon Sep 17 00:00:00 2001 From: Christophe Guibout Date: Tue, 25 Aug 2026 18:03:16 +0200 Subject: [PATCH 123/600] boards: st: stm32mp135f_dk: add dma support Enable DMA for STM32MP135F-DK board. Signed-off-by: Christophe Guibout --- boards/st/stm32mp135f_dk/stm32mp135f_dk.dts | 12 ++++++++++++ boards/st/stm32mp135f_dk/twister.yaml | 1 + 2 files changed, 13 insertions(+) diff --git a/boards/st/stm32mp135f_dk/stm32mp135f_dk.dts b/boards/st/stm32mp135f_dk/stm32mp135f_dk.dts index 4eb2a31bec38..ca248f7dd171 100644 --- a/boards/st/stm32mp135f_dk/stm32mp135f_dk.dts +++ b/boards/st/stm32mp135f_dk/stm32mp135f_dk.dts @@ -103,6 +103,18 @@ status = "okay"; }; +&dma1 { + status = "okay"; +}; + +&dma2 { + status = "okay"; +}; + +&dmamux1 { + status = "okay"; +}; + &pll1 { clocks = <&clk_hse>; div-m = <2>; diff --git a/boards/st/stm32mp135f_dk/twister.yaml b/boards/st/stm32mp135f_dk/twister.yaml index 56494010d02b..33ca2ec575c5 100644 --- a/boards/st/stm32mp135f_dk/twister.yaml +++ b/boards/st/stm32mp135f_dk/twister.yaml @@ -6,6 +6,7 @@ toolchain: - zephyr - gnuarmemb supported: + - dma - gpio - uart - spi From 219d376ae23854c730996efc29d22d2b43c5e34f Mon Sep 17 00:00:00 2001 From: Christophe Guibout Date: Wed, 19 Aug 2026 09:18:07 +0200 Subject: [PATCH 124/600] tests: drivers: Enable tests for stm32mp135f_dk board SUITE PASS - 100.00% [dma_m2m_loop]: pass = 2, fail = 0, skip = 1, total = 3 duration = 0.576 seconds Signed-off-by: Christophe Guibout --- .../drivers/dma/loop_transfer/boards/stm32mp135f_dk.conf | 5 +++++ .../dma/loop_transfer/boards/stm32mp135f_dk.overlay | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.conf create mode 100644 tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.overlay diff --git a/tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.conf b/tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.conf new file mode 100644 index 000000000000..689658000ab6 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.conf @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +# Keep a fixed fallback channel if dma_request_channel() is unsupported. +CONFIG_DMA_LOOP_TRANSFER_CHANNEL_NR=2 diff --git a/tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.overlay b/tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.overlay new file mode 100644 index 000000000000..8aa51d72a3d6 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/stm32mp135f_dk.overlay @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +tst_dma0: &dmamux1 { + status = "okay"; +}; From 020e83e06b35e98a4a3c2354f67e5eee3f163363 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 14:42:11 -0700 Subject: [PATCH 125/600] drivers: gpio: add gpio_port_pin_is_supported() The single-pin GPIO APIs validate their pin argument with __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, "Unsupported pin"); gpio_pin_t is a uint8_t, shifting by at least the width of the shifted type is undefined behaviour. An out of range pin therefore aliases a supported pin and the test that is meant to reject it passes instead, which is why these assertions never fired for the pins they were added to catch. Add gpio_port_pin_is_supported(), which rejects any pin that is not below GPIO_MAX_PINS_PER_PORT before using it. Signed-off-by: Flavio Ceolin --- include/zephyr/drivers/gpio.h | 49 +++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/include/zephyr/drivers/gpio.h b/include/zephyr/drivers/gpio.h index 9cf3600da1e6..8609caaaecf2 100644 --- a/include/zephyr/drivers/gpio.h +++ b/include/zephyr/drivers/gpio.h @@ -690,6 +690,30 @@ struct gpio_dt_spec { */ #define GPIO_MAX_PINS_PER_PORT (sizeof(gpio_port_pins_t) * __CHAR_BIT__) +/** + * @brief Check whether a pin is in range and supported by a GPIO controller. + * + * @note @p pin is checked against @ref GPIO_MAX_PINS_PER_PORT before it is + * used as a shift count. Drivers must use this helper instead of testing + * `port_pin_mask & BIT(pin)` directly, as @p pin may originate from an + * untrusted caller. + * + * @param port_pin_mask Mask identifying the pins supported by the controller, + * i.e. @ref gpio_driver_config.port_pin_mask. + * @param pin Pin number to check. + * + * @retval true If @p pin is in range and supported by the controller. + * @retval false Otherwise. + */ +static inline bool gpio_port_pin_is_supported(gpio_port_pins_t port_pin_mask, gpio_pin_t pin) +{ + if (pin >= GPIO_MAX_PINS_PER_PORT) { + return false; + } + + return (port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U; +} + /** * This structure is common to all GPIO drivers and is expected to be * the first element in the object pointed to by the config field @@ -1035,8 +1059,7 @@ static inline int z_impl_gpio_pin_interrupt_configure(const struct device *port, (flags & GPIO_INT_ENABLE_DISABLE_ONLY_VALUE) != 0, "At least one of GPIO_INT_LOW_0, GPIO_INT_HIGH_1 has to be enabled."); #undef GPIO_INT_ENABLE_DISABLE_ONLY_VALUE - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); if (((flags & GPIO_INT_LEVELS_LOGICAL) != 0) && ((data->invert & (gpio_port_pins_t)BIT(pin)) != 0)) { @@ -1142,8 +1165,7 @@ static inline int z_impl_gpio_pin_configure(const struct device *port, flags &= ~GPIO_OUTPUT_INIT_LOGICAL; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); if ((flags & GPIO_ACTIVE_LOW) != 0) { data->invert |= (gpio_port_pins_t)BIT(pin); @@ -1236,7 +1258,7 @@ static inline int gpio_pin_is_input(const struct device *port, gpio_pin_t pin) __unused const struct gpio_driver_config *cfg = (const struct gpio_driver_config *)port->config; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); rv = gpio_port_get_direction(port, BIT(pin), &pins, NULL); if (rv < 0) { @@ -1281,7 +1303,7 @@ static inline int gpio_pin_is_output(const struct device *port, gpio_pin_t pin) __unused const struct gpio_driver_config *cfg = (const struct gpio_driver_config *)port->config; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); rv = gpio_port_get_direction(port, BIT(pin), NULL, &pins); if (rv < 0) { @@ -1691,8 +1713,7 @@ static inline int z_impl_gpio_pin_get_raw(const struct device *port, gpio_pin_t gpio_port_value_t value; int ret; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); ret = z_impl_gpio_port_get_raw(port, &value); if (ret == 0) { @@ -1730,8 +1751,7 @@ static inline int z_impl_gpio_pin_get(const struct device *port, gpio_pin_t pin) gpio_port_value_t value; int ret; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); ret = z_impl_gpio_port_get(port, &value); if (ret == 0) { @@ -1780,8 +1800,7 @@ static inline int z_impl_gpio_pin_set_raw(const struct device *port, gpio_pin_t (const struct gpio_driver_config *)port->config; int ret; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); if (value != 0) { ret = z_impl_gpio_port_set_bits_raw(port, (gpio_port_pins_t)BIT(pin)); @@ -1823,8 +1842,7 @@ static inline int z_impl_gpio_pin_set(const struct device *port, gpio_pin_t pin, const struct gpio_driver_data *const data = (const struct gpio_driver_data *)port->data; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); if (data->invert & (gpio_port_pins_t)BIT(pin)) { value = (value != 0) ? 0 : 1; @@ -1866,8 +1884,7 @@ static inline int z_impl_gpio_pin_toggle(const struct device *port, gpio_pin_t p __unused const struct gpio_driver_config *const cfg = (const struct gpio_driver_config *)port->config; - __ASSERT((cfg->port_pin_mask & (gpio_port_pins_t)BIT(pin)) != 0U, - "Unsupported pin"); + __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); return z_impl_gpio_port_toggle_bits(port, (gpio_port_pins_t)BIT(pin)); } From d145faab7e3a8cea62a8fd7ed5f30ef73e762f59 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 14:42:20 -0700 Subject: [PATCH 126/600] drivers: gpio: reject out of range pins at runtime Return -EINVAL for any pin that is not below GPIO_MAX_PINS_PER_PORT, so that the value a driver receives is always one it can safely use as an index no matter how the build is configured. Signed-off-by: Flavio Ceolin --- include/zephyr/drivers/gpio.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/zephyr/drivers/gpio.h b/include/zephyr/drivers/gpio.h index 8609caaaecf2..21acde2a6d6f 100644 --- a/include/zephyr/drivers/gpio.h +++ b/include/zephyr/drivers/gpio.h @@ -1061,6 +1061,12 @@ static inline int z_impl_gpio_pin_interrupt_configure(const struct device *port, #undef GPIO_INT_ENABLE_DISABLE_ONLY_VALUE __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); + /* See the note in z_impl_gpio_pin_configure(). */ + if (pin >= GPIO_MAX_PINS_PER_PORT) { + SYS_PORT_TRACING_FUNC_EXIT(gpio_pin, interrupt_configure, port, pin, -EINVAL); + return -EINVAL; + } + if (((flags & GPIO_INT_LEVELS_LOGICAL) != 0) && ((data->invert & (gpio_port_pins_t)BIT(pin)) != 0)) { /* Invert signal bits */ @@ -1167,6 +1173,16 @@ static inline int z_impl_gpio_pin_configure(const struct device *port, __ASSERT(gpio_port_pin_is_supported(cfg->port_pin_mask, pin), "Unsupported pin"); + /* + * pin is passed on to the driver as-is, where it is commonly used as an + * array index, so it must be range checked even when assertions are + * compiled out. + */ + if (pin >= GPIO_MAX_PINS_PER_PORT) { + SYS_PORT_TRACING_FUNC_EXIT(gpio_pin, configure, port, pin, -EINVAL); + return -EINVAL; + } + if ((flags & GPIO_ACTIVE_LOW) != 0) { data->invert |= (gpio_port_pins_t)BIT(pin); } else { @@ -1362,6 +1378,12 @@ static inline int z_impl_gpio_pin_get_config(const struct device *port, return -ENOSYS; } + /* See the note in z_impl_gpio_pin_configure(). */ + if (pin >= GPIO_MAX_PINS_PER_PORT) { + SYS_PORT_TRACING_FUNC_EXIT(gpio_pin, get_config, port, pin, -EINVAL); + return -EINVAL; + } + ret = api->pin_get_config(port, pin, flags); SYS_PORT_TRACING_FUNC_EXIT(gpio_pin, get_config, port, pin, ret); return ret; From c9641fa5bfec44892e0cf2d84f78c7c60fca8ad6 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 14:42:30 -0700 Subject: [PATCH 127/600] drivers: gpio: validate pin in the syscall handlers Validate pin against the controller's port_pin_mask before entering the API, and return -EINVAL for anything the controller does not support. Signed-off-by: Flavio Ceolin --- drivers/gpio/gpio_handlers.c | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/gpio/gpio_handlers.c b/drivers/gpio/gpio_handlers.c index 2a827afea79f..8bbb76df810e 100644 --- a/drivers/gpio/gpio_handlers.c +++ b/drivers/gpio/gpio_handlers.c @@ -7,11 +7,24 @@ #include #include +static inline bool gpio_syscall_pin_is_supported(const struct device *port, gpio_pin_t pin) +{ + const struct gpio_driver_config *const cfg = + (const struct gpio_driver_config *)port->config; + + return gpio_port_pin_is_supported(cfg->port_pin_mask, pin); +} + static inline int z_vrfy_gpio_pin_configure(const struct device *port, gpio_pin_t pin, gpio_flags_t flags) { K_OOPS(K_SYSCALL_DRIVER_GPIO(port, pin_configure)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_configure(port, pin, flags); } #include @@ -24,6 +37,10 @@ static inline int z_vrfy_gpio_pin_get_config(const struct device *port, K_OOPS(K_SYSCALL_OBJ(port, K_OBJ_DRIVER_GPIO)); K_OOPS(K_SYSCALL_MEMORY_WRITE(flags, sizeof(gpio_flags_t))); + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_get_config(port, pin, flags); } #include @@ -126,6 +143,11 @@ static inline int z_vrfy_gpio_port_set_clr_bits(const struct device *port, static inline int z_vrfy_gpio_pin_get_raw(const struct device *port, gpio_pin_t pin) { K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_get_raw)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_get_raw(port, pin); } #include @@ -133,6 +155,11 @@ static inline int z_vrfy_gpio_pin_get_raw(const struct device *port, gpio_pin_t static inline int z_vrfy_gpio_pin_get(const struct device *port, gpio_pin_t pin) { K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_get_raw)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_get(port, pin); } #include @@ -141,6 +168,11 @@ static inline int z_vrfy_gpio_pin_set_raw(const struct device *port, gpio_pin_t { K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_set_bits_raw)); K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_clear_bits_raw)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_set_raw(port, pin, value); } #include @@ -149,6 +181,11 @@ static inline int z_vrfy_gpio_pin_set(const struct device *port, gpio_pin_t pin, { K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_set_bits_raw)); K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_clear_bits_raw)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_set(port, pin, value); } #include @@ -156,6 +193,11 @@ static inline int z_vrfy_gpio_pin_set(const struct device *port, gpio_pin_t pin, static inline int z_vrfy_gpio_pin_toggle(const struct device *port, gpio_pin_t pin) { K_OOPS(K_SYSCALL_DRIVER_GPIO(port, port_toggle_bits)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_toggle(port, pin); } #include @@ -165,6 +207,11 @@ static inline int z_vrfy_gpio_pin_interrupt_configure(const struct device *port, gpio_flags_t flags) { K_OOPS(K_SYSCALL_OBJ(port, K_OBJ_DRIVER_GPIO)); + + if (!gpio_syscall_pin_is_supported(port, pin)) { + return -EINVAL; + } + return z_impl_gpio_pin_interrupt_configure(port, pin, flags); } #include From d28a05d5de4420f27ba3de7b9e4442fa056d51de Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 14:42:40 -0700 Subject: [PATCH 128/600] drivers: gpio: emul: validate pins Use gpio_port_pin_is_supported(), which range checks the pin before it is used as a shift count. Signed-off-by: Flavio Ceolin --- drivers/gpio/gpio_emul.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio_emul.c b/drivers/gpio/gpio_emul.c index 8cbaa4449dc9..af354f4ecc0f 100644 --- a/drivers/gpio/gpio_emul.c +++ b/drivers/gpio/gpio_emul.c @@ -400,7 +400,7 @@ int gpio_emul_flags_get(const struct device *port, gpio_pin_t pin, gpio_flags_t return -EINVAL; } - if ((config->common.port_pin_mask & BIT(pin)) == 0) { + if (!gpio_port_pin_is_supported(config->common.port_pin_mask, pin)) { return -EINVAL; } @@ -435,7 +435,7 @@ static int gpio_emul_pin_configure(const struct device *port, gpio_pin_t pin, return -ENOTSUP; } - if ((config->common.port_pin_mask & BIT(pin)) == 0) { + if (!gpio_port_pin_is_supported(config->common.port_pin_mask, pin)) { return -EINVAL; } @@ -486,7 +486,7 @@ static int gpio_emul_pin_get_config(const struct device *port, gpio_pin_t pin, (struct gpio_emul_data *)port->data; const struct gpio_emul_config *config = (const struct gpio_emul_config *)port->config; - if ((config->common.port_pin_mask & BIT(pin)) == 0) { + if (!gpio_port_pin_is_supported(config->common.port_pin_mask, pin)) { return -EINVAL; } @@ -702,7 +702,7 @@ static int gpio_emul_pin_interrupt_configure(const struct device *port, gpio_pin (const struct gpio_emul_config *)port->config; k_spinlock_key_t key; - if ((BIT(pin) & config->common.port_pin_mask) == 0) { + if (!gpio_port_pin_is_supported(config->common.port_pin_mask, pin)) { return -EINVAL; } From e3752373ecaacc9ee6029d7c7003a3c394397608 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Thu, 20 Aug 2026 14:42:54 -0700 Subject: [PATCH 129/600] tests: drivers: gpio: add pin range tests Check that the single-pin GPIO APIs reject a pin the controller does not support instead of aliasing it onto one that it does. The emulated controller is instantiated with ngpios = 8, so the pins the tests use cover both the ones that alias a supported pin once the shift count wraps (32 to 39 on a 32-bit build, 64 to 71 on a 64-bit one) and the ones that are simply not in port_pin_mask. Supported pins are exercised as well, to show the range check does not reject them. Three configurations are run. The default one and the userspace one keep assertions enabled, and the third disables them so that the checks that do not depend on CONFIG_ASSERT are reached. The out of range cases only run from user mode or with assertions compiled out, because a supervisor caller is expected to trip the assertion in the GPIO API instead. Assisted-by: Claude Code:opus-5 Signed-off-by: Flavio Ceolin --- .../gpio/gpio_pin_range/CMakeLists.txt | 8 ++ tests/drivers/gpio/gpio_pin_range/app.overlay | 23 ++++ tests/drivers/gpio/gpio_pin_range/prj.conf | 3 + tests/drivers/gpio/gpio_pin_range/src/main.c | 127 ++++++++++++++++++ tests/drivers/gpio/gpio_pin_range/tests.yaml | 35 +++++ 5 files changed, 196 insertions(+) create mode 100644 tests/drivers/gpio/gpio_pin_range/CMakeLists.txt create mode 100644 tests/drivers/gpio/gpio_pin_range/app.overlay create mode 100644 tests/drivers/gpio/gpio_pin_range/prj.conf create mode 100644 tests/drivers/gpio/gpio_pin_range/src/main.c create mode 100644 tests/drivers/gpio/gpio_pin_range/tests.yaml diff --git a/tests/drivers/gpio/gpio_pin_range/CMakeLists.txt b/tests/drivers/gpio/gpio_pin_range/CMakeLists.txt new file mode 100644 index 000000000000..565208d818c5 --- /dev/null +++ b/tests/drivers/gpio/gpio_pin_range/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(gpio_pin_range) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/drivers/gpio/gpio_pin_range/app.overlay b/tests/drivers/gpio/gpio_pin_range/app.overlay new file mode 100644 index 000000000000..3809767c47e8 --- /dev/null +++ b/tests/drivers/gpio/gpio_pin_range/app.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 HubbleNetwork + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + test-gpio = &test_gpio_emul; + }; + + test_gpio_emul: test_gpio_emul { + status = "okay"; + compatible = "zephyr,gpio-emul"; + gpio-controller; + #gpio-cells = <0x2>; + ngpios = <8>; + rising-edge; + falling-edge; + high-level; + low-level; + }; +}; diff --git a/tests/drivers/gpio/gpio_pin_range/prj.conf b/tests/drivers/gpio/gpio_pin_range/prj.conf new file mode 100644 index 000000000000..d12333ad8a57 --- /dev/null +++ b/tests/drivers/gpio/gpio_pin_range/prj.conf @@ -0,0 +1,3 @@ +CONFIG_GPIO=y +CONFIG_GPIO_GET_CONFIG=y +CONFIG_ZTEST=y diff --git a/tests/drivers/gpio/gpio_pin_range/src/main.c b/tests/drivers/gpio/gpio_pin_range/src/main.c new file mode 100644 index 000000000000..f6724f7ab2ba --- /dev/null +++ b/tests/drivers/gpio/gpio_pin_range/src/main.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2026 HubbleNetwork + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define TEST_NODE DT_ALIAS(test_gpio) +#define TEST_NGPIOS DT_PROP(TEST_NODE, ngpios) + +static const struct device *const test_dev = DEVICE_DT_GET(TEST_NODE); + +#if defined(CONFIG_USERSPACE) || !defined(CONFIG_ASSERT) + +/* + * Out of range pins that alias a supported pin once the shift count is masked + * to the width of an unsigned long: BIT(32) == 1 on a 32-bit target and + * BIT(64) == 1 on a 64-bit one. + */ +static const gpio_pin_t oob_pins[] = { + 32, 33, 39, 64, 65, 71, 128, 129, 192, 255, +}; + +static void expect_rejected(gpio_pin_t pin) +{ + gpio_flags_t out_flags; + int ret; + + ret = gpio_pin_configure(test_dev, pin, GPIO_OUTPUT_HIGH); + zassert_true(ret < 0, "gpio_pin_configure(pin=%u) returned %d, expected an error", pin, + ret); + + ret = gpio_pin_interrupt_configure(test_dev, pin, GPIO_INT_EDGE_RISING); + zassert_true(ret < 0, "gpio_pin_interrupt_configure(pin=%u) returned %d, expected an error", + pin, ret); + + ret = gpio_pin_get_config(test_dev, pin, &out_flags); + zassert_true(ret < 0, "gpio_pin_get_config(pin=%u) returned %d, expected an error", pin, + ret); +} + +ZTEST_USER(gpio_pin_range, test_out_of_range_pin_rejected) +{ + ARRAY_FOR_EACH(oob_pins, i) { + expect_rejected(oob_pins[i]); + } +} + +#endif /* CONFIG_USERSPACE || !CONFIG_ASSERT */ + +/* + * Pins that are in range for gpio_port_pins_t but not supported by this port + * must keep being rejected. From user mode the syscall verifier turns these + * into -EINVAL; from supervisor mode the GPIO API asserts on them, so without + * userspace the test only runs with assertions compiled out. + */ +#if defined(CONFIG_USERSPACE) || !defined(CONFIG_ASSERT) +ZTEST_USER(gpio_pin_range, test_unsupported_pin_rejected) +{ + for (gpio_pin_t pin = TEST_NGPIOS; pin < GPIO_MAX_PINS_PER_PORT; pin++) { + int ret = gpio_pin_configure(test_dev, pin, GPIO_OUTPUT_HIGH); + + zassert_equal(ret, -EINVAL, "gpio_pin_configure(pin=%u) returned %d, expected %d", + pin, ret, -EINVAL); + } +} +#endif + +/* The range check must not reject any pin the controller actually supports. */ +ZTEST_USER(gpio_pin_range, test_supported_pins_still_work) +{ + for (gpio_pin_t pin = 0; pin < TEST_NGPIOS; pin++) { + gpio_flags_t out_flags; + int ret; + + ret = gpio_pin_configure(test_dev, pin, GPIO_OUTPUT_HIGH); + zassert_ok(ret, "gpio_pin_configure(pin=%u) failed: %d", pin, ret); + + ret = gpio_pin_get_config(test_dev, pin, &out_flags); + zassert_ok(ret, "gpio_pin_get_config(pin=%u) failed: %d", pin, ret); + zassert_true((out_flags & GPIO_OUTPUT) != 0, + "pin %u was not configured as an output", pin); + + ret = gpio_pin_set(test_dev, pin, 0); + zassert_ok(ret, "gpio_pin_set(pin=%u) failed: %d", pin, ret); + + ret = gpio_pin_toggle(test_dev, pin); + zassert_ok(ret, "gpio_pin_toggle(pin=%u) failed: %d", pin, ret); + } +} + +/* Exercise the helper directly, including the boundary it guards. */ +ZTEST(gpio_pin_range, test_gpio_port_pin_is_supported) +{ + zassert_true(gpio_port_pin_is_supported(0xffffffffU, 0)); + zassert_true(gpio_port_pin_is_supported(0xffffffffU, GPIO_MAX_PINS_PER_PORT - 1)); + zassert_false(gpio_port_pin_is_supported(0xffffffffU, GPIO_MAX_PINS_PER_PORT)); + zassert_false(gpio_port_pin_is_supported(0xffffffffU, UINT8_MAX)); + + /* The aliasing cases that made the old check unsound. */ + zassert_true(gpio_port_pin_is_supported(0x1U, 0)); + zassert_false(gpio_port_pin_is_supported(0x1U, 32)); + zassert_false(gpio_port_pin_is_supported(0x1U, 64)); + zassert_false(gpio_port_pin_is_supported(0x1U, 128)); + zassert_false(gpio_port_pin_is_supported(0x1U, 192)); + + /* A zero mask supports nothing. */ + zassert_false(gpio_port_pin_is_supported(0x0U, 0)); +} + +static void *gpio_pin_range_setup(void) +{ + zassert_true(device_is_ready(test_dev), "GPIO device is not ready"); + + k_object_access_grant(test_dev, k_current_get()); + + return NULL; +} + +BUILD_ASSERT(TEST_NGPIOS < GPIO_MAX_PINS_PER_PORT, + "the test needs unsupported pins below GPIO_MAX_PINS_PER_PORT"); + +ZTEST_SUITE(gpio_pin_range, NULL, gpio_pin_range_setup, NULL, NULL, NULL); diff --git a/tests/drivers/gpio/gpio_pin_range/tests.yaml b/tests/drivers/gpio/gpio_pin_range/tests.yaml new file mode 100644 index 000000000000..060cc2c0aa6f --- /dev/null +++ b/tests/drivers/gpio/gpio_pin_range/tests.yaml @@ -0,0 +1,35 @@ +common: + tags: + - drivers + - gpio + +tests: + drivers.gpio.pin_range: + platform_allow: + - native_sim + - native_sim/native/64 + - qemu_x86 + - qemu_x86_64 + integration_platforms: + - native_sim/native/64 + + drivers.gpio.pin_range.no_assert: + extra_configs: + - CONFIG_ASSERT=n + platform_allow: + - native_sim + - native_sim/native/64 + - qemu_x86 + - qemu_x86_64 + integration_platforms: + - native_sim/native/64 + + drivers.gpio.pin_range.userspace: + filter: CONFIG_ARCH_HAS_USERSPACE + extra_configs: + - CONFIG_TEST_USERSPACE=y + platform_allow: + - qemu_x86 + - qemu_x86_64 + integration_platforms: + - qemu_x86_64 From 490917f0cc949fea34f14a9807cbf89c11042ae7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 24 Aug 2026 14:16:10 +0300 Subject: [PATCH 130/600] Bluetooth: host: Document the callback execution-context contract State the execution-context contract on the application-facing callback structures (bt_conn_cb, bt_conn_auth_cb, bt_conn_auth_info_cb, bt_l2cap_chan_ops, bt_iso_chan_ops, bt_le_scan_cb, bt_le_ext_adv_cb, bt_le_per_adv_sync_cb), on the GATT attribute read/write methods and on the GATT client operation callbacks: the callbacks run in a thread context chosen by the stack, never an ISR, and should not block. The struct notes reference a new "Callback execution contexts" section in the LE Host documentation, which explains the hazards of blocking in a callback, the buffer-allocation deadlock in particular, and the established mitigations: keeping callbacks short, bounded allocations, and sizing buffer pools so that allocations do not have to wait. Blocking is deliberately discouraged rather than forbidden, since most Bluetooth APIs today may block and calling them from callbacks is common, managed practice. Where the documentation named an exact thread, replace it with the same contract. The named threads are already inaccurate after the host work was consolidated onto the Bluetooth workqueue, and naming them makes any future change of the dispatch context an API break, while nothing an application may rely on is lost: the guarantee that matters is thread context (not ISR), and the behavior that matters is not to block. Assisted-by: Claude:claude-fable-5 Signed-off-by: Johan Hedberg --- .../bluetooth/bluetooth-le-host.rst | 42 ++++++++++++++ include/zephyr/bluetooth/bluetooth.h | 31 ++++++++++- include/zephyr/bluetooth/conn.h | 30 +++++++++- include/zephyr/bluetooth/gatt.h | 55 +++++++++++++------ include/zephyr/bluetooth/iso.h | 12 +++- include/zephyr/bluetooth/l2cap.h | 8 +++ 6 files changed, 155 insertions(+), 23 deletions(-) diff --git a/doc/services/connectivity/bluetooth/bluetooth-le-host.rst b/doc/services/connectivity/bluetooth/bluetooth-le-host.rst index bef6f14f9227..588ca07ab7ab 100644 --- a/doc/services/connectivity/bluetooth/bluetooth-le-host.rst +++ b/doc/services/connectivity/bluetooth/bluetooth-le-host.rst @@ -99,6 +99,48 @@ Connections Connection handling and the related APIs can be found in the :ref:`Connection Management ` section. +.. _bluetooth_callback_contexts: + +Callback execution contexts +=========================== + +The Host delivers events to the application through registered callbacks, for +example those in :c:struct:`bt_conn_cb`, :c:struct:`bt_le_scan_cb` or +:c:struct:`bt_l2cap_chan_ops`. Unless documented otherwise, these callbacks +are invoked from a thread context, never from an ISR. Most run in a context +internal to the stack, but some are today invoked synchronously from within +the API call that triggers them, and so run in the calling thread: for +example :c:func:`bt_unpair` invokes ``bond_deleted``, and +:c:func:`bt_gatt_unsubscribe` can invoke ``notify`` with ``NULL`` data, +before returning. Neither behavior is part of the API: a callback delivered +synchronously today may be deferred to a stack-internal context in a future +release, or vice versa, and the specific thread has changed between releases +before and may change again. Applications should rely only on the guarantees +above, not on being called from, or before the return of, anything in +particular. + +When a callback runs in a context internal to the stack, that context is +shared with the stack's own processing: time spent in the callback delays +other Bluetooth activity, and blocking carries an additional risk, since a +wait that only Bluetooth processing itself can satisfy becomes a deadlock, +because that processing cannot proceed until the callback returns. The +classic example is allocating a buffer with ``K_FOREVER`` from a pool that is +replenished by the same context that runs the callback. As applications +cannot rely on which context a given callback uses, the practices below apply +to every callback. + +Calling Bluetooth APIs from callbacks is common and supported, even though +most of them may block; the blocking risk is then managed rather than +avoided: + +* Keep callbacks short, and defer work that is long-running or blocks + indefinitely to an application-owned thread or work queue. +* Prefer allocations with ``K_NO_WAIT`` or a bounded timeout over + ``K_FOREVER``, and handle the failure. +* Size buffer pools (for example :kconfig:option:`CONFIG_BT_L2CAP_TX_BUF_COUNT` + or :kconfig:option:`CONFIG_BT_ATT_TX_COUNT`) so that allocations made from + callbacks do not have to wait. + Security ======== diff --git a/include/zephyr/bluetooth/bluetooth.h b/include/zephyr/bluetooth/bluetooth.h index 2f7c359c7d7f..86171bb04ac7 100644 --- a/include/zephyr/bluetooth/bluetooth.h +++ b/include/zephyr/bluetooth/bluetooth.h @@ -212,6 +212,14 @@ struct bt_le_per_adv_response_info { * @note Must point to valid memory during the lifetime of the advertising set. * * @note Used in @ref bt_le_ext_adv_create. + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. */ struct bt_le_ext_adv_cb { /** @@ -333,7 +341,9 @@ typedef void (*bt_ready_cb_t)(int err); * earlier. * * @param cb Callback to notify completion or NULL to perform the - * enabling synchronously. The callback is called from the system workqueue. + * enabling synchronously. The callback is called from a thread context + * internal to the stack, never from an ISR; see + * @rstref{Callback execution contexts }. * * @return Zero on success or (negative) error code otherwise. */ @@ -1869,6 +1879,14 @@ struct bt_le_per_adv_sync_state_info { * advertising. * * @note Used in @ref bt_le_per_adv_sync_cb_register function. + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. */ struct bt_le_per_adv_sync_cb { @@ -2519,7 +2537,16 @@ struct bt_le_scan_recv_info { uint8_t secondary_phy; }; -/** Listener context for (LE) scanning. */ +/** Listener context for (LE) scanning. + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. + */ struct bt_le_scan_cb { /** diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 69f5cdfbcadf..3af67b462e83 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -2217,6 +2217,14 @@ struct bt_conn_br_cb { * tracking the connection state. If a callback is not of interest for * an instance, it may be set to NULL and will as a consequence not be * used for that instance. + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. */ struct bt_conn_cb { /** @brief A new connection has been established. @@ -2888,7 +2896,16 @@ struct bt_conn_pairing_feat { */ #define BT_PASSKEY_RAND 0xffffffff -/** Authenticated pairing callback structure */ +/** Authenticated pairing callback structure + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. + */ struct bt_conn_auth_cb { #if defined(CONFIG_BT_SMP_APP_PAIRING_ACCEPT) || defined(__DOXYGEN__) /** @brief Query to proceed incoming pairing or not. @@ -3113,7 +3130,16 @@ struct bt_conn_auth_cb { #endif /* CONFIG_BT_APP_PASSKEY */ }; -/** Authenticated pairing information callback structure */ +/** Authenticated pairing information callback structure + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. + */ struct bt_conn_auth_info_cb { /** @brief notify that pairing procedure was complete. * diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index 974648651b57..7dbb1c821cb9 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -181,6 +181,11 @@ struct bt_gatt_attr; * @note The GATT server propagates the return value from this * method back to the remote client. * + * @note When the stack invokes this method to serve a remote + * operation it does so from a thread context chosen by the + * stack, never from an ISR, and the implementation should not + * block. + * * @param conn The connection that is requesting to read. * NULL if local. * @param attr The attribute that's being read @@ -224,6 +229,11 @@ typedef ssize_t (*bt_gatt_attr_read_func_t)(struct bt_conn *conn, * @note The GATT server propagates the return value from this * method back to the remote client. * + * @note When the stack invokes this method to serve a remote + * operation it does so from a thread context chosen by the + * stack, never from an ISR, and the implementation should not + * block. + * * @param conn The connection that is requesting to write * @param attr The attribute that's being written * @param buf Buffer with the data to write @@ -1436,7 +1446,8 @@ struct bt_gatt_notify_params { * With the addition that after sending the notification the * callback function will be called. * - * The callback is run from System Workqueue context. + * The callback runs in a thread context, never in an ISR; see + * @rstref{Callback execution contexts }. * When called from the System Workqueue context this API will not wait for * resources for the callback but instead return an error. * @@ -1731,7 +1742,8 @@ uint16_t bt_gatt_get_uatt_mtu(struct bt_conn *conn); * * Used with @ref bt_gatt_exchange_mtu function to initiate an MTU exchange. The * response is handled in the callback @p func, which is called upon - * completion from the Bluetooth RX thread. + * completion in a thread context, never in an ISR; see + * @rstref{Callback execution contexts }. * * @p params must remain valid until the callback executes. */ @@ -1748,9 +1760,10 @@ struct bt_gatt_exchange_params { * * As the response comes in callback @p params->func, for example * @ref bt_gatt_get_mtu can be invoked in the mtu_exchange-callback to read - * out the new negotiated ATT connection MTU. The callback is run from the - * context of the Bluetooth RX thread and @p params must remain - * valid until start of callback. + * out the new negotiated ATT connection MTU. The callback runs in a thread + * context, never in an ISR; see + * @rstref{Callback execution contexts }. + * @p params must remain valid until start of callback. * * @param conn Connection object. * @param params Exchange MTU parameters. @@ -1926,8 +1939,10 @@ struct bt_gatt_discover_params { * For each attribute found the callback is called which can then decide * whether to continue discovering or stop. * - * The Response comes in callback @p params->func. The callback is run from - * the BT RX thread. @p params must remain valid until start of callback where + * The Response comes in callback @p params->func. The callback runs in + * a thread context, never in an ISR; see + * @rstref{Callback execution contexts }. + * @p params must remain valid until start of callback where * iter `attr` is `NULL` or callback will return `BT_GATT_ITER_STOP`. * * @param conn Connection object. @@ -2060,8 +2075,9 @@ struct bt_gatt_read_params { * Note that the effect of returning @ref BT_GATT_ITER_CONTINUE from the * callback varies depending on the type of read operation. * - * The Response comes in callback @p params->func. The callback is run from - * the context of the Bluetooth RX thread. + * The Response comes in callback @p params->func. The callback runs in + * a thread context, never in an ISR; see + * @rstref{Callback execution contexts }. * @p params must remain valid until start of callback. * If the received data length is invalid, the callback @p params->func will * called with the error @ref BT_ATT_ERR_INVALID_PDU. @@ -2111,8 +2127,9 @@ struct bt_gatt_write_params { /** @brief Write Attribute Value by handle * - * The Response comes in callback @p params->func. The callback is run from - * the context of the Bluetooth RX thread. + * The Response comes in callback @p params->func. The callback runs in + * a thread context, never in an ISR; see + * @rstref{Callback execution contexts }. * @p params must remain valid until start of callback. * * @param conn Connection object. @@ -2134,7 +2151,8 @@ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params); * With the addition that after sending the write the callback function will be * called. * - * The callback is run from System Workqueue context. + * The callback runs in a thread context, never in an ISR; see + * @rstref{Callback execution contexts }. * When called from the System Workqueue context this API will not wait for * resources for the callback but instead return an error. * @@ -2318,10 +2336,10 @@ struct bt_gatt_subscribe_params { * this callback. Notification callback with NULL data will not be called if * subscription was removed by this method. * - * The Response comes in callback @p params->subscribe. The callback is run from - * the context of the Bluetooth RX thread. - * The Notification callback @p params->notify is also called from the BT RX - * thread. + * The Response comes in callback @p params->subscribe, and notifications in + * @p params->notify. Both callbacks run in a thread context, never in an + * ISR; see + * @rstref{Callback execution contexts }. * * @note Notifications are asynchronous therefore the @p params must remain * valid while subscribed and cannot be reused for additional subscriptions @@ -2372,8 +2390,9 @@ int bt_gatt_resubscribe(uint8_t id, const bt_addr_le_t *peer, * will be called if subscription was removed by this call, until then the * parameters cannot be reused. * - * The Response comes in callback @p params->func. The callback is run from - * the BT RX thread. + * The Response comes in callback @p params->notify with NULL data, + * either from within this function or from a thread context chosen by + * the stack. * * @param conn Connection object. * @param params Subscribe parameters. The parameters shall be a @ref bt_gatt_subscribe_params from diff --git a/include/zephyr/bluetooth/iso.h b/include/zephyr/bluetooth/iso.h index 3e0f245b3e09..49bea873b6af 100644 --- a/include/zephyr/bluetooth/iso.h +++ b/include/zephyr/bluetooth/iso.h @@ -689,7 +689,17 @@ struct bt_iso_biginfo { bool encryption; }; -/** @brief ISO Channel operations structure. */ +/** + * @brief ISO Channel operations structure. + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. + */ struct bt_iso_chan_ops { /** * @brief Channel connected callback diff --git a/include/zephyr/bluetooth/l2cap.h b/include/zephyr/bluetooth/l2cap.h index 5f33813c5c92..fc727e2e15ff 100644 --- a/include/zephyr/bluetooth/l2cap.h +++ b/include/zephyr/bluetooth/l2cap.h @@ -618,6 +618,14 @@ struct bt_l2cap_br_chan { /** @brief L2CAP Channel operations structure. * * The object has to stay valid and constant for the lifetime of the channel. + * + * @note The callbacks are invoked from a thread context, never from an + * ISR. Whether a callback is invoked from a context internal to + * the stack or synchronously from within the API call that + * triggers it, and from which context, is not part of the API and + * may change between releases. See + * @rstref{Callback execution contexts } + * for the hazards of blocking in a callback and their mitigations. */ struct bt_l2cap_chan_ops { /** @brief Channel connected callback From 35fe3a441063d3d2618364f82efbc0da742da130 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 26 Aug 2026 14:21:53 +1000 Subject: [PATCH 131/600] modem: cellular: vendor: request Kconfig CMUX MTU When switching to CMUX mode, request the MTU that Zephyr is capable of sending, instead of a hardcoded value. Signed-off-by: Jordan Yates --- drivers/modem/vendor_modem_cellular/cellular_nordic_nrf93m1.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_quectel_bg9x.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_quectel_eg800q.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_quectel_eg915u.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_simcom_a76xx.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_simcom_sim7080.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_sqn_gm02s.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_swir_hl7800.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_telit_le910c1tx.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_telit_lex10q1.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_u_blox_lara_r6.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r4.c | 2 +- drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r5.c | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/modem/vendor_modem_cellular/cellular_nordic_nrf93m1.c b/drivers/modem/vendor_modem_cellular/cellular_nordic_nrf93m1.c index 7117d7486474..85b1601ad4d7 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_nordic_nrf93m1.c +++ b/drivers/modem/vendor_modem_cellular/cellular_nordic_nrf93m1.c @@ -50,7 +50,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMI", cgmi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP("AT+CFUN=4", ok_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(nordic_nrf93m1_init_chat_script, nordic_nrf93m1_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_quectel_bg9x.c b/drivers/modem/vendor_modem_cellular/cellular_quectel_bg9x.c index 1c095e950484..9bba4f39ef97 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_quectel_bg9x.c +++ b/drivers/modem/vendor_modem_cellular/cellular_quectel_bg9x.c @@ -27,7 +27,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+QGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CIMI", cimi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+QCCID", qccid_match), - MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5,127", 300)); + MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), 300)); MODEM_CHAT_SCRIPT_DEFINE(quectel_bg9x_init_chat_script, quectel_bg9x_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_quectel_eg800q.c b/drivers/modem/vendor_modem_cellular/cellular_quectel_eg800q.c index d437cca52bc9..7d59e0060697 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_quectel_eg800q.c +++ b/drivers/modem/vendor_modem_cellular/cellular_quectel_eg800q.c @@ -25,7 +25,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMI", cgmi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CIMI", cimi_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(quectel_eg800q_init_chat_script, quectel_eg800q_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 30); diff --git a/drivers/modem/vendor_modem_cellular/cellular_quectel_eg915u.c b/drivers/modem/vendor_modem_cellular/cellular_quectel_eg915u.c index 9e13545c4bf0..49551c3d10e9 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_quectel_eg915u.c +++ b/drivers/modem/vendor_modem_cellular/cellular_quectel_eg915u.c @@ -25,7 +25,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMI", cgmi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CIMI", cimi_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(quectel_eg915u_init_chat_script, quectel_eg915u_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 30); diff --git a/drivers/modem/vendor_modem_cellular/cellular_simcom_a76xx.c b/drivers/modem/vendor_modem_cellular/cellular_simcom_a76xx.c index a1f8cf345438..18ac6102b283 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_simcom_a76xx.c +++ b/drivers/modem/vendor_modem_cellular/cellular_simcom_a76xx.c @@ -31,7 +31,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGREG?", ok_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGSN", imei_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMM", cgmm_match), - MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5,127", 300)); + MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), 300)); MODEM_CHAT_SCRIPT_DEFINE(simcom_a76xx_init_chat_script, simcom_a76xx_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_simcom_sim7080.c b/drivers/modem/vendor_modem_cellular/cellular_simcom_sim7080.c index 81db032d20e4..75626dba7b30 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_simcom_sim7080.c +++ b/drivers/modem/vendor_modem_cellular/cellular_simcom_sim7080.c @@ -26,7 +26,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGREG?", ok_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGSN", imei_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMM", cgmm_match), - MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5,127", 300)); + MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), 300)); MODEM_CHAT_SCRIPT_DEFINE(simcom_sim7080_init_chat_script, simcom_sim7080_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_sqn_gm02s.c b/drivers/modem/vendor_modem_cellular/cellular_sqn_gm02s.c index 16e4f9ea0332..ef59e078780a 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_sqn_gm02s.c +++ b/drivers/modem/vendor_modem_cellular/cellular_sqn_gm02s.c @@ -22,7 +22,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMM", cgmm_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMI", cgmi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_init_chat_script, sqn_gm02s_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_swir_hl7800.c b/drivers/modem/vendor_modem_cellular/cellular_swir_hl7800.c index e83cd67abdea..f8916a9774be 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_swir_hl7800.c +++ b/drivers/modem/vendor_modem_cellular/cellular_swir_hl7800.c @@ -36,7 +36,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMI", cgmi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CIMI", cimi_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(swir_hl7800_init_chat_script, swir_hl7800_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_telit_le910c1tx.c b/drivers/modem/vendor_modem_cellular/cellular_telit_le910c1tx.c index f606acda1332..ed644a44de7a 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_telit_le910c1tx.c +++ b/drivers/modem/vendor_modem_cellular/cellular_telit_le910c1tx.c @@ -33,7 +33,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP("AT+CFUN=1", ok_match), MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 5000), - MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5,127", 1000)); + MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), 1000)); MODEM_CHAT_SCRIPT_DEFINE(telit_le910c1tx_init_chat_script, telit_le910c1tx_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_telit_lex10q1.c b/drivers/modem/vendor_modem_cellular/cellular_telit_lex10q1.c index 0f00ac57b535..e46d1bdc898e 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_telit_lex10q1.c +++ b/drivers/modem/vendor_modem_cellular/cellular_telit_lex10q1.c @@ -30,7 +30,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMI", cgmi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 10000), - MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5,127", 1000)); + MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), 1000)); MODEM_CHAT_SCRIPT_DEFINE(telit_lex10q1_init_chat_script, telit_lex10q1_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_u_blox_lara_r6.c b/drivers/modem/vendor_modem_cellular/cellular_u_blox_lara_r6.c index 4720d6780a50..c52d5ada3013 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_u_blox_lara_r6.c +++ b/drivers/modem/vendor_modem_cellular/cellular_u_blox_lara_r6.c @@ -60,7 +60,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CIMI", cimi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CCID", ccid_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,31", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(u_blox_lara_r6_init_chat_script, u_blox_lara_r6_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r4.c b/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r4.c index 0662cf26fbd5..4b3da6bc5870 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r4.c +++ b/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r4.c @@ -26,7 +26,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGREG?", ok_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGSN", imei_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMM", cgmm_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(u_blox_sara_r4_init_chat_script, u_blox_sara_r4_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); diff --git a/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r5.c b/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r5.c index ce56d431e4ee..0ef856e31a4e 100644 --- a/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r5.c +++ b/drivers/modem/vendor_modem_cellular/cellular_u_blox_sara_r5.c @@ -30,7 +30,7 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE( MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGMR", cgmr_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CIMI", cimi_match), MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CCID", ccid_match), - MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5,127", ok_match)); + MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMUX=0,0,5," STRINGIFY(CONFIG_MODEM_CMUX_MTU), ok_match)); MODEM_CHAT_SCRIPT_DEFINE(u_blox_sara_r5_init_chat_script, u_blox_sara_r5_init_chat_script_cmds, abort_matches, modem_cellular_chat_callback_handler, 10); From 15bb2707acdcc4aa7f3a9d6ceecb0d12c67c0101 Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Wed, 26 Aug 2026 15:02:46 +0800 Subject: [PATCH 132/600] bluetooth: classic: l2cap: Fix race condition in config rsp handling Move channel state transition to config rsp sent callback to ensure the response is actually transmitted before marking the channel as connected. Previously, the state was updated with connected state and the channel `connected` will also be involved immediately after queuing the response. In the callback `connected`, the packets will be sent by the upper layer profiles to the sending queue immediately. Because the config RSP and the upper-layer profile send data through different channels, the host cannot guarantee that these packets are sent in the order of the sending function calls. Therefore, it is possible that profile packets may precede config RSP packets on the HCI bus. This change introduces `l2cap_br_config_rsp_sent_cb()` to handle the `L2CAP_FLAG_CONN_RCONF_DONE` flag and state transition only after the config response is successfully sent. Error responses continue to use the non-callback send path as they don't affect channel state. Signed-off-by: Lyle Zhu --- subsys/bluetooth/host/classic/l2cap_br.c | 55 ++++++++++++++++++------ 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/subsys/bluetooth/host/classic/l2cap_br.c b/subsys/bluetooth/host/classic/l2cap_br.c index 681baa3c4d96..519aec295b1c 100644 --- a/subsys/bluetooth/host/classic/l2cap_br.c +++ b/subsys/bluetooth/host/classic/l2cap_br.c @@ -4603,6 +4603,40 @@ static uint16_t l2cap_br_conf_opt_ext_win_size(struct bt_l2cap_chan *chan, struc } #endif /* CONFIG_BT_L2CAP_RET_FC */ +static void l2cap_br_config_rsp_sent_cb(struct bt_conn *conn, void *user_data, int err) +{ + uint16_t scid = POINTER_TO_UINT(user_data); + struct bt_l2cap_chan *chan; + + chan = bt_l2cap_br_lookup_tx_cid(conn, scid); + if (chan == NULL) { + return; + } + + if (err != 0) { + LOG_ERR("Config response of chan %p failed to send (%d)", BR_CHAN(chan), err); + l2cap_br_chan_disconn(chan); + return; + } + + atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_RCONF_DONE); + + if (!atomic_test_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_LCONF_DONE)) { + LOG_DBG("Local config req is not done"); + return; + } + + if (BR_CHAN(chan)->state == BT_L2CAP_CONFIG) { + LOG_DBG("scid 0x%04x rx MTU %u dcid 0x%04x tx MTU %u", BR_CHAN(chan)->rx.cid, + BR_CHAN(chan)->rx.mtu, BR_CHAN(chan)->tx.cid, BR_CHAN(chan)->tx.mtu); + + bt_l2cap_br_chan_set_state(chan, BT_L2CAP_CONNECTED); + if (chan->ops != NULL && chan->ops->connected != NULL) { + chan->ops->connected(chan); + } + } +} + static void l2cap_br_conf_req(struct bt_l2cap_br *l2cap, uint8_t ident, uint16_t len, struct net_buf *buf) { @@ -4614,6 +4648,7 @@ static void l2cap_br_conf_req(struct bt_l2cap_br *l2cap, uint8_t ident, uint16_t struct bt_l2cap_conf_opt *opt = NULL; uint16_t flags, dcid, opt_len, hint, result = BT_L2CAP_CONF_SUCCESS; struct net_buf *rsp_buf; + int err; if (len < sizeof(*req)) { LOG_ERR("Too small L2CAP conf req packet size"); @@ -4764,9 +4799,8 @@ static void l2cap_br_conf_req(struct bt_l2cap_br *l2cap, uint8_t ident, uint16_t hdr->len = sys_cpu_to_le16(rsp_buf->len - sizeof(*hdr)); - l2cap_send(conn, BT_L2CAP_CID_BR_SIG, rsp_buf); - if (result != BT_L2CAP_CONF_SUCCESS) { + l2cap_send(conn, BT_L2CAP_CID_BR_SIG, rsp_buf); return; } @@ -4785,17 +4819,12 @@ static void l2cap_br_conf_req(struct bt_l2cap_br *l2cap, uint8_t ident, uint16_t } #endif /* CONFIG_BT_L2CAP_RET_FC */ - atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_RCONF_DONE); - - if (atomic_test_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_LCONF_DONE) && - BR_CHAN(chan)->state == BT_L2CAP_CONFIG) { - LOG_DBG("scid 0x%04x rx MTU %u dcid 0x%04x tx MTU %u", BR_CHAN(chan)->rx.cid, - BR_CHAN(chan)->rx.mtu, BR_CHAN(chan)->tx.cid, BR_CHAN(chan)->tx.mtu); - - bt_l2cap_br_chan_set_state(chan, BT_L2CAP_CONNECTED); - if (chan->ops && chan->ops->connected) { - chan->ops->connected(chan); - } + err = bt_l2cap_br_send_cb(conn, BT_L2CAP_CID_BR_SIG, rsp_buf, l2cap_br_config_rsp_sent_cb, + UINT_TO_POINTER(BR_CHAN(chan)->tx.cid)); + if (err != 0) { + LOG_ERR("Failed to send config response of chan %p (%d)", BR_CHAN(chan), err); + net_buf_unref(rsp_buf); + l2cap_br_chan_disconn(chan); } } From b0bff8f246678234cb046d4fa74cad33684474ee Mon Sep 17 00:00:00 2001 From: Ren Chen Date: Tue, 18 Aug 2026 10:06:47 +0800 Subject: [PATCH 133/600] drivers: flash: ite_it51xxx_m1k: add write/read protected lock op code This change adds write and read protected lock extended operation code. Signed-off-by: Ren Chen --- drivers/flash/flash_ite_it51xxx_m1k.c | 85 +++++++++++++++++++ .../drivers/flash/it51xxx_flash_api_ex.h | 20 +++++ 2 files changed, 105 insertions(+) diff --git a/drivers/flash/flash_ite_it51xxx_m1k.c b/drivers/flash/flash_ite_it51xxx_m1k.c index 9cf1ed3a065a..d5e9b27d3b57 100644 --- a/drivers/flash/flash_ite_it51xxx_m1k.c +++ b/drivers/flash/flash_ite_it51xxx_m1k.c @@ -88,6 +88,15 @@ static struct flash_info ext_flash_infos[2] = { #define FSPI28AMEN BIT(4) #define SECTOR_ERASE_4KB_UNIT BIT(3) +/* 0xec: Flash Control Register 9 */ +#define SMFI_FLHCTRL9R (IT51XXX_SMFI_REGS_BASE + 0xec) +#define EC_PATH_PROTECT_LOCK BIT(4) + +/* 0xed: Flash Control Register 10 */ +#define SMFI_FLHCTRL10R (IT51XXX_SMFI_REGS_BASE + 0xed) +#define HOST_PATH_PROTECT_LOCK BIT(4) +#define DBGR_PATH_PROTECT_LOCK BIT(0) + /* 0xa6: Manual Flash 1K Command Control 1 */ #define SMFI_M1KFLHCTRL1 (IT51XXX_M1K_REGS_BASE + 0x00) #define W1S_M1K_PE BIT(1) @@ -584,6 +593,79 @@ static int m1k_flash_read_write_protect(const struct device *dev, const bool wri return 0; } + +static int m1k_flash_wr_protect_lock(const struct device *dev, const uintptr_t in, void *out) +{ + const struct flash_it51xxx_ex_op_wr_protect_lock *request = + (const struct flash_it51xxx_ex_op_wr_protect_lock *)in; + struct flash_it51xxx_ex_op_wr_protect_lock *result = + (struct flash_it51xxx_ex_op_wr_protect_lock *)out; + struct flash_it51xxx_dev_data *data = dev->data; + uint8_t ctrl_9_reg_val, ctrl_10_reg_val; + + if (data->flash != FLASH_IT51XXX_INTERNAL) { + LOG_ERR("supported internal flash (e-flash) only"); + return -ENOTSUP; + } + + if (request != NULL) { + if (request->path == 0 || (request->path & ~PROTECT_PATH_ALL) != 0) { + LOG_ERR("invalid path %#x", request->path); + return -EINVAL; + } + + if (request->path & PROTECT_PATH_EC) { + ctrl_9_reg_val = sys_read8(SMFI_FLHCTRL9R); + ctrl_9_reg_val |= EC_PATH_PROTECT_LOCK; + sys_write8(ctrl_9_reg_val, SMFI_FLHCTRL9R); + } + + if (request->path & (PROTECT_PATH_HOST | PROTECT_PATH_DBGR)) { + ctrl_10_reg_val = sys_read8(SMFI_FLHCTRL10R); + + if (request->path & PROTECT_PATH_HOST) { + ctrl_10_reg_val |= HOST_PATH_PROTECT_LOCK; + } + if (request->path & PROTECT_PATH_DBGR) { + ctrl_10_reg_val |= DBGR_PATH_PROTECT_LOCK; + } + + sys_write8(ctrl_10_reg_val, SMFI_FLHCTRL10R); + } + } + + if (result != NULL) { + if (result->path == 0 || (result->path & ~PROTECT_PATH_ALL) != 0) { + LOG_ERR("invalid path %#x to get state", result->path); + return -EINVAL; + } + + ctrl_9_reg_val = sys_read8(SMFI_FLHCTRL9R); + ctrl_10_reg_val = sys_read8(SMFI_FLHCTRL10R); + + if (result->path & PROTECT_PATH_EC) { + if (!(ctrl_9_reg_val & EC_PATH_PROTECT_LOCK)) { + result->is_locked = false; + return 0; + } + } + if (result->path & PROTECT_PATH_HOST) { + if (!(ctrl_10_reg_val & HOST_PATH_PROTECT_LOCK)) { + result->is_locked = false; + return 0; + } + } + if (result->path & PROTECT_PATH_DBGR) { + if (!(ctrl_10_reg_val & DBGR_PATH_PROTECT_LOCK)) { + result->is_locked = false; + return 0; + } + } + result->is_locked = true; + } + + return 0; +} #endif /* CONFIG_FLASH_EX_OP_ENABLED */ /* Read data from flash */ @@ -793,6 +875,9 @@ static int flash_it51xxx_ex_op(const struct device *dev, uint16_t opcode, const case FLASH_IT51XXX_READ_PROTECT: ret = m1k_flash_read_write_protect(dev, false, in, out); break; + case FLASH_IT51XXX_WR_PROTECT_LOCK: + ret = m1k_flash_wr_protect_lock(dev, in, out); + break; default: return -ENOTSUP; } diff --git a/include/zephyr/drivers/flash/it51xxx_flash_api_ex.h b/include/zephyr/drivers/flash/it51xxx_flash_api_ex.h index 0c9693046765..8fa21ea1fbb4 100644 --- a/include/zephyr/drivers/flash/it51xxx_flash_api_ex.h +++ b/include/zephyr/drivers/flash/it51xxx_flash_api_ex.h @@ -88,6 +88,10 @@ enum flash_it51xxx_ex_op { * Read protection. */ FLASH_IT51XXX_READ_PROTECT, + /** + * Lock for eFlash write/read protection. + */ + FLASH_IT51XXX_WR_PROTECT_LOCK, }; /** @@ -104,6 +108,22 @@ struct flash_it51xxx_ex_op_addr_protection { bool is_protected; }; +/** + * @brief eFlash write/read protection lock request/result + */ +struct flash_it51xxx_ex_op_wr_protect_lock { + /** Bitmap of protection paths to lock (see PROTECT_PATH_*) */ + uint8_t path; + /** + * Lock status of the specified protection paths + * + * For requests, this field is unused because only locking + * operation is supported. For results, it indicates whether + * the specified protection paths are locked. + */ + bool is_locked; +}; + /** * @} */ From 29e818d4c3792cb9fa4c0c5f023ab914903f1f32 Mon Sep 17 00:00:00 2001 From: Mingzong Zhao Date: Wed, 26 Aug 2026 15:17:24 +0800 Subject: [PATCH 134/600] dts: bindings: input: add row-size/col-size limits to DT binding Add min/max constraints for row-size and col-size in the input-keymap devicetree binding to reject out-of-range values at build time. This aligns schema validation with MATRIX_KEY 8-bit row/column encoding and uint8_t runtime storage in input_keymap. Signed-off-by: Mingzong Zhao --- dts/bindings/input/input-keymap.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dts/bindings/input/input-keymap.yaml b/dts/bindings/input/input-keymap.yaml index d827b3f04939..a063a3611ab7 100644 --- a/dts/bindings/input/input-keymap.yaml +++ b/dts/bindings/input/input-keymap.yaml @@ -17,12 +17,16 @@ properties: row-size: type: int + min: 1 + max: 255 required: true description: | The number of rows in the keymap. col-size: type: int + min: 1 + max: 255 required: true description: | The number of columns in the keymap. From 04cda19b834cae992b1ff4ba21824712cfda37da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Wed, 26 Aug 2026 08:24:11 +0000 Subject: [PATCH 135/600] drivers: mfd: infineon_mxcrypto: fix build without a child driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MFD init hook calls Cy_Crypto_Core_Enable(), but the driver never selected the MXCRYPTO PDL sources that provide it: those are only built when the TRNG or the crypto child driver selects one of the USE_INFINEON_MXCRYPTO_* symbols. Any build that enables MFD for another Infineon function driver (the autanalog or HPPASS drivers, for instance) but leaves crypto and entropy off therefore failed to link: undefined reference to `Cy_Crypto_Core_Enable' This broke the kit_pse84_eval comparator, adc_stream and autanalog tests once the board defconfigs stopped enabling crypto and entropy. Add USE_INFINEON_MXCRYPTO_CORE for the PDL sources shared by the three accelerators and select it from the MFD driver, so the driver builds on its own. Enable it by default only when a child function driver is enabled so it is no longer pulled into builds with no use for the block. Signed-off-by: Benjamin Cabé Assisted-by: Claude:opus-5 --- drivers/mfd/Kconfig.infineon_mxcrypto | 8 ++++++-- modules/hal_infineon/Kconfig | 9 +++++++++ modules/hal_infineon/mtb-dsl-pse8xxgp/CMakeLists.txt | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/Kconfig.infineon_mxcrypto b/drivers/mfd/Kconfig.infineon_mxcrypto index 6b0ff0c32fe9..a9b125853536 100644 --- a/drivers/mfd/Kconfig.infineon_mxcrypto +++ b/drivers/mfd/Kconfig.infineon_mxcrypto @@ -5,14 +5,18 @@ config MFD_INFINEON_MXCRYPTO bool "Infineon MXCRYPTO multi-function device driver" - default y + default y if ENTROPY_INFINEON_MXCRYPTO_TRNG || CRYPTO_INFINEON_MXCRYPTO depends on DT_HAS_INFINEON_MXCRYPTO_ENABLED + select USE_INFINEON_MXCRYPTO_CORE help Enable the Infineon MXCRYPTO MFD driver. The MXCRYPTO block is a single hardware crypto engine shared by a True Random Number Generator (TRNG) and AES / SHA acceleration. This driver owns the shared register base address and the mutex that serialises access between the - TRNG and crypto child function drivers. + TRNG and crypto child function drivers, and enables the block through + the MXCRYPTO PDL. + + Enabled by default when a child function driver is enabled. config MFD_INFINEON_MXCRYPTO_INIT_PRIORITY int "Infineon MXCRYPTO MFD init priority" diff --git a/modules/hal_infineon/Kconfig b/modules/hal_infineon/Kconfig index e711d44ce10a..87d88f85cf66 100644 --- a/modules/hal_infineon/Kconfig +++ b/modules/hal_infineon/Kconfig @@ -104,18 +104,27 @@ config USE_INFINEON_TRNG help Enable True Random Number Generator (TRNG) HAL module driver for Infineon devices +config USE_INFINEON_MXCRYPTO_CORE + bool + help + Enable Infineon MXCRYPTO PDL core sources, shared by the TRNG, AES + and SHA accelerators and by the MFD driver that enables the block + config USE_INFINEON_MXCRYPTO_TRNG bool + select USE_INFINEON_MXCRYPTO_CORE help Enable Infineon MXCRYPTO PDL TRNG hardware accelerator sources config USE_INFINEON_MXCRYPTO_AES bool + select USE_INFINEON_MXCRYPTO_CORE help Enable Infineon MXCRYPTO PDL AES (ECB/CBC/CTR/CCM/GCM) hardware accelerator sources config USE_INFINEON_MXCRYPTO_SHA bool + select USE_INFINEON_MXCRYPTO_CORE help Enable Infineon MXCRYPTO PDL SHA (SHA-224/256/384/512) hardware accelerator sources diff --git a/modules/hal_infineon/mtb-dsl-pse8xxgp/CMakeLists.txt b/modules/hal_infineon/mtb-dsl-pse8xxgp/CMakeLists.txt index afc5dfc655e4..0d4bcda65f88 100644 --- a/modules/hal_infineon/mtb-dsl-pse8xxgp/CMakeLists.txt +++ b/modules/hal_infineon/mtb-dsl-pse8xxgp/CMakeLists.txt @@ -57,7 +57,7 @@ endif() zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_LPCOMP ${pdl_drv_dir}/source/cy_lpcomp.c) -if(CONFIG_USE_INFINEON_MXCRYPTO_AES OR CONFIG_USE_INFINEON_MXCRYPTO_SHA OR CONFIG_USE_INFINEON_MXCRYPTO_TRNG) +if(CONFIG_USE_INFINEON_MXCRYPTO_CORE) zephyr_library_sources(${pdl_drv_dir}/source/cy_crypto.c) zephyr_library_sources(${pdl_drv_dir}/source/cy_crypto_core_hw.c) zephyr_library_sources(${pdl_drv_dir}/source/cy_crypto_core_hw_v1.c) From b44b481adb5e0db68a92d3e74bedf824f05beff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stasiak?= Date: Tue, 25 Aug 2026 15:39:31 +0200 Subject: [PATCH 136/600] drivers: nrf_clock_calibration: remove duplicate code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed leftover duplicate code after Nordic clock control rework. Signed-off-by: Michał Stasiak --- drivers/clock_control/nrf_clock_calibration.c | 34 +------------------ .../drivers/clock_control/nrf_clock_control.h | 6 ++-- 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/drivers/clock_control/nrf_clock_calibration.c b/drivers/clock_control/nrf_clock_calibration.c index f75d05c15161..6af224562025 100644 --- a/drivers/clock_control/nrf_clock_calibration.c +++ b/drivers/clock_control/nrf_clock_calibration.c @@ -54,18 +54,12 @@ static struct onoff_manager *mgrs; #endif /* Temperature sensor is only needed if - * CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0, or * CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0, since a value of 0 * indicates performing calibration periodically regardless of temperature * change. */ -#ifdef CONFIG_CLOCK_CONTROL_NRF #define USE_TEMP_SENSOR \ (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0) -#else -#define USE_TEMP_SENSOR \ - (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0) -#endif #if USE_TEMP_SENSOR static const struct device *const temp_sensor = @@ -167,29 +161,20 @@ static void start_hw_cal(void) { #if defined(CONFIG_CLOCK_CONTROL_NRF) nrfx_clock_calibration_start(); - calib_skip_cnt = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP; #else nrfx_clock_lfclk_calibration_start(); - calib_skip_cnt = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP; #endif + calib_skip_cnt = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP; } /* Start cycle by starting backoff timer and releasing HFCLK XTAL. */ static void start_cycle(void) { k_timer_start(&backoff_timer, -#if defined(CONFIG_CLOCK_CONTROL_NRF) K_MSEC(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD), -#else - K_MSEC(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD), -#endif K_NO_WAIT); hf_release(); -#if defined(CONFIG_CLOCK_CONTROL_NRF) if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON)) { -#else - if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON)) { -#endif lf_release(); } @@ -202,12 +187,7 @@ static void start_cal_process(void) return; } - -#if defined(CONFIG_CLOCK_CONTROL_NRF) - if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON)) { -#else if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON)) { -#endif hf_request(); } else { /* LF clock is probably running but it is requested to ensure @@ -287,11 +267,7 @@ static void measure_temperature(struct k_work *work) } if ((calib_skip_cnt == 0) || -#if defined(CONFIG_CLOCK_CONTROL_NRF) - (diff >= CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF)) { -#else (diff >= CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF)) { -#endif prev_temperature = temperature; started = true; start_hw_cal(); @@ -356,11 +332,7 @@ void z_nrf_clock_calibration_done_handler(void) int z_nrf_clock_calibration_count(void) { -#ifdef CONFIG_CLOCK_CONTROL_NRF - if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) { -#else if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) { -#endif return -1; } @@ -369,11 +341,7 @@ int z_nrf_clock_calibration_count(void) int z_nrf_clock_calibration_skips_count(void) { -#ifdef CONFIG_CLOCK_CONTROL_NRF - if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) { -#else if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) { -#endif return -1; } diff --git a/include/zephyr/drivers/clock_control/nrf_clock_control.h b/include/zephyr/drivers/clock_control/nrf_clock_control.h index 35e9a2ff068e..22ca812d5082 100644 --- a/include/zephyr/drivers/clock_control/nrf_clock_control.h +++ b/include/zephyr/drivers/clock_control/nrf_clock_control.h @@ -136,8 +136,7 @@ void z_nrf_clock_calibration_force_start(void); /** @brief Return number of calibrations performed. * - * Valid when @kconfig{CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG} or - * @kconfig{CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG} is set. + * Valid when @kconfig{CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG} is set. * * @return Number of calibrations or -1 if feature is disabled. */ @@ -145,8 +144,7 @@ int z_nrf_clock_calibration_count(void); /** @brief Return number of attempts when calibration was skipped. * - * Valid when @kconfig{CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG} or - * @kconfig{CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG} is set. + * Valid when @kconfig{CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG} is set. * * @return Number of calibrations or -1 if feature is disabled. */ From 500b77e0943d58e82268baee4e2c8fb5e7aad5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stasiak?= Date: Wed, 26 Aug 2026 09:18:00 +0200 Subject: [PATCH 137/600] tests: nrf_clock_calibration: remove duplicate code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed leftover duplicate code after Nordic clock control rework. Signed-off-by: Michał Stasiak --- .../src/test_nrf_clock_calibration.c | 48 +------------------ 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c b/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c index 455165749b0a..c2aa3207ce48 100644 --- a/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c +++ b/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c @@ -143,26 +143,16 @@ static void sync_just_after_calibration(void) */ ZTEST(nrf_clock_calibration, test_basic_clock_calibration) { -#if defined(CONFIG_CLOCK_CONTROL_NRF) - int wait_ms = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD * - (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + 1) + -#else int wait_ms = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD * (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + 1) + -#endif CALIBRATION_PROCESS_TIME_MS; struct sensor_value value = { .val1 = 0, .val2 = 0 }; mock_temp_nrf5_value_set(&value); sync_just_after_calibration(); -#if defined(CONFIG_CLOCK_CONTROL_NRF) TEST_CALIBRATION(1, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, wait_ms); -#else - TEST_CALIBRATION(1, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, - wait_ms); -#endif } /* Test checks if calibration happens just after clock is enabled. */ @@ -201,11 +191,7 @@ ZTEST(nrf_clock_calibration, test_calibration_after_enabling_lfclk) turn_on_clock(clk_dev); #endif -#if defined(CONFIG_CLOCK_CONTROL_NRF) - TEST_CALIBRATION(1, 0, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD); -#else TEST_CALIBRATION(1, 0, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD); -#endif } /* Test checks if temperature change triggers calibration. */ @@ -216,29 +202,6 @@ ZTEST(nrf_clock_calibration, test_temp_change_triggers_calibration) mock_temp_nrf5_value_set(&value); sync_just_after_calibration(); -#if defined(CONFIG_CLOCK_CONTROL_NRF) - /* change temperature by 0.25'C which should not trigger calibration */ - value.val2 += ((CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF - 1) * - 250000); - - mock_temp_nrf5_value_set(&value); - - /* expected one skip */ - TEST_CALIBRATION(0, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, - CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP * - CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + - CALIBRATION_PROCESS_TIME_MS); - - TEST_CALIBRATION(1, 0, - CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + 40); - - value.val2 += (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF * 250000); - mock_temp_nrf5_value_set(&value); - - /* expect calibration due to temp change. */ - TEST_CALIBRATION(1, 0, - CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + 40); -#else /* change temperature by 0.25'C which should not trigger calibration */ value.val2 += ((CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF - 1) * 250000); @@ -260,7 +223,6 @@ ZTEST(nrf_clock_calibration, test_temp_change_triggers_calibration) /* expect calibration due to temp change. */ TEST_CALIBRATION(1, 0, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + 40); -#endif } /* Test checks if z_nrf_clock_calibration_force_start() results in immediate @@ -276,19 +238,11 @@ ZTEST(nrf_clock_calibration, test_force_calibration) TEST_CALIBRATION(1, 0, CALIBRATION_PROCESS_TIME_MS + 5); -#if defined(CONFIG_CLOCK_CONTROL_NRF) - /* and back to scheduled operation. */ - TEST_CALIBRATION(1, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, - CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD * - (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + 1) + - CALIBRATION_PROCESS_TIME_MS); -#else /* and back to scheduled operation. */ TEST_CALIBRATION(1, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD * (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + 1) + CALIBRATION_PROCESS_TIME_MS); -#endif - } + ZTEST_SUITE(nrf_clock_calibration, NULL, NULL, NULL, NULL, NULL); From f6ae3030a6582494077b97528be8537ba175b206 Mon Sep 17 00:00:00 2001 From: Avary Higbee Date: Thu, 27 Aug 2026 11:12:09 -0600 Subject: [PATCH 138/600] mgmt: hawkbit: Prevent dangling k_malloc When there was a networking error in the s_http_start function, the hawkbit state would change to terminate, and the s_http_end function would run, freeing any k_malloc memory. But because there was no return statement, the function would proceed to allocate memory, which would not get freed because the state was already terminate which does not have a cleanup exit function assigned. Signed-off-by: Avary Higbee --- subsys/mgmt/hawkbit/hawkbit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/subsys/mgmt/hawkbit/hawkbit.c b/subsys/mgmt/hawkbit/hawkbit.c index be619a7524a8..7b0aee795939 100644 --- a/subsys/mgmt/hawkbit/hawkbit.c +++ b/subsys/mgmt/hawkbit/hawkbit.c @@ -1283,6 +1283,7 @@ static void s_http_start(void *o) if (!start_http_client(&s->hb_context.sock)) { s->hb_context.code_status = HAWKBIT_NETWORKING_ERROR; smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]); + return; } s->hb_context.response_data_size = RESPONSE_BUFFER_SIZE; From 0854530db048ccae9ea43ea8f8f61614f85f4c14 Mon Sep 17 00:00:00 2001 From: Karthikeyan Sivaji Date: Mon, 13 Jul 2026 14:29:59 +0530 Subject: [PATCH 139/600] dts: arm: microchip: pic32cx_sg: Add ethernet node Updated pic32cx_sg.dtsi to include Ethernet node. Signed-off-by: Karthikeyan Sivaji --- .../pic32c/pic32cx_sg/common/pic32cx_sg.dtsi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dts/arm/microchip/pic32c/pic32cx_sg/common/pic32cx_sg.dtsi b/dts/arm/microchip/pic32c/pic32cx_sg/common/pic32cx_sg.dtsi index 553522e30faa..7c11302345c1 100644 --- a/dts/arm/microchip/pic32c/pic32cx_sg/common/pic32cx_sg.dtsi +++ b/dts/arm/microchip/pic32c/pic32cx_sg/common/pic32cx_sg.dtsi @@ -355,6 +355,25 @@ status = "disabled"; }; + gmac: ethernet@42000800 { + compatible = "microchip,gmac-g1-eth"; + reg = <0x42000800 0x400>; + interrupts = <84 0>; + interrupt-names = "gmac"; + num-queues = <1>; + clocks = <&mclkperiph CLOCK_MCHP_MCLKPERIPH_ID_AHB_GMAC>, + <&mclkperiph CLOCK_MCHP_MCLKPERIPH_ID_APBC_GMAC>; + clock-names = "mclk-ahb", "mclk-apb"; + status = "disabled"; + + mdio: mdio { + compatible = "microchip,gmac-g1-mdio"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; + tcc2: tcc@42000c00 { compatible = "microchip,tcc-g1"; reg = <0x42000c00 0x2000>; From 41e9d8e9902db086e82b0a51ff003425c26048e2 Mon Sep 17 00:00:00 2001 From: Karthikeyan Sivaji Date: Mon, 13 Jul 2026 14:30:34 +0530 Subject: [PATCH 140/600] drivers: mdio: microchip: Update G1 MDIO Driver Increase MDIO_MCHP_OP_TIMEOUT from 25 to 50 microseconds. Signed-off-by: Karthikeyan Sivaji --- drivers/ethernet/mdio/mdio_mchp_gmac_g1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ethernet/mdio/mdio_mchp_gmac_g1.c b/drivers/ethernet/mdio/mdio_mchp_gmac_g1.c index f418a0769b76..98b6cc223b47 100644 --- a/drivers/ethernet/mdio/mdio_mchp_gmac_g1.c +++ b/drivers/ethernet/mdio/mdio_mchp_gmac_g1.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(mdio_mchp_gmac_g1, CONFIG_MDIO_LOG_LEVEL); #define DT_DRV_COMPAT microchip_gmac_g1_mdio -#define MDIO_MCHP_OP_TIMEOUT 25 +#define MDIO_MCHP_OP_TIMEOUT 50 struct mdio_dev_data { struct k_mutex reg_mutex; From 73b0e1f60b2e670a483667ba36766859cf0c3e92 Mon Sep 17 00:00:00 2001 From: Karthikeyan Sivaji Date: Mon, 13 Jul 2026 14:31:49 +0530 Subject: [PATCH 141/600] boards: microchip: pic32cx_sg41_cult: add Ethernet to supported list Updated pic32cx_sg41_cult files to include Ethernet in the supported features list. Signed-off-by: Karthikeyan Sivaji --- .../pic32cx_sg41_cult/Kconfig.defconfig | 12 +++++ .../pic32cx_sg41_cult-pinctrl.dtsi | 20 ++++++++ .../pic32cx_sg41_cult/pic32cx_sg41_cult.dts | 46 +++++++++++++++++++ .../pic32cx_sg41_cult/pic32cx_sg41_cult.yaml | 1 + 4 files changed, 79 insertions(+) create mode 100644 boards/microchip/pic32c/pic32cx_sg41_cult/Kconfig.defconfig diff --git a/boards/microchip/pic32c/pic32cx_sg41_cult/Kconfig.defconfig b/boards/microchip/pic32c/pic32cx_sg41_cult/Kconfig.defconfig new file mode 100644 index 000000000000..d75388fbe269 --- /dev/null +++ b/boards/microchip/pic32c/pic32cx_sg41_cult/Kconfig.defconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2026 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_PIC32CX_SG41_CULT + +config EEPROM + default y if NVMEM + +configdefault NET_L2_ETHERNET + default y + +endif # BOARD_PIC32CX_SG41_CULT diff --git a/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult-pinctrl.dtsi b/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult-pinctrl.dtsi index f7e2b9591613..d766892b7c30 100644 --- a/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult-pinctrl.dtsi +++ b/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult-pinctrl.dtsi @@ -69,4 +69,24 @@ ; }; }; + + mdio_default: mdio_default { + group1 { + pinmux = , + ; + }; + }; + + gmac_rmii: gmac_rmii { + group1 { + pinmux = , + , + , + , + , + , + , + ; + }; + }; }; diff --git a/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.dts b/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.dts index d38d40b6be97..0b0a981bc142 100644 --- a/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.dts +++ b/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.dts @@ -275,6 +275,26 @@ dmas = <&dmac 10 0x10>, <&dmac 11 0x11>; dma-names = "rx", "tx"; status = "okay"; + + eeprom: eeprom@5e { + compatible = "atmel,at24", "atmel,24mac402"; + reg = <0x5e>; + size = <256>; + pagesize = <16>; + address-width = <8>; + timeout = <5>; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + mac_address: mac-address@9a { + reg = <0x9a 6>; + #nvmem-cell-cells = <0>; + }; + }; + }; }; &tc5 { @@ -330,3 +350,29 @@ dma-names = "rx", "tx"; status = "okay"; }; + +&gmac { + pinctrl-0 = <&gmac_rmii>; + pinctrl-names = "default"; + + nvmem-cells = <&mac_address>; + nvmem-cell-names = "mac-address"; + phy-handle = <&phy>; + status = "okay"; +}; + +&mdio { + pinctrl-0 = <&mdio_default>; + pinctrl-names = "default"; + status = "okay"; + + phy: ethernet-phy@0 { + compatible = "ethernet-phy"; + status = "okay"; + reg = <0>; + }; +}; + +&trng { + status = "okay"; +}; diff --git a/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.yaml b/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.yaml index 2574f871b2ff..700fbd17de91 100644 --- a/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.yaml +++ b/boards/microchip/pic32c/pic32cx_sg41_cult/pic32cx_sg41_cult.yaml @@ -21,6 +21,7 @@ supported: - gpio - hwinfo - i2c + - netif:eth - interrupt_controller - pinctrl - pwm From 133c2eb7d3cb4625f74cbeb3e8d99a705b21543d Mon Sep 17 00:00:00 2001 From: Karthikeyan Sivaji Date: Mon, 13 Jul 2026 14:43:22 +0530 Subject: [PATCH 142/600] boards: microchip: pic32cx_sg61_cult: add Ethernet to supported list Updated pic32cx_sg61_cult files to include Ethernet in the supported features list. Signed-off-by: Karthikeyan Sivaji --- .../pic32cx_sg61_cult/Kconfig.defconfig | 12 +++++ .../pic32cx_sg61_cult-pinctrl.dtsi | 20 +++++++++ .../pic32cx_sg61_cult/pic32cx_sg61_cult.dts | 45 +++++++++++++++++++ .../pic32cx_sg61_cult/pic32cx_sg61_cult.yaml | 1 + 4 files changed, 78 insertions(+) create mode 100644 boards/microchip/pic32c/pic32cx_sg61_cult/Kconfig.defconfig diff --git a/boards/microchip/pic32c/pic32cx_sg61_cult/Kconfig.defconfig b/boards/microchip/pic32c/pic32cx_sg61_cult/Kconfig.defconfig new file mode 100644 index 000000000000..39d0d2f3d82d --- /dev/null +++ b/boards/microchip/pic32c/pic32cx_sg61_cult/Kconfig.defconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2026 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_PIC32CX_SG61_CULT + +config EEPROM + default y if NVMEM + +configdefault NET_L2_ETHERNET + default y + +endif # BOARD_PIC32CX_SG61_CULT diff --git a/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult-pinctrl.dtsi b/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult-pinctrl.dtsi index 35b54430e942..a09a27898411 100644 --- a/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult-pinctrl.dtsi +++ b/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult-pinctrl.dtsi @@ -69,4 +69,24 @@ ; }; }; + + mdio_default: mdio_default { + group1 { + pinmux = , + ; + }; + }; + + gmac_rmii: gmac_rmii { + group1 { + pinmux = , + , + , + , + , + , + , + ; + }; + }; }; diff --git a/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.dts b/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.dts index a2fb4216032c..0d24ab577adb 100644 --- a/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.dts +++ b/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.dts @@ -279,6 +279,26 @@ dmas = <&dmac 10 0x10>, <&dmac 11 0x11>; dma-names = "rx", "tx"; status = "okay"; + + eeprom: eeprom@5e { + compatible = "atmel,at24", "atmel,24mac402"; + reg = <0x5e>; + size = <256>; + pagesize = <16>; + address-width = <8>; + timeout = <5>; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + mac_address: mac-address@9a { + reg = <0x9a 6>; + #nvmem-cell-cells = <0>; + }; + }; + }; }; &tc5 { @@ -332,5 +352,30 @@ cs-gpios = <&portc 24 GPIO_ACTIVE_LOW>; dmas = <&dmac 3 0x4>, <&dmac 4 0x5>; dma-names = "rx", "tx"; +}; + +&gmac { + pinctrl-0 = <&gmac_rmii>; + pinctrl-names = "default"; + + nvmem-cells = <&mac_address>; + nvmem-cell-names = "mac-address"; + phy-handle = <&phy>; + status = "okay"; +}; + +&mdio { + pinctrl-0 = <&mdio_default>; + pinctrl-names = "default"; + status = "okay"; + + phy: ethernet-phy@0 { + compatible = "ethernet-phy"; + status = "okay"; + reg = <0>; + }; +}; + +&trng { status = "okay"; }; diff --git a/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.yaml b/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.yaml index 9ac67f872832..f011b29cd948 100644 --- a/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.yaml +++ b/boards/microchip/pic32c/pic32cx_sg61_cult/pic32cx_sg61_cult.yaml @@ -21,6 +21,7 @@ supported: - gpio - hwinfo - i2c + - netif:eth - interrupt_controller - pinctrl - pwm From 2b11b75e92859538f67df35d1c883af404e4031e Mon Sep 17 00:00:00 2001 From: JaeHwan Jin Date: Wed, 19 Aug 2026 15:56:43 +0900 Subject: [PATCH 143/600] drivers: lora: add instantaneous RSSI to the API Add lora_rssi(), an optional driver operation that samples the receiver signal strength once at the bandwidth set by lora_config(). The radio has to be receiving already, and what a driver returns otherwise is not defined. Signed-off-by: JaeHwan Jin --- doc/releases/release-notes-4.5.rst | 1 + include/zephyr/drivers/lora.h | 37 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index e55c7cc4fd4f..cd97813d694d 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -470,6 +470,7 @@ New APIs and options * :c:func:`lora_recv_duty_cycle` * :c:func:`lora_recv_duty_cycle_async` + * :c:func:`lora_rssi` * Management diff --git a/include/zephyr/drivers/lora.h b/include/zephyr/drivers/lora.h index e50ed7f6da90..c41913ad2f01 100644 --- a/include/zephyr/drivers/lora.h +++ b/include/zephyr/drivers/lora.h @@ -297,6 +297,13 @@ typedef int (*lora_api_cad)(const struct device *dev, k_timeout_t timeout); typedef int (*lora_api_cad_async)(const struct device *dev, lora_cad_cb cb, void *user_data); +/** + * @brief Callback API for reading the instantaneous RSSI + * + * @see lora_rssi() for argument descriptions. + */ +typedef int (*lora_api_rssi)(const struct device *dev, int16_t *rssi); + /** * @typedef lora_api_recv_duty_cycle() * @brief Callback API for blocking receive with duty cycling @@ -349,6 +356,8 @@ __subsystem struct lora_driver_api { lora_api_cad cad; /** @driver_ops_optional @copybrief lora_cad_async */ lora_api_cad_async cad_async; + /** @driver_ops_optional @copybrief lora_rssi */ + lora_api_rssi rssi; /** @driver_ops_optional @copybrief lora_recv_duty_cycle_async */ lora_api_recv_duty_cycle_async recv_duty_cycle_async; /** @driver_ops_optional @copybrief lora_recv_duty_cycle */ @@ -525,6 +534,34 @@ static inline int lora_cad_async(const struct device *dev, lora_cad_cb cb, return api->cad_async(dev, cb, user_data); } +/** + * @brief Read the instantaneous RSSI of the current channel + * + * Samples the receiver's signal strength once, at the bandwidth configured by + * @ref lora_config. + * + * The radio must already be receiving, set up by @ref lora_recv_async. The + * value read outside of receive mode is undefined; the driver does not detect + * this. + * + * @param dev LoRa device + * @param rssi Sampled level in dBm + * @return 0 on success + * @return -EBUSY if the modem is in use + * @return -ENOSYS if the operation is not supported by the driver + * @return negative on other errors + */ +static inline int lora_rssi(const struct device *dev, int16_t *rssi) +{ + const struct lora_driver_api *api = DEVICE_API_GET(lora, dev); + + if (api->rssi == NULL) { + return -ENOSYS; + } + + return api->rssi(dev, rssi); +} + /** * @brief Receive data using duty cycling (wake-on-radio) * From e3acaff82842657c37925670f5a63cad2ff947f8 Mon Sep 17 00:00:00 2001 From: JaeHwan Jin Date: Wed, 26 Aug 2026 10:10:35 +0800 Subject: [PATCH 144/600] drivers: lora: loramac-node: guard the SX126x bus sequence One SX126x bus access is three steps: wait for the chip to be ready, run the transfer, then wait for it to go idle again. The idle wait sleeps, so a second thread can enter the sequence in the middle and leave the handshake out of step. No caller has hit this so far. Reading the RSSI of a receive already in progress is the first case where the calling thread and the DIO1 work handler are on the bus at the same time, so take a binary semaphore around the sequence. Nothing inside the sequence re-enters it, so the lock never needs to be recursive. SX127x needs no equivalent: one access there is a single transfer, which the SPI driver already serialises. Signed-off-by: JaeHwan Jin --- drivers/lora/loramac-node/sx126x.c | 6 ++++++ drivers/lora/loramac-node/sx126x_common.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/drivers/lora/loramac-node/sx126x.c b/drivers/lora/loramac-node/sx126x.c index a1de81b0a687..20c5f6c17d42 100644 --- a/drivers/lora/loramac-node/sx126x.c +++ b/drivers/lora/loramac-node/sx126x.c @@ -118,6 +118,8 @@ static int sx126x_spi_transceive(uint8_t *req_tx, uint8_t *req_rx, .count = ARRAY_SIZE(rx_buf) }; + k_sem_take(&dev_data.bus_lock, K_FOREVER); + /* Wake the device if necessary */ SX126xCheckDeviceReady(); @@ -134,6 +136,9 @@ static int sx126x_spi_transceive(uint8_t *req_tx, uint8_t *req_rx, if (req_len >= 1 && req_tx[0] != RADIO_SET_SLEEP) { SX126xWaitOnBusy(); } + + k_sem_give(&dev_data.bus_lock); + return ret; } @@ -441,6 +446,7 @@ static int sx126x_lora_init(const struct device *dev) return -EIO; } + k_sem_init(&dev_data.bus_lock, 1, 1); k_work_init(&dev_data.dio1_irq_work, sx126x_dio1_irq_work_handler); ret = sx126x_variant_init(dev); diff --git a/drivers/lora/loramac-node/sx126x_common.h b/drivers/lora/loramac-node/sx126x_common.h index 81ac661859cc..8b309ae3d722 100644 --- a/drivers/lora/loramac-node/sx126x_common.h +++ b/drivers/lora/loramac-node/sx126x_common.h @@ -10,6 +10,7 @@ #define ZEPHYR_DRIVERS_SX126X_COMMON_H_ #include +#include #include #include #include @@ -53,6 +54,8 @@ struct sx126x_data { struct k_work dio1_irq_work; DioIrqHandler *radio_dio_irq; RadioOperatingModes_t mode; + /* serialises the ready/transfer/busy sequence of one bus access */ + struct k_sem bus_lock; }; void sx126x_reset(struct sx126x_data *dev_data); From 8e8e573908d2153edfba11b8c3306794113477d5 Mon Sep 17 00:00:00 2001 From: JaeHwan Jin Date: Wed, 26 Aug 2026 10:11:27 +0800 Subject: [PATCH 145/600] drivers: lora: implement instantaneous RSSI Wire lora_rssi() to the primitive each backend already exposes: Radio.Rssi() in loramac-node, ral_get_rssi_inst() in LoRa Basics Modem and the GET_RSSI_INST command in the native SX126x driver. The native LR11xx driver and the RYLRxxx modem are left out. LR11xx has the command but no hardware was on hand to test it, and RYLRxxx is an AT command module with no equivalent. Signed-off-by: JaeHwan Jin --- drivers/lora/lora-basics-modem/lbm_common.c | 12 ++++++++++++ drivers/lora/loramac-node/sx126x.c | 1 + drivers/lora/loramac-node/sx127x.c | 1 + drivers/lora/loramac-node/sx12xx_common.c | 13 +++++++++++++ drivers/lora/loramac-node/sx12xx_common.h | 2 ++ drivers/lora/native/sx126x/sx126x.c | 15 +++++++++++++++ 6 files changed, 44 insertions(+) diff --git a/drivers/lora/lora-basics-modem/lbm_common.c b/drivers/lora/lora-basics-modem/lbm_common.c index 7bb845f6d132..2aba0df935ce 100644 --- a/drivers/lora/lora-basics-modem/lbm_common.c +++ b/drivers/lora/lora-basics-modem/lbm_common.c @@ -653,6 +653,17 @@ int lbm_lora_common_init(const struct device *dev) return 0; } +int lbm_lora_rssi(const struct device *dev, int16_t *rssi) +{ + const struct lbm_lora_config_common *config = dev->config; + + if (ral_get_rssi_inst(&config->ralf.ral, rssi) != RAL_STATUS_OK) { + return -EIO; + } + + return 0; +} + DEVICE_API(lora, lbm_lora_api) = { .config = lbm_lora_config, .airtime = lbm_lora_airtime, @@ -661,4 +672,5 @@ DEVICE_API(lora, lbm_lora_api) = { .recv = lbm_lora_recv, .recv_async = lbm_lora_recv_async, .test_cw = lbm_lora_test_cw, + .rssi = lbm_lora_rssi, }; diff --git a/drivers/lora/loramac-node/sx126x.c b/drivers/lora/loramac-node/sx126x.c index 20c5f6c17d42..71d664cd00c7 100644 --- a/drivers/lora/loramac-node/sx126x.c +++ b/drivers/lora/loramac-node/sx126x.c @@ -477,6 +477,7 @@ static DEVICE_API(lora, sx126x_lora_api) = { .recv = sx12xx_lora_recv, .recv_async = sx12xx_lora_recv_async, .test_cw = sx12xx_lora_test_cw, + .rssi = sx12xx_lora_rssi, }; DEVICE_DT_INST_DEFINE(0, &sx126x_lora_init, NULL, &dev_data, diff --git a/drivers/lora/loramac-node/sx127x.c b/drivers/lora/loramac-node/sx127x.c index a7c65ac57a9f..409ce32efea0 100644 --- a/drivers/lora/loramac-node/sx127x.c +++ b/drivers/lora/loramac-node/sx127x.c @@ -633,6 +633,7 @@ static DEVICE_API(lora, sx127x_lora_api) = { .recv = sx12xx_lora_recv, .recv_async = sx12xx_lora_recv_async, .test_cw = sx12xx_lora_test_cw, + .rssi = sx12xx_lora_rssi, }; DEVICE_DT_INST_DEFINE(0, &sx127x_lora_init, NULL, NULL, diff --git a/drivers/lora/loramac-node/sx12xx_common.c b/drivers/lora/loramac-node/sx12xx_common.c index 6724445daf74..30c3a222e67a 100644 --- a/drivers/lora/loramac-node/sx12xx_common.c +++ b/drivers/lora/loramac-node/sx12xx_common.c @@ -434,6 +434,19 @@ int sx12xx_lora_test_cw(const struct device *dev, uint32_t frequency, return 0; } +int sx12xx_lora_rssi(const struct device *dev, int16_t *rssi) +{ + /* + * Deliberately no modem_acquire(): that claims the radio for one + * exclusive operation, and reading the RSSI is a query on the receive + * already in progress. The bus access itself is serialised one layer + * down. + */ + *rssi = Radio.Rssi(MODEM_LORA); + + return 0; +} + int sx12xx_init(const struct device *dev) { atomic_set(&dev_data.modem_usage, 0); diff --git a/drivers/lora/loramac-node/sx12xx_common.h b/drivers/lora/loramac-node/sx12xx_common.h index 686f0a83eddc..eb27a78b350b 100644 --- a/drivers/lora/loramac-node/sx12xx_common.h +++ b/drivers/lora/loramac-node/sx12xx_common.h @@ -40,6 +40,8 @@ int sx12xx_lora_test_cw(const struct device *dev, uint32_t frequency, int8_t tx_power, uint16_t duration); +int sx12xx_lora_rssi(const struct device *dev, int16_t *rssi); + int sx12xx_init(const struct device *dev); #endif /* ZEPHYR_DRIVERS_SX12XX_COMMON_H_ */ diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c index f9e2c0d1383c..c8e682ee84ce 100644 --- a/drivers/lora/native/sx126x/sx126x.c +++ b/drivers/lora/native/sx126x/sx126x.c @@ -1452,6 +1452,20 @@ static uint32_t sx126x_lora_airtime(const struct device *dev, uint32_t data_len) return (t_preamble_us + t_payload_us + 500) / 1000; } +static int sx126x_lora_rssi(const struct device *dev, int16_t *rssi) +{ + uint8_t buf[1]; + int ret; + + ret = sx126x_hal_read_cmd(dev, SX126X_CMD_GET_RSSI_INST, buf, 1); + if (ret == 0) { + /* RSSI is -value/2 dBm */ + *rssi = -((int16_t)buf[0] >> 1); + } + + return ret; +} + static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, int8_t tx_power, uint16_t duration) { @@ -1519,6 +1533,7 @@ static DEVICE_API(lora, sx126x_lora_api) = { .recv_duty_cycle_async = sx126x_lora_recv_duty_cycle_async, .airtime = sx126x_lora_airtime, .test_cw = sx126x_lora_test_cw, + .rssi = sx126x_lora_rssi, }; #ifdef CONFIG_PM_DEVICE From 8a3cba60ebea865f71169698b1d14f52a30d4a1a Mon Sep 17 00:00:00 2001 From: JaeHwan Jin Date: Wed, 19 Aug 2026 16:00:16 +0900 Subject: [PATCH 146/600] drivers: lora: add energy-detection carrier sense Add lora_energy_detect(), which reports whether a channel carries energy above a threshold. Unlike lora_cad() it reacts to any transmitter, not only to a LoRa preamble. No radio has a command for this, so it is built in common code on top of lora_recv_async() and lora_rssi() rather than added as a driver operation. Variants such as tracking the peak level can then be written without touching any backend. The receiver needs time to settle before the first sample is valid, so the wait before sampling and the interval between samples are Kconfig options. The settling time is measured per backend, so it defaults to 0.5 ms on loramac-node and the native driver and to 10 ms otherwise. Signed-off-by: JaeHwan Jin --- doc/releases/release-notes-4.5.rst | 1 + drivers/lora/CMakeLists.txt | 1 + drivers/lora/Kconfig | 19 +++++++++ drivers/lora/lora_common.c | 66 ++++++++++++++++++++++++++++++ include/zephyr/drivers/lora.h | 24 +++++++++++ 5 files changed, 111 insertions(+) create mode 100644 drivers/lora/lora_common.c diff --git a/doc/releases/release-notes-4.5.rst b/doc/releases/release-notes-4.5.rst index cd97813d694d..d63b25d8bb4e 100644 --- a/doc/releases/release-notes-4.5.rst +++ b/doc/releases/release-notes-4.5.rst @@ -470,6 +470,7 @@ New APIs and options * :c:func:`lora_recv_duty_cycle` * :c:func:`lora_recv_duty_cycle_async` + * :c:func:`lora_energy_detect` * :c:func:`lora_rssi` * Management diff --git a/drivers/lora/CMakeLists.txt b/drivers/lora/CMakeLists.txt index 9cd035fcf2b8..fecfe8ea8bb0 100644 --- a/drivers/lora/CMakeLists.txt +++ b/drivers/lora/CMakeLists.txt @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 +zephyr_sources(lora_common.c) zephyr_sources_ifdef(CONFIG_LORA_SHELL shell.c) # zephyr-keep-sorted-start diff --git a/drivers/lora/Kconfig b/drivers/lora/Kconfig index 657bdb9ce28c..e31c08c13714 100644 --- a/drivers/lora/Kconfig +++ b/drivers/lora/Kconfig @@ -52,6 +52,25 @@ config LORA_SHELL help Enable LoRa Shell for testing. +config LORA_RSSI_SETTLE_US + int "RSSI settling time in microseconds" + default 500 if LORA_MODULE_BACKEND_LORAMAC_NODE + default 500 if LORA_MODULE_BACKEND_NATIVE + default 10000 + help + Time to wait after entering receive mode before the RSSI is read, + to let the receiver settle. How long a radio needs varies with the + driver: one that sleeps between accesses spends part of this time + in its wake-up calibration, and reads taken too early report the + noise floor. The defaults come from an SX1262: loramac-node and the + native driver are ready in 0.5 ms, the Basics Modem glue needs 7 ms. + +config LORA_ENERGY_DETECT_SAMPLE_INTERVAL_US + int "Energy detection sample interval in microseconds" + default 500 + help + Time to sleep between two RSSI samples while sensing the channel. + config LORA_INIT_PRIORITY int "LoRa initialization priority" default 90 diff --git a/drivers/lora/lora_common.c b/drivers/lora/lora_common.c new file mode 100644 index 000000000000..2b4e9d6b50ea --- /dev/null +++ b/drivers/lora/lora_common.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static void lora_ed_discard(const struct device *dev, uint8_t *data, uint16_t size, int16_t rssi, + int8_t snr, void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(data); + ARG_UNUSED(size); + ARG_UNUSED(rssi); + ARG_UNUSED(snr); + ARG_UNUSED(user_data); +} + +int lora_energy_detect(const struct device *dev, int16_t rssi_threshold, k_timeout_t duration) +{ + k_timepoint_t expiry; + bool busy = false; + int ret, stop; + + if (K_TIMEOUT_EQ(duration, K_NO_WAIT) || K_TIMEOUT_EQ(duration, K_FOREVER)) { + return -EINVAL; + } + + ret = lora_recv_async(dev, lora_ed_discard, NULL); + if (ret < 0) { + return ret; + } + + k_sleep(K_USEC(CONFIG_LORA_RSSI_SETTLE_US)); + + expiry = sys_timepoint_calc(duration); + + while (!sys_timepoint_expired(expiry)) { + int16_t rssi; + + ret = lora_rssi(dev, &rssi); + if (ret < 0) { + break; + } + + if (rssi >= rssi_threshold) { + busy = true; + break; + } + + k_sleep(K_USEC(CONFIG_LORA_ENERGY_DETECT_SAMPLE_INTERVAL_US)); + } + + stop = lora_recv_async(dev, NULL, NULL); + if (stop < 0) { + return stop; + } + + if (ret < 0) { + return ret; + } + + return busy ? 1 : 0; +} diff --git a/include/zephyr/drivers/lora.h b/include/zephyr/drivers/lora.h index c41913ad2f01..6b2019028b41 100644 --- a/include/zephyr/drivers/lora.h +++ b/include/zephyr/drivers/lora.h @@ -562,6 +562,30 @@ static inline int lora_rssi(const struct device *dev, int16_t *rssi) return api->rssi(dev, rssi); } +/** + * @brief Perform energy-detection carrier sense + * + * Puts the radio into receive mode and samples the RSSI repeatedly for + * @p duration. The channel is reported busy as soon as one sample reaches + * @p rssi_threshold, and clear if the window elapses without that happening. + * + * Unlike @ref lora_cad this reacts to any energy on the channel, not only to + * a LoRa preamble. Sensing happens at the bandwidth set by @ref lora_config. + * + * @note This is a blocking call. + * + * @param dev LoRa device + * @param rssi_threshold Level in dBm at or above which the channel is busy + * @param duration Carrier sense window, neither K_NO_WAIT nor K_FOREVER + * @return 0 if the channel is clear + * @return 1 if the channel is busy + * @return -EINVAL if @p duration is K_NO_WAIT or K_FOREVER + * @return -EBUSY if the modem is in use + * @return -ENOSYS if the driver supports neither RSSI nor asynchronous receive + * @return negative on other errors + */ +int lora_energy_detect(const struct device *dev, int16_t rssi_threshold, k_timeout_t duration); + /** * @brief Receive data using duty cycling (wake-on-radio) * From d3acec8a0d890c21e5523f666716b53c7429a4e9 Mon Sep 17 00:00:00 2001 From: JaeHwan Jin Date: Wed, 19 Aug 2026 16:00:48 +0900 Subject: [PATCH 147/600] drivers: lora: shell: add rssi and energy_detect commands Both commands enter receive mode first and leave it afterwards, since the RSSI only means anything while the radio is receiving. Signed-off-by: JaeHwan Jin --- drivers/lora/shell.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/drivers/lora/shell.c b/drivers/lora/shell.c index e04e1d2016a0..0bdc1aef5769 100644 --- a/drivers/lora/shell.c +++ b/drivers/lora/shell.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -316,6 +317,84 @@ static int cmd_lora_test_cw(const struct shell *sh, return 0; } +static void lora_shell_discard(const struct device *dev, uint8_t *data, uint16_t size, int16_t rssi, + int8_t snr, void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(data); + ARG_UNUSED(size); + ARG_UNUSED(rssi); + ARG_UNUSED(snr); + ARG_UNUSED(user_data); +} + +static int cmd_lora_rssi(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + int16_t rssi; + int ret, stop; + + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + dev = get_configured_modem(sh); + if (!dev) { + return -ENODEV; + } + + ret = lora_recv_async(dev, lora_shell_discard, NULL); + if (ret < 0) { + shell_error(sh, "Failed to enter receive mode: %i", ret); + return ret; + } + + k_sleep(K_USEC(CONFIG_LORA_RSSI_SETTLE_US)); + + ret = lora_rssi(dev, &rssi); + + stop = lora_recv_async(dev, NULL, NULL); + if (stop < 0) { + shell_error(sh, "Failed to leave receive mode: %i", stop); + return stop; + } + + if (ret < 0) { + shell_error(sh, "LoRa RSSI read failed: %i", ret); + return ret; + } + + shell_print(sh, "RSSI: %d dBm", rssi); + + return 0; +} + +static int cmd_lora_energy_detect(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + long threshold, duration; + int ret; + + dev = get_configured_modem(sh); + if (!dev) { + return -ENODEV; + } + + if (parse_long_range(&threshold, sh, argv[1], "threshold", INT16_MIN, INT16_MAX) < 0 || + parse_long_range(&duration, sh, argv[2], "duration", 1, INT32_MAX) < 0) { + return -EINVAL; + } + + ret = lora_energy_detect(dev, (int16_t)threshold, K_MSEC((uint32_t)duration)); + if (ret < 0) { + shell_error(sh, "LoRa energy detect failed: %i", ret); + return ret; + } + + shell_print(sh, "Channel %s", ret == 1 ? "busy" : "clear"); + + return 0; +} + SHELL_STATIC_SUBCMD_SET_CREATE(sub_lora, SHELL_CMD(config, NULL, SHELL_HELP("Configure the LoRa radio", @@ -332,6 +411,13 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_lora, SHELL_HELP("Send a continuous wave", " "), cmd_lora_test_cw, 4, 0), + SHELL_CMD_ARG(rssi, NULL, + SHELL_HELP("Read the instantaneous RSSI", "No arguments"), + cmd_lora_rssi, 1, 0), + SHELL_CMD_ARG(energy_detect, NULL, + SHELL_HELP("Energy-detection carrier sense", + " "), + cmd_lora_energy_detect, 3, 0), SHELL_SUBCMD_SET_END /* Array terminated. */ ); From c3109b9ebe67864b751acbb625922114fa002dae Mon Sep 17 00:00:00 2001 From: Antoni Duda Date: Fri, 19 Jun 2026 15:25:44 +0200 Subject: [PATCH 148/600] usb: device_next: cdc_acm: rx throughput improvements Instead of storing RX data in ring_buf and copying it, store it in k_fifo of net_bufs allocated from per instance buf pool. Signed-off-by: Antoni Duda --- subsys/usb/device_next/class/usbd_cdc_acm.c | 147 +++++++++++--------- 1 file changed, 85 insertions(+), 62 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c index b487c0ffd546..d37adf4275fa 100644 --- a/subsys/usb/device_next/class/usbd_cdc_acm.c +++ b/subsys/usb/device_next/class/usbd_cdc_acm.c @@ -45,10 +45,16 @@ LOG_MODULE_REGISTER(usbd_cdc_acm, CONFIG_USBD_CDC_ACM_LOG_LEVEL); #define CDC_ACM_CLASS_SUSPENDED 1 #define CDC_ACM_IRQ_RX_ENABLED 2 #define CDC_ACM_IRQ_TX_ENABLED 3 -#define CDC_ACM_RX_FIFO_BUSY 4 -#define CDC_ACM_TX_FIFO_BUSY 5 +#define CDC_ACM_TX_FIFO_BUSY 4 -struct cdc_acm_uart_fifo { +struct cdc_acm_rx_uart_fifo { + struct k_fifo *bufs; + struct net_buf_pool *pool; + bool irq; + bool altered; +}; + +struct cdc_acm_tx_uart_fifo { struct ring_buf *rb; bool irq; bool altered; @@ -117,8 +123,8 @@ struct cdc_acm_uart_data { void *cb_data; /* UART API IRQ callback work */ struct k_work irq_cb_work; - struct cdc_acm_uart_fifo rx_fifo; - struct cdc_acm_uart_fifo tx_fifo; + struct cdc_acm_rx_uart_fifo rx_fifo; + struct cdc_acm_tx_uart_fifo tx_fifo; /* USBD CDC ACM TX fifo work */ struct k_work_delayable tx_fifo_work; /* USBD CDC ACM RX fifo work */ @@ -283,6 +289,7 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data, const struct device *dev = usbd_class_get_private(c_data); struct cdc_acm_uart_data *data = dev->data; struct udc_buf_info *bi; + int ret = 0; bi = udc_get_buf_info(buf); if (err) { @@ -294,10 +301,6 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data, bi->ep, buf->len); } - if (bi->ep == cdc_acm_get_bulk_out(c_data)) { - atomic_clear_bit(&data->state, CDC_ACM_RX_FIFO_BUSY); - } - if (bi->ep == cdc_acm_get_bulk_in(c_data)) { atomic_clear_bit(&data->state, CDC_ACM_TX_FIFO_BUSY); } @@ -311,16 +314,21 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data, if (bi->ep == cdc_acm_get_bulk_out(c_data)) { /* RX transfer completion */ - size_t done; LOG_HEXDUMP_INF(buf->data, buf->len, ""); - done = ring_buf_put(data->rx_fifo.rb, buf->data, buf->len); - if (done && data->cb) { + if (buf->len == 0) { + /* Drop transfer with zero length */ + net_buf_unref(buf); + cdc_acm_work_submit(&data->rx_fifo_work); + goto ep_buf_already_handled; + } + + k_fifo_put(data->rx_fifo.bufs, buf); + if (data->cb) { cdc_acm_work_submit(&data->irq_cb_work); } - atomic_clear_bit(&data->state, CDC_ACM_RX_FIFO_BUSY); - cdc_acm_work_submit(&data->rx_fifo_work); + goto ep_buf_already_handled; } if (bi->ep == cdc_acm_get_bulk_in(c_data)) { @@ -354,7 +362,9 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data, } ep_request_error: - return usbd_ep_buf_free(uds_ctx, buf); + ret = usbd_ep_buf_free(uds_ctx, buf); +ep_buf_already_handled: + return ret; } static void usbd_cdc_acm_update(struct usbd_class_data *const c_data, @@ -713,8 +723,6 @@ static void cdc_acm_rx_fifo_handler(struct k_work *work) struct cdc_acm_uart_data *data; const struct cdc_acm_uart_config *cfg; struct usbd_class_data *c_data; - struct net_buf *buf; - int ret; data = CONTAINER_OF(work, struct cdc_acm_uart_data, rx_fifo_work); cfg = data->dev->config; @@ -726,29 +734,25 @@ static void cdc_acm_rx_fifo_handler(struct k_work *work) return; } - if (ring_buf_space_get(data->rx_fifo.rb) < cdc_acm_get_bulk_mps(c_data)) { - LOG_INF("RX buffer too small, throttle"); - return; - } - - if (atomic_test_and_set_bit(&data->state, CDC_ACM_RX_FIFO_BUSY)) { - LOG_WRN("RX transfer already in progress"); - return; - } + while (true) { + struct udc_buf_info *bi; + struct net_buf *buf; - buf = cdc_acm_buf_alloc(c_data, cdc_acm_get_bulk_out(c_data)); - if (buf == NULL) { - return; - } + buf = net_buf_alloc(data->rx_fifo.pool, K_NO_WAIT); + if (buf == NULL) { + break; + } - /* Shrink the buffer size if operating on a full speed bus */ - buf->size = MIN(cdc_acm_get_bulk_mps(c_data), buf->size); + /* Shrink the buffer size if operating on a full speed bus */ + buf->size = MIN(cdc_acm_get_bulk_mps(c_data), buf->size); - ret = usbd_ep_enqueue(c_data, buf); - if (ret) { - LOG_ERR("Failed to enqueue net_buf for 0x%02x", - cdc_acm_get_bulk_out(c_data)); - net_buf_unref(buf); + bi = udc_get_buf_info(buf); + bi->ep = cdc_acm_get_bulk_out(c_data); + if (usbd_ep_enqueue(c_data, buf) != 0) { + LOG_ERR("Failed to enqueue net_buf for 0x%02x", bi->ep); + net_buf_unref(buf); + break; + } } } @@ -778,15 +782,12 @@ static void cdc_acm_irq_rx_enable(const struct device *dev) atomic_set_bit(&data->state, CDC_ACM_IRQ_RX_ENABLED); /* Permit buffer to be drained regardless of USB state */ - if (!ring_buf_is_empty(data->rx_fifo.rb)) { + if (!k_fifo_is_empty(data->rx_fifo.bufs)) { LOG_INF("rx_en: trigger irq_cb_work"); cdc_acm_work_submit(&data->irq_cb_work); } - if (!atomic_test_bit(&data->state, CDC_ACM_RX_FIFO_BUSY)) { - LOG_INF("rx_en: trigger rx_fifo_work"); - cdc_acm_work_submit(&data->rx_fifo_work); - } + cdc_acm_work_submit(&data->rx_fifo_work); } static void cdc_acm_irq_rx_disable(const struct device *dev) @@ -828,10 +829,11 @@ static int cdc_acm_fifo_read(const struct device *dev, const int size) { struct cdc_acm_uart_data *const data = dev->data; - uint32_t len; + struct net_buf *head; + int offset = 0; + int len; - LOG_INF("UART dev %p size %d length %u", - dev, size, ring_buf_size_get(data->rx_fifo.rb)); + LOG_INF("UART dev %p size %d", dev, size); if (!check_wq_ctx(dev)) { LOG_WRN("Invoked by inappropriate context"); @@ -839,12 +841,24 @@ static int cdc_acm_fifo_read(const struct device *dev, return 0; } - len = ring_buf_get(data->rx_fifo.rb, rx_data, size); - if (len) { - data->rx_fifo.altered = true; + while (true) { + head = k_fifo_peek_head(data->rx_fifo.bufs); + if (head == NULL || offset == size) { + break; + } + + len = MIN(size - offset, head->len); + memcpy(&rx_data[offset], net_buf_pull_mem(head, len), len); + offset += len; + + if (head->len == 0) { + head = k_fifo_get(data->rx_fifo.bufs, K_NO_WAIT); + net_buf_unref(head); + data->rx_fifo.altered = true; + } } - return len; + return offset; } static int cdc_acm_irq_tx_ready(const struct device *dev) @@ -907,7 +921,7 @@ static void cdc_acm_irq_update(const struct device *dev) } if (atomic_test_bit(&data->state, CDC_ACM_IRQ_RX_ENABLED) && - !ring_buf_is_empty(data->rx_fifo.rb)) { + !k_fifo_is_empty(data->rx_fifo.bufs)) { data->rx_fifo.irq = true; } else { data->rx_fifo.irq = false; @@ -974,7 +988,7 @@ static void cdc_acm_irq_cb_handler(struct k_work *work) } if (atomic_test_bit(&data->state, CDC_ACM_IRQ_RX_ENABLED) && - !ring_buf_is_empty(data->rx_fifo.rb)) { + !k_fifo_is_empty(data->rx_fifo.bufs)) { LOG_DBG("rx irq pending, submit irq_cb_work"); cdc_acm_work_submit(&data->irq_cb_work); } @@ -999,20 +1013,22 @@ static void cdc_acm_irq_callback_set(const struct device *dev, static int cdc_acm_poll_in(const struct device *dev, unsigned char *const c) { struct cdc_acm_uart_data *const data = dev->data; - uint32_t len; - int ret = -1; + struct net_buf *head; - if (ring_buf_is_empty(data->rx_fifo.rb)) { - return ret; + head = k_fifo_peek_head(data->rx_fifo.bufs); + if (head == NULL) { + return -1; } - len = ring_buf_get(data->rx_fifo.rb, c, 1); - if (len) { + *c = net_buf_pull_u8(head); + + if (head->len == 0) { + head = k_fifo_get(data->rx_fifo.bufs, K_NO_WAIT); + net_buf_unref(head); cdc_acm_work_submit(&data->rx_fifo_work); - ret = 0; } - return ret; + return 0; } static void cdc_acm_poll_out(const struct device *dev, const unsigned char c) @@ -1149,7 +1165,6 @@ static int usbd_cdc_acm_preinit(const struct device *dev) struct cdc_acm_uart_data *const data = dev->data; ring_buf_reset(data->tx_fifo.rb); - ring_buf_reset(data->rx_fifo.rb); k_work_init_delayable(&data->tx_fifo_work, cdc_acm_tx_fifo_handler); k_work_init(&data->rx_fifo_work, cdc_acm_rx_fifo_handler); @@ -1359,6 +1374,9 @@ const static struct usb_desc_header *cdc_acm_hs_desc_##n[] = { \ (struct usb_desc_header *) &cdc_acm_desc_##n.nil_desc, \ }; +#define CDC_ACM_RX_BUF_COUNT(n) \ + DIV_ROUND_UP(DT_INST_PROP(n, rx_fifo_size), USBD_MAX_BULK_MPS) + #define USBD_CDC_ACM_DT_DEVICE_DEFINE(n) \ BUILD_ASSERT(DT_INST_ON_BUS(n, usb), \ "node " DT_NODE_PATH(DT_DRV_INST(n)) \ @@ -1379,8 +1397,10 @@ const static struct usb_desc_header *cdc_acm_hs_desc_##n[] = { \ USBD_DUT_STRING_INTERFACE); \ )) \ \ - RING_BUF_DECLARE(cdc_acm_rb_rx_##n, DT_INST_PROP(n, rx_fifo_size)); \ RING_BUF_DECLARE(cdc_acm_rb_tx_##n, DT_INST_PROP(n, tx_fifo_size)); \ + UDC_BUF_POOL_DEFINE(cdc_acm_rx_pool_##n, \ + CDC_ACM_RX_BUF_COUNT(n), USBD_MAX_BULK_MPS, \ + sizeof(struct udc_buf_info), NULL); \ \ static const struct cdc_acm_uart_config uart_config_##n = { \ .c_data = &cdc_acm_##n, \ @@ -1393,10 +1413,13 @@ const static struct usb_desc_header *cdc_acm_hs_desc_##n[] = { \ (cdc_acm_hs_desc_##n,), (NULL,)) \ }; \ \ + static struct k_fifo cdc_acm_uart_rx_fifo##n = \ + Z_FIFO_INITIALIZER(cdc_acm_uart_rx_fifo##n); \ static struct cdc_acm_uart_data uart_data_##n = { \ .dev = DEVICE_DT_GET(DT_DRV_INST(n)), \ .line_coding = CDC_ACM_DEFAULT_LINECODING, \ - .rx_fifo.rb = &cdc_acm_rb_rx_##n, \ + .rx_fifo.bufs = &cdc_acm_uart_rx_fifo##n, \ + .rx_fifo.pool = &cdc_acm_rx_pool_##n, \ .tx_fifo.rb = &cdc_acm_rb_tx_##n, \ .flow_ctrl = DT_INST_PROP(n, hw_flow_control), \ .notif_sem = Z_SEM_INITIALIZER(uart_data_##n.notif_sem, 0, 1), \ From a8ced2c9dbe0db8684a47b2362caff24a7609efc Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 6 Aug 2026 19:42:23 +0200 Subject: [PATCH 149/600] usb: device_next: fix CDC ACM pool description After the changes to the RX transfer path, there is a pool per instance for the RX path and OUT transfers. The existing bulk endpoints pool and relevant Kconfig options are limited to the bulk IN endpoint. Update their descriptions. Signed-off-by: Johann Fischer --- subsys/usb/device_next/class/Kconfig.cdc_acm | 6 +++--- subsys/usb/device_next/class/usbd_cdc_acm.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/subsys/usb/device_next/class/Kconfig.cdc_acm b/subsys/usb/device_next/class/Kconfig.cdc_acm index d0449e2c5721..682c1b08276c 100644 --- a/subsys/usb/device_next/class/Kconfig.cdc_acm +++ b/subsys/usb/device_next/class/Kconfig.cdc_acm @@ -33,8 +33,8 @@ config USBD_CDC_ACM_BUF_POOL bool "Use dedicated buffer pool" default y if !USBD_MAX_SPEED_FULL help - Use a dedicated buffer pool whose size is based on the number of CDC - ACM instances and the size of the bulk endpoints. When disabled, the + Use a dedicated TX buffer pool whose size is based on the number of CDC + ACM instances and the size of the bulk IN endpoint. When disabled, the implementation uses the UDC driver's pool. config USBD_CDC_ACM_BUF_POOL_SIZE @@ -45,7 +45,7 @@ config USBD_CDC_ACM_BUF_POOL_SIZE help Size of each buffer in the CDC ACM buffer pool. This should be large enough to accommodate the maximum packet size for - the endpoints. + the IN endpoint. config USBD_CDC_ACM_TX_DELAY_MS int diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c index d37adf4275fa..f829c07a7ed9 100644 --- a/subsys/usb/device_next/class/usbd_cdc_acm.c +++ b/subsys/usb/device_next/class/usbd_cdc_acm.c @@ -138,7 +138,7 @@ static void cdc_acm_irq_rx_enable(const struct device *dev); #if CONFIG_USBD_CDC_ACM_BUF_POOL UDC_BUF_POOL_DEFINE(cdc_acm_ep_pool, - DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT), CONFIG_USBD_CDC_ACM_BUF_POOL_SIZE, sizeof(struct udc_buf_info), NULL); @@ -164,7 +164,7 @@ static struct net_buf *cdc_acm_buf_alloc(struct usbd_class_data *const c_data, } #else /* - * The required buffer is 128 bytes per instance on a full-speed device. Use + * The required IN buffer is 64 bytes per instance on a full-speed device. Use * common (UDC) buffer, as this results in a smaller footprint. */ static struct net_buf *cdc_acm_buf_alloc(struct usbd_class_data *const c_data, From 3820679807680b36d13c0693b5e0f9ad51dabc28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 30 Jul 2026 23:25:45 +0200 Subject: [PATCH 150/600] drivers: i2c: add VIRTIO I2C adapter driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a driver for the VIRTIO I2C adapter device (virtio spec 1.3, section 5.16), letting a Zephyr guest drive an I2C bus exposed by a hypervisor or a vhost-user backend. One i2c_msg is one descriptor chain, and the whole batch is queued before the device is notified, so a multi-message transfer costs one round trip instead of one per message. FAIL_NEXT groups the messages the device runs back to back; a message that ends in a stop closes its group, so a failure takes the rest of that group with it. Every operation is a round trip to the device, so the I2C API can only be used from a thread. VIRTIO_I2C_F_ZERO_LENGTH_REQUEST is mandatory, so a device that does not offer it is refused. 10-bit addressing is not supported, and the bus speed is accepted and ignored. Assisted-by: Claude Code:opus-5 Signed-off-by: Benjamin Cabé --- drivers/i2c/CMakeLists.txt | 1 + drivers/i2c/Kconfig | 1 + drivers/i2c/Kconfig.virtio | 31 ++ drivers/i2c/i2c_virtio.c | 342 +++++++++++++++++++++ dts/bindings/i2c/virtio,i2c.yaml | 34 ++ tests/drivers/build_all/i2c/tests.yaml | 7 + tests/drivers/build_all/i2c/virtio.overlay | 18 ++ 7 files changed, 434 insertions(+) create mode 100644 drivers/i2c/Kconfig.virtio create mode 100644 drivers/i2c/i2c_virtio.c create mode 100644 dts/bindings/i2c/virtio,i2c.yaml create mode 100644 tests/drivers/build_all/i2c/virtio.overlay diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 0a4677bd4c99..65c46935c141 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -94,6 +94,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SMARTBOND i2c_smartbond.c) zephyr_library_sources_ifdef(CONFIG_I2C_SY1XX i2c_sy1xx.c) zephyr_library_sources_ifdef(CONFIG_I2C_TCA954X i2c_tca954x.c) zephyr_library_sources_ifdef(CONFIG_I2C_TELINK_B91 i2c_b91.c) +zephyr_library_sources_ifdef(CONFIG_I2C_VIRTIO i2c_virtio.c) zephyr_library_sources_ifdef(CONFIG_I2C_WCH i2c_wch.c) zephyr_library_sources_ifdef(CONFIG_I2C_XEC i2c_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_I2C_XEC_V2 i2c_mchp_xec_v2.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index d5cea67a4d20..672bd2a1f502 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -199,6 +199,7 @@ source "drivers/i2c/Kconfig.stm32" source "drivers/i2c/Kconfig.sy1xx" source "drivers/i2c/Kconfig.tca954x" source "drivers/i2c/Kconfig.test" +source "drivers/i2c/Kconfig.virtio" source "drivers/i2c/Kconfig.wch" source "drivers/i2c/Kconfig.xilinx_axi" # zephyr-keep-sorted-stop diff --git a/drivers/i2c/Kconfig.virtio b/drivers/i2c/Kconfig.virtio new file mode 100644 index 000000000000..99bfa26e8d80 --- /dev/null +++ b/drivers/i2c/Kconfig.virtio @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +config I2C_VIRTIO + bool "VIRTIO I2C adapter driver" + default y + depends on DT_HAS_VIRTIO_I2C_ENABLED + select VIRTIO + help + Enable driver for VIRTIO I2C adapter devices. + + Every transfer is a round trip to the virtio device, so the I2C API + can only be used from a thread. + +if I2C_VIRTIO + +config I2C_VIRTIO_MAX_MSGS + int "Messages submitted per batch" + default 8 + range 1 255 + help + How many messages of one i2c_transfer the driver queues before + notifying the device. A transfer longer than this is split into + several batches, which is correct but costs one extra round trip + apiece, so the default is set well above the two messages a register + read takes. + + Each slot costs a handful of bytes of RAM, and sizes the request + virtqueue at three descriptors per message. + +endif # I2C_VIRTIO diff --git a/drivers/i2c/i2c_virtio.c b/drivers/i2c/i2c_virtio.c new file mode 100644 index 000000000000..2b2f06580f78 --- /dev/null +++ b/drivers/i2c/i2c_virtio.c @@ -0,0 +1,342 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Driver for the VIRTIO I2C adapter device (virtio spec 1.3, section 5.16). + * + * The wire protocol puts one struct i2c_msg in one descriptor chain, so a + * transfer of N messages is N chains. They are all queued before the device is + * notified, which costs a multi-message transfer one round trip instead of one + * per message, and every register read is two messages. + * + * Every operation is a round trip to the device, so none of the I2C API calls + * may be issued from an ISR. + */ + +#define DT_DRV_COMPAT virtio_i2c + +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(i2c_virtio, CONFIG_I2C_LOG_LEVEL); + +#define VIRTIO_I2C_REQUESTQ 0 + +/* Mandatory: zero-length requests, and M_RD carrying the direction. */ +#define VIRTIO_I2C_F_ZERO_LENGTH_REQUEST 0 + +/* Fail the following request too, if this one fails. */ +#define VIRTIO_I2C_FLAGS_FAIL_NEXT BIT(0) +/* This message is a read; otherwise it is a write. */ +#define VIRTIO_I2C_FLAGS_M_RD BIT(1) + +#define VIRTIO_I2C_MSG_OK 0 + +struct virtio_i2c_out_hdr { + uint16_t addr; + uint16_t padding; + uint32_t flags; +} __packed; + +struct virtio_i2c_in_hdr { + uint8_t status; +} __packed; + +/* Per-message state, one slot per message of the batch in flight. */ +struct i2c_virtio_slot { + struct virtio_i2c_out_hdr out_hdr; + struct virtio_i2c_in_hdr in_hdr; + /* The completion callback is handed a slot and nothing else. */ + const struct device *dev; +}; + +struct i2c_virtio_config { + const struct device *vdev; +}; + +struct i2c_virtio_data { + /* serializes the batch in flight and the slots it uses */ + struct k_mutex lock; + /* given once per completed chain, taken once per queued chain */ + struct k_sem done; + struct virtq *requestq; + /* outcome of the batch in flight, written by the completion callback */ + int status; + struct i2c_virtio_slot slots[CONFIG_I2C_VIRTIO_MAX_MSGS]; +}; + +/* + * Runs on the virtio ISR. Chains may come back out of order -- legal, and + * harmless here, because a message is judged as it returns and only a failure + * writes to the shared status. + */ +static void i2c_virtio_chain_cb(void *opaque, uint32_t used_len) +{ + struct i2c_virtio_slot *slot = opaque; + struct i2c_virtio_data *data = slot->dev->data; + + if (used_len < sizeof(slot->in_hdr)) { + LOG_ERR("short response (%u bytes)", used_len); + data->status = -EIO; + } else if (slot->in_hdr.status != VIRTIO_I2C_MSG_OK) { + LOG_DBG("message rejected by the device (status %u)", slot->in_hdr.status); + data->status = -EIO; + } else { + /* Success: leave the shared status alone. */ + } + + k_sem_give(&data->done); +} + +/* Queue one message as a chain, without notifying the device. */ +static int i2c_virtio_queue_msg(struct i2c_virtio_data *data, struct i2c_virtio_slot *slot, + struct i2c_msg *msg, uint16_t addr, bool last, + virtq_receive_callback cb) +{ + /* Device-readable buffers must come first in the array. */ + struct virtq_buf bufs[3]; + uint16_t nbufs = 0; + uint16_t readable; + uint32_t flags = 0; + + if (i2c_is_read_op(msg)) { + flags |= VIRTIO_I2C_FLAGS_M_RD; + } + /* + * FAIL_NEXT groups the messages the device runs back to back, with a + * restart rather than a stop between them, and is cleared on the last + * message of each group: a stop closes a group, and so does the end of + * the transfer. + */ + if (!last && !i2c_is_stop_op(msg)) { + flags |= VIRTIO_I2C_FLAGS_FAIL_NEXT; + } + + /* The address goes out shifted, leaving room for the R/W bit. */ + slot->out_hdr.addr = sys_cpu_to_le16(addr << 1); + slot->out_hdr.padding = 0; + slot->out_hdr.flags = sys_cpu_to_le32(flags); + slot->in_hdr.status = 0; + + bufs[nbufs++] = (struct virtq_buf){.addr = &slot->out_hdr, .len = sizeof(slot->out_hdr)}; + + if (msg->len > 0) { + bufs[nbufs++] = (struct virtq_buf){.addr = msg->buf, .len = msg->len}; + } + + /* The payload is device-readable on a write, device-writable on a read. */ + readable = i2c_is_read_op(msg) ? 1 : nbufs; + + bufs[nbufs++] = (struct virtq_buf){.addr = &slot->in_hdr, .len = sizeof(slot->in_hdr)}; + + return virtq_add_buffer_chain(data->requestq, bufs, nbufs, readable, cb, slot, K_NO_WAIT); +} + +static int i2c_virtio_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, + uint16_t addr) +{ + const struct i2c_virtio_config *cfg = dev->config; + struct i2c_virtio_data *data = dev->data; + int ret = 0; + + if (num_msgs == 0) { + return 0; + } + + for (uint8_t i = 0; i < num_msgs; i++) { + if (msgs[i].flags & I2C_MSG_ADDR_10_BITS) { + LOG_ERR("10-bit addressing is not supported"); + return -ENOTSUP; + } + if (msgs[i].len > 0 && msgs[i].buf == NULL) { + return -EINVAL; + } + } + + k_mutex_lock(&data->lock, K_FOREVER); + + for (uint16_t base = 0; base < num_msgs;) { + uint16_t batch = MIN(CONFIG_I2C_VIRTIO_MAX_MSGS, num_msgs - base); + uint16_t queued = 0; + + /* + * A batch ends where its first group does, so a group that + * fails takes the rest of the transfer with it: nothing past + * the stop is ever queued. + */ + for (uint16_t i = 0; i < batch; i++) { + if (i2c_is_stop_op(&msgs[base + i])) { + batch = i + 1; + break; + } + } + + /* + * Cleared before the first chain is queued: the device may + * complete one as soon as it is made available. + */ + data->status = 0; + + for (uint16_t i = 0; i < batch; i++) { + /* + * "last" is per transfer, not per batch: a group + * longer than a batch has to keep FAIL_NEXT set + * across the seam. + */ + bool last = (base + i) == (num_msgs - 1); + + ret = i2c_virtio_queue_msg(data, &data->slots[i], &msgs[base + i], addr, + last, i2c_virtio_chain_cb); + if (ret != 0) { + LOG_ERR("failed to queue message %u: %d", base + i, ret); + break; + } + queued++; + } + + if (queued > 0) { + virtio_notify_virtqueue(cfg->vdev, VIRTIO_I2C_REQUESTQ); + } + + /* + * Wait for every chain that made it onto the queue, even when + * one failed to queue: the device owns those buffers until it + * returns them, and abandoning them would corrupt the slots the + * next transfer reuses. + */ + for (uint16_t i = 0; i < queued; i++) { + k_sem_take(&data->done, K_FOREVER); + } + + if (ret != 0) { + goto out; + } + + ret = data->status; + if (ret != 0) { + goto out; + } + + base += batch; + } + +out: + k_mutex_unlock(&data->lock); + return ret; +} + +static int i2c_virtio_configure(const struct device *dev, uint32_t config) +{ + ARG_UNUSED(dev); + + if (config & I2C_ADDR_10_BITS) { + return -ENOTSUP; + } + if (!(config & I2C_MODE_CONTROLLER)) { + return -ENOTSUP; + } + /* + * The device has no bus-speed control -- there is no bus. Any speed is + * accepted and ignored, so that drivers written for real parts, which + * configure a speed as a matter of course, bind unmodified. + */ + return 0; +} + +static int i2c_virtio_get_config(const struct device *dev, uint32_t *config) +{ + ARG_UNUSED(dev); + + *config = I2C_MODE_CONTROLLER | I2C_SPEED_SET(I2C_SPEED_STANDARD); + return 0; +} + +static DEVICE_API(i2c, i2c_virtio_api) = { + .configure = i2c_virtio_configure, + .get_config = i2c_virtio_get_config, + .transfer = i2c_virtio_transfer, +}; + +static uint16_t i2c_virtio_enum_queues_cb(uint16_t q_index, uint16_t q_size_max, void *unused) +{ + ARG_UNUSED(unused); + + if (q_index != VIRTIO_I2C_REQUESTQ) { + return 0; + } + /* Up to three descriptors per message, for a whole batch at once. */ + return MIN(NHPOT(3 * CONFIG_I2C_VIRTIO_MAX_MSGS), q_size_max); +} + +static int i2c_virtio_init(const struct device *dev) +{ + const struct i2c_virtio_config *cfg = dev->config; + struct i2c_virtio_data *data = dev->data; + int ret; + + if (!device_is_ready(cfg->vdev)) { + LOG_ERR_DEVICE_NOT_READY(cfg->vdev); + return -ENODEV; + } + + k_mutex_init(&data->lock); + k_sem_init(&data->done, 0, CONFIG_I2C_VIRTIO_MAX_MSGS); + for (uint16_t i = 0; i < CONFIG_I2C_VIRTIO_MAX_MSGS; i++) { + data->slots[i].dev = dev; + } + + if (!virtio_read_device_feature_bit(cfg->vdev, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST)) { + LOG_ERR("device does not offer VIRTIO_I2C_F_ZERO_LENGTH_REQUEST"); + return -ENOTSUP; + } + + ret = virtio_write_driver_feature_bit(cfg->vdev, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST, true); + if (ret != 0) { + LOG_ERR("failed to accept VIRTIO_I2C_F_ZERO_LENGTH_REQUEST: %d", ret); + return ret; + } + + ret = virtio_commit_feature_bits(cfg->vdev); + if (ret != 0) { + LOG_ERR("virtio_commit_feature_bits failed: %d", ret); + return ret; + } + + ret = virtio_init_virtqueues(cfg->vdev, 1, i2c_virtio_enum_queues_cb, NULL); + if (ret != 0) { + LOG_ERR("virtio_init_virtqueues failed: %d", ret); + return ret; + } + + data->requestq = virtio_get_virtqueue(cfg->vdev, VIRTIO_I2C_REQUESTQ); + if (data->requestq == NULL) { + LOG_ERR("failed to get the request virtqueue"); + return -ENODEV; + } + + virtio_finalize_init(cfg->vdev); + + LOG_DBG("ready, up to %u messages per batch", CONFIG_I2C_VIRTIO_MAX_MSGS); + + return 0; +} + +#define I2C_VIRTIO_DEFINE(inst) \ + static struct i2c_virtio_data i2c_virtio_data_##inst; \ + static const struct i2c_virtio_config i2c_virtio_config_##inst = { \ + .vdev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + }; \ + I2C_DEVICE_DT_INST_DEFINE(inst, i2c_virtio_init, NULL, &i2c_virtio_data_##inst, \ + &i2c_virtio_config_##inst, POST_KERNEL, \ + CONFIG_I2C_INIT_PRIORITY, &i2c_virtio_api); + +DT_INST_FOREACH_STATUS_OKAY(I2C_VIRTIO_DEFINE) diff --git a/dts/bindings/i2c/virtio,i2c.yaml b/dts/bindings/i2c/virtio,i2c.yaml new file mode 100644 index 000000000000..13808b972b74 --- /dev/null +++ b/dts/bindings/i2c/virtio,i2c.yaml @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +description: | + VIRTIO I2C adapter device (ID:34) + +compatible: "virtio,i2c" + +include: [i2c-controller.yaml, base.yaml] + +examples: + - | + virtio_mmio0: virtio_mmio@a000000 { + compatible = "virtio,mmio"; + reg = <0xa000000 0x200>; + interrupts = <0 16 4>; + status = "okay"; + + virtio_i2c0: virtio-i2c { + compatible = "virtio,i2c"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + eeprom0: eeprom@50 { + compatible = "atmel,at24"; + reg = <0x50>; + size = <256>; + pagesize = <8>; + address-width = <8>; + timeout = <5>; + }; + }; + }; diff --git a/tests/drivers/build_all/i2c/tests.yaml b/tests/drivers/build_all/i2c/tests.yaml index bd3d795cc5dd..525abde36240 100644 --- a/tests/drivers/build_all/i2c/tests.yaml +++ b/tests/drivers/build_all/i2c/tests.yaml @@ -11,3 +11,10 @@ tests: drivers.i2c.build.bus_recovery: platform_allow: nucleo_f411re extra_args: "CONFIG_I2C=y CONFIG_I2C_BUS_RECOVERY=y CONFIG_GPIO=y" + drivers.i2c.build.virtio: + platform_allow: + - qemu_cortex_a53 + - qemu_riscv64 + integration_platforms: + - qemu_cortex_a53 + extra_args: DTC_OVERLAY_FILE="virtio.overlay" diff --git a/tests/drivers/build_all/i2c/virtio.overlay b/tests/drivers/build_all/i2c/virtio.overlay new file mode 100644 index 000000000000..7ae7e7ad9d62 --- /dev/null +++ b/tests/drivers/build_all/i2c/virtio.overlay @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * + * SPDX-License-Identifier: Apache-2.0 + * + * Application overlay for testing the VIRTIO I2C driver build + */ + +&virtio_mmio1 { + status = "okay"; + + test_i2c_virtio: virtio-i2c { + compatible = "virtio,i2c"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; +}; From 181907202d0d6014ee3df5d54672e1d938e0418d Mon Sep 17 00:00:00 2001 From: Hongquan Li Date: Tue, 11 Aug 2026 22:52:03 +0800 Subject: [PATCH 151/600] arch: riscv: pass NULL thread to custom stack guard without multithreading In !CONFIG_MULTITHREADING mode the inline assembly in z_riscv_switch_to_main_no_multithreading() calls z_riscv_custom_stack_guard_enable() without setting a0 to the k_thread * the callee's contract requires. The Andes implementation only survives because it ignores the argument; any implementation that dereferences thread faults. Change the contract to accept a NULL thread in no-multithreading mode (no thread object exists there) and pass NULL from the caller; the Andes HSP implementation now checks for NULL and guards the main stack. Also pin main_entry to callee-saved s1 so the jalr target survives the call. Fixes #113190 Signed-off-by: Hongquan Li --- arch/riscv/core/thread.c | 17 ++++++++++++++++- arch/riscv/custom/andes/hsp.c | 13 ++++++++++++- arch/riscv/include/kernel_arch_func.h | 9 +++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/arch/riscv/core/thread.c b/arch/riscv/core/thread.c index 336f31327479..40997524eb34 100644 --- a/arch/riscv/core/thread.c +++ b/arch/riscv/core/thread.c @@ -287,12 +287,27 @@ FUNC_NORETURN void z_riscv_switch_to_main_no_multithreading(k_thread_entry_t mai irq_unlock(RV_STATUS_IE); + /* + * No thread object exists in no-multithreading mode, so pass NULL: + * the callee's contract allows a NULL thread and expects the + * implementation to guard the main stack in that case. + */ + register struct k_thread *a0 __asm__("a0") = NULL; + + /* + * Bind main_entry to a callee-saved register: the call below + * clobbers the caller-saved registers, and jalr consumes %1 only + * after the call returns. %0 is safe anywhere because mv sp + * consumes it before the call. + */ + register k_thread_entry_t s1 __asm__("s1") = main_entry; + __asm__ volatile ( "mv sp, %0\n" "call z_riscv_custom_stack_guard_enable\n" "jalr ra, %1, 0\n" : - : "r" (main_stack), "r" (main_entry) + : "r" (main_stack), "r" (s1), "r" (a0) : "memory"); #else irq_unlock(RV_STATUS_IE); diff --git a/arch/riscv/custom/andes/hsp.c b/arch/riscv/custom/andes/hsp.c index 8d1b4b648b01..69561ec1e59e 100644 --- a/arch/riscv/custom/andes/hsp.c +++ b/arch/riscv/custom/andes/hsp.c @@ -107,6 +107,8 @@ void z_riscv_custom_stack_guard_init(void) * with the specified thread or interrupt context. * * @param thread Thread whose stack will be monitored by the stack guard. + * May be NULL in no-multithreading mode, in which case the + * main stack is guarded. */ void z_riscv_custom_stack_guard_enable(struct k_thread *thread) { @@ -133,7 +135,16 @@ void z_riscv_custom_stack_guard_enable(struct k_thread *thread) } #endif /* CONFIG_USERSPACE */ #else /* !CONFIG_MULTITHREADING */ - bound = (unsigned long)K_KERNEL_STACK_BUFFER(z_main_stack); + /* + * No thread object exists for the main thread in + * no-multithreading mode; the caller passes NULL and the + * main stack is guarded instead. + */ + if (thread != NULL) { + bound = thread->stack_info.start; + } else { + bound = (unsigned long)K_KERNEL_STACK_BUFFER(z_main_stack); + } #endif /* CONFIG_MULTITHREADING */ } diff --git a/arch/riscv/include/kernel_arch_func.h b/arch/riscv/include/kernel_arch_func.h index 8ccdd7a9c3d2..cb8cdc610caf 100644 --- a/arch/riscv/include/kernel_arch_func.h +++ b/arch/riscv/include/kernel_arch_func.h @@ -22,7 +22,16 @@ #ifdef CONFIG_CUSTOM_STACK_GUARD void z_riscv_custom_stack_guard_init(void); + +/* + * Enable the custom stack guard for the given thread's stack. + * + * @thread may be NULL when CONFIG_MULTITHREADING is disabled, since no + * thread object exists for the main thread in that mode; implementations + * must handle NULL and guard the main stack instead. + */ void z_riscv_custom_stack_guard_enable(struct k_thread *thread); + void z_riscv_custom_stack_guard_disable(void); bool z_riscv_custom_stack_guard_is_fault(struct arch_esf *esf); #endif /* CONFIG_CUSTOM_STACK_GUARD */ From 065989f35f1ca4caa4e0cd6c51bee8c67f90e3e7 Mon Sep 17 00:00:00 2001 From: Hongquan Li Date: Tue, 11 Aug 2026 22:52:10 +0800 Subject: [PATCH 152/600] tests: riscv: add custom stack guard overflow test Add a test that overflows the stack by unbounded recursion and expects the custom stack guard to raise K_ERR_STACK_CHK_FAIL, covering both multithreading and no-multithreading configurations on Andes AE350. The no-multithreading scenario exercises the boot path fixed for issue #113190, where the guard is enabled for the main stack via a NULL thread argument. Signed-off-by: Hongquan Li --- .../riscv/custom-stack-guard/CMakeLists.txt | 13 ++++ tests/arch/riscv/custom-stack-guard/prj.conf | 4 ++ .../arch/riscv/custom-stack-guard/src/main.c | 63 +++++++++++++++++++ .../arch/riscv/custom-stack-guard/tests.yaml | 16 +++++ 4 files changed, 96 insertions(+) create mode 100644 tests/arch/riscv/custom-stack-guard/CMakeLists.txt create mode 100644 tests/arch/riscv/custom-stack-guard/prj.conf create mode 100644 tests/arch/riscv/custom-stack-guard/src/main.c create mode 100644 tests/arch/riscv/custom-stack-guard/tests.yaml diff --git a/tests/arch/riscv/custom-stack-guard/CMakeLists.txt b/tests/arch/riscv/custom-stack-guard/CMakeLists.txt new file mode 100644 index 000000000000..2c5feb517edf --- /dev/null +++ b/tests/arch/riscv/custom-stack-guard/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Process Mission +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.28.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(riscv_custom_stack_guard) + +target_sources(app PRIVATE src/main.c) + +target_include_directories(app PRIVATE + ${ZEPHYR_BASE}/kernel/include + ${ZEPHYR_BASE}/arch/${ARCH}/include + ) diff --git a/tests/arch/riscv/custom-stack-guard/prj.conf b/tests/arch/riscv/custom-stack-guard/prj.conf new file mode 100644 index 000000000000..56499ae3e766 --- /dev/null +++ b/tests/arch/riscv/custom-stack-guard/prj.conf @@ -0,0 +1,4 @@ +CONFIG_ZTEST=y +CONFIG_RISCV_CUSTOM_CSR_ANDES_HSP=y +CONFIG_HW_STACK_PROTECTION=y +CONFIG_CUSTOM_STACK_GUARD=y diff --git a/tests/arch/riscv/custom-stack-guard/src/main.c b/tests/arch/riscv/custom-stack-guard/src/main.c new file mode 100644 index 000000000000..88d8fce36950 --- /dev/null +++ b/tests/arch/riscv/custom-stack-guard/src/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026 Process Mission + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static volatile ZTEST_BMEM bool valid_fault; + +void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf) +{ + int rv = TC_PASS; + + TC_PRINT("Caught system error -- reason %d %d\n", reason, valid_fault); + if (!valid_fault || reason != K_ERR_STACK_CHK_FAIL) { + TC_PRINT("Fatal error was unexpected, aborting...\n"); + rv = TC_FAIL; + } + TC_END_RESULT_CUSTOM(rv, "test_custom_stack_guard"); + TC_END_REPORT(rv); + arch_system_halt(reason); +} + +static void (*volatile overflow_stack_fn)(int); + +/* + * Recurse with a frame large enough that the stack pointer eventually + * crosses the custom stack guard bound. The depth parameter and the + * post-call use of the frame buffer defeat tail-call and frame + * optimizations. The call goes through a volatile function pointer so + * that the compiler does not diagnose the (intentional) infinite + * recursion. + */ +static __noinline void overflow_stack(int depth) +{ + char frame[256]; + + frame[0] = (char)depth; + overflow_stack_fn(depth + 1); + __asm__ volatile("" : : "r"(frame[0]) : "memory"); +} + +/** + * @brief Verify that the custom stack guard catches a stack overflow. + * @details Overflow the current stack by unbounded recursion and expect + * the custom stack guard to raise a fatal error. With MULTITHREADING=n + * this also exercises the no-multithreading boot path, where the guard + * is enabled for the main stack via a NULL thread argument. + */ +ZTEST(riscv_custom_stack_guard, test_stack_overflow) +{ + valid_fault = true; + overflow_stack_fn = overflow_stack; + overflow_stack(0); + + zassert_unreachable("Stack overflow did not fault"); + TC_END_REPORT(TC_FAIL); +} + +ZTEST_SUITE(riscv_custom_stack_guard, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/arch/riscv/custom-stack-guard/tests.yaml b/tests/arch/riscv/custom-stack-guard/tests.yaml new file mode 100644 index 000000000000..bbb5f6934c5b --- /dev/null +++ b/tests/arch/riscv/custom-stack-guard/tests.yaml @@ -0,0 +1,16 @@ +common: + platform_allow: + - adp_xc7k/ae350 + filter: CONFIG_CUSTOM_STACK_GUARD + ignore_faults: true + tags: + - kernel + - riscv + +tests: + arch.riscv.custom-stack-guard.no-mt.main-stack-overflow: + extra_configs: + - CONFIG_MULTITHREADING=n + arch.riscv.custom-stack-guard.mt.thread-stack-overflow: + extra_configs: + - CONFIG_MULTITHREADING=y From e30f65a1f9fb9f3e8483843c18cb7ae3a855b6d6 Mon Sep 17 00:00:00 2001 From: Anthony Raterta Date: Fri, 25 Jul 2025 16:20:46 +0800 Subject: [PATCH 153/600] drivers: regulator: add support for MAX20362 PMIC This driver integrates with the Zephyr regulator API to control the MAX20362 PMIC. Additional MAX20362-specific helper functions are exposed to support device features that are not covered by the generic regulator API. Signed-off-by: Anthony Raterta --- drivers/regulator/CMakeLists.txt | 1 + drivers/regulator/Kconfig | 1 + drivers/regulator/Kconfig.max20362 | 28 + drivers/regulator/regulator_max20362.c | 645 ++++++++++++++++++ .../regulator/adi,max20362-regulator.yaml | 102 +++ include/zephyr/drivers/regulator/max20362.h | 129 ++++ .../zephyr/dt-bindings/regulator/max20362.h | 63 ++ .../regulator/boards/native_sim_i2c.dtsi | 14 + 8 files changed, 983 insertions(+) create mode 100644 drivers/regulator/Kconfig.max20362 create mode 100644 drivers/regulator/regulator_max20362.c create mode 100644 dts/bindings/regulator/adi,max20362-regulator.yaml create mode 100644 include/zephyr/drivers/regulator/max20362.h create mode 100644 include/zephyr/dt-bindings/regulator/max20362.h diff --git a/drivers/regulator/CMakeLists.txt b/drivers/regulator/CMakeLists.txt index 52658ad31ed5..c8278091596e 100644 --- a/drivers/regulator/CMakeLists.txt +++ b/drivers/regulator/CMakeLists.txt @@ -21,6 +21,7 @@ zephyr_library_sources_ifdef(CONFIG_REGULATOR_GPIO regulator_gpio.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_INFINEON_AUTANALOG_PRB regulator_infineon_autanalog_prb.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_M5PM1 regulator_m5pm1.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_MAX20335 regulator_max20335.c) +zephyr_library_sources_ifdef(CONFIG_REGULATOR_MAX20362 regulator_max20362.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_MODULINO_LATCH_RELAY regulator_modulino_latch_relay.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_MPM54304 regulator_mpm54304.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_MSPM0_VREF regulator_mspm0_vref.c) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 26094d63cc06..eea797b15ddc 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -40,6 +40,7 @@ source "drivers/regulator/Kconfig.gpio" source "drivers/regulator/Kconfig.infineon_autanalog_prb" source "drivers/regulator/Kconfig.m5pm1" source "drivers/regulator/Kconfig.max20335" +source "drivers/regulator/Kconfig.max20362" source "drivers/regulator/Kconfig.modulino_latch_relay" source "drivers/regulator/Kconfig.mpm54304" source "drivers/regulator/Kconfig.mspm0" diff --git a/drivers/regulator/Kconfig.max20362 b/drivers/regulator/Kconfig.max20362 new file mode 100644 index 000000000000..6c8265c7278c --- /dev/null +++ b/drivers/regulator/Kconfig.max20362 @@ -0,0 +1,28 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +config REGULATOR_MAX20362 + bool "MAX20362 PMIC regulator device driver" + default y + depends on DT_HAS_ADI_MAX20362_REGULATOR_ENABLED + select I2C + help + Enable the Analog Devices MAX20362 PMIC regulator device driver + +if REGULATOR_MAX20362 + +config REGULATOR_ADI_MAX20362_COMMON_INIT_PRIORITY + int "MAX20362 regulator driver init priority (common part)" + default 86 + help + Init priority for the ADI MAX20362 regulator driver + (common part). It must be greater than I2C init priority. + +config REGULATOR_ADI_MAX20362_INIT_PRIORITY + int "MAX20362 regulator driver init priority" + default 87 + help + Init priority for the ADI MAX20362 regulator driver. It must be + greater than REGULATOR_ADI_MAX20362_COMMON_INIT_PRIORITY + +endif diff --git a/drivers/regulator/regulator_max20362.c b/drivers/regulator/regulator_max20362.c new file mode 100644 index 000000000000..03a26435f52b --- /dev/null +++ b/drivers/regulator/regulator_max20362.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT adi_max20362_regulator + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(regulator_max20362, CONFIG_REGULATOR_LOG_LEVEL); + +#define MAX20362_CHIP_ID_VAL 0x00 + +/* Register addresses */ +#define MAX20362_REG_CHIP_ID 0x00 +#define MAX20362_REG_BBST_CFG 0x01 +#define MAX20362_REG_BBST_VST 0x02 +#define MAX20362_REG_CAP_VST 0x0A +#define MAX20362_REG_IGN_CFG 0x0C +#define MAX20362_REG_STATUS 0x10 +#define MAX20362_REG_INT 0x14 +#define MAX20362_REG_INGEN_INT 0x16 +#define MAX20362_REG_LDO_INT 0x17 +#define MAX20362_REG_INT_MASK 0x18 +#define MAX20362_REG_INGEN_INT_MASK 0x1A +#define MAX20362_REG_LDO_INT_MASK 0x1B +#define MAX20362_REG_LDO_CFG 0x40 +#define MAX20362_REG_LDO_VST 0x41 +#define MAX20362_REG_WRTE_LCK 0x50 +#define MAX20362_REG_BBST_LCK 0x51 +#define MAX20362_REG_DVS_CFG 0x54 + +/* Register bit masks */ +#define MAX20362_BBST_VSET_MASK GENMASK(6, 0) +#define MAX20362_CAP_VSET_MASK GENMASK(3, 0) +#define MAX20362_LDO_VSET_MASK GENMASK(4, 0) +#define MAX20362_CAP_STEP_MASK GENMASK(5, 4) +#define MAX20362_CAP_CSET_MASK GENMASK(5, 0) +#define MAX20362_BB_EN_MASK BIT(5) +#define MAX20362_LDO_EN_MASK BIT(0) +#define MAX20362_LDO_DSCRGE_MASK BIT(2) +#define MAX20362_BB_DSCRGE_MASK BIT(1) +#define MAX20362_CAP_DSCRGE_MASK BIT(7) +#define MAX20362_BBLDO_MASK BIT(5) +#define MAX20362_DVS_MASK GENMASK(1, 0) +#define MAX20362_BBVDROP_MASK GENMASK(7, 6) + +/* Lock/unlock values */ +#define MAX20362_LOCK_BB 0xAA +#define MAX20362_UNLOCK_BB 0x55 +#define MAX20362_MASK_WRITE 0x01 +#define MAX20362_UNMASK_WRITE 0x00 + +/* BBLDO field values: whether the LDO waits for the buck-boost to be on before enabling */ +#define MAX20362_LDO_WAIT_FOR_BB 0x01 +#define MAX20362_LDO_NO_WAIT_FOR_BB 0x00 + +/* Settle time after switching the DVS interface source (microseconds) */ +#define MAX20362_DVS_SETTLE_TIME_US 300 + +const struct linear_range cap_current_range = LINEAR_RANGE_INIT(5000, 1000, 0x00, 0x2D); + +const struct linear_range bbout_range[] = { + LINEAR_RANGE_INIT(1500000, 50000, 0x00, 0x50), +}; + +const struct linear_range ldo_range[] = { + LINEAR_RANGE_INIT(900000, 100000, 0x00, 0x1F), +}; + +const struct linear_range cap_ranges[] = { + LINEAR_RANGE_INIT(2500000, 500000, 0x00, 0x0E), + LINEAR_RANGE_INIT(1600000, 250000, 0x00, 0x0F), + LINEAR_RANGE_INIT(1650000, 125000, 0x04, 0x0F), +}; + +enum max20362_pmic_sources { + MAX20362_PMIC_SOURCE_BBOOST, + MAX20362_PMIC_SOURCE_CAP, + MAX20362_PMIC_SOURCE_LDO, +}; + +struct regulator_max20362_desc { + uint8_t vset_mask; + uint8_t vsel_reg; + uint8_t enable_mask; + uint8_t cfg_reg; + uint8_t act_dscrge_mask; + uint8_t discharge_reg; + uint8_t cset_mask; + uint8_t csel_reg; + uint8_t uv_range_size; + const struct linear_range *uv_range; + const struct linear_range *ua_range; +}; + +struct regulator_max20362_common_config { + struct i2c_dt_spec bus; + uint8_t bbat_vdrop; + uint8_t dvs_source; + uint8_t ldo_source; +}; + +struct regulator_max20362_config { + struct regulator_common_config common; + struct i2c_dt_spec bus; + const struct regulator_max20362_desc *desc; + uint8_t source; +}; + +struct regulator_max20362_data { + struct regulator_common_data common; +}; + +static const struct regulator_max20362_desc __maybe_unused bboost_desc = { + .vset_mask = MAX20362_BBST_VSET_MASK, + .vsel_reg = MAX20362_REG_BBST_VST, + .enable_mask = MAX20362_BB_EN_MASK, + .cfg_reg = MAX20362_REG_BBST_CFG, + .act_dscrge_mask = MAX20362_BB_DSCRGE_MASK, + .discharge_reg = MAX20362_REG_BBST_CFG, + .uv_range = bbout_range, + .uv_range_size = ARRAY_SIZE(bbout_range), +}; + +static const struct regulator_max20362_desc __maybe_unused cap_desc = { + .vset_mask = MAX20362_CAP_VSET_MASK, + .vsel_reg = MAX20362_REG_CAP_VST, + .act_dscrge_mask = MAX20362_CAP_DSCRGE_MASK, + .discharge_reg = MAX20362_REG_CAP_VST, + .cset_mask = MAX20362_CAP_CSET_MASK, + .csel_reg = MAX20362_REG_IGN_CFG, + .uv_range = cap_ranges, + .ua_range = &cap_current_range, + .uv_range_size = ARRAY_SIZE(cap_ranges), +}; + +static const struct regulator_max20362_desc __maybe_unused ldo_desc = { + .vset_mask = MAX20362_LDO_VSET_MASK, + .vsel_reg = MAX20362_REG_LDO_VST, + .enable_mask = MAX20362_LDO_EN_MASK, + .cfg_reg = MAX20362_REG_LDO_CFG, + .act_dscrge_mask = MAX20362_LDO_DSCRGE_MASK, + .discharge_reg = MAX20362_REG_LDO_CFG, + .uv_range = ldo_range, + .uv_range_size = ARRAY_SIZE(ldo_range), +}; + +static inline int regulator_max20362_reg_read(const struct i2c_dt_spec *bus, uint8_t reg, + uint8_t *data) +{ + return i2c_reg_read_byte_dt(bus, reg, data); +} + +static inline int regulator_max20362_reg_write(const struct i2c_dt_spec *bus, uint8_t reg, + uint8_t data) +{ + return i2c_reg_write_byte_dt(bus, reg, data); +} + +static inline int regulator_max20362_reg_update(const struct i2c_dt_spec *bus, uint8_t addr, + uint8_t mask, uint8_t value) +{ + return i2c_reg_update_byte_dt(bus, addr, mask, FIELD_PREP(mask, value)); +} + +static int regulator_max20362_set_lock(const struct device *dev, bool lock) +{ + const struct regulator_max20362_config *config = dev->config; + int ret; + + if (config->source != MAX20362_PMIC_SOURCE_BBOOST) { + LOG_ERR("Regulator source %d does not support set_lock.", config->source); + return -ENOTSUP; + } + + ret = regulator_max20362_reg_write(&config->bus, MAX20362_REG_WRTE_LCK, + MAX20362_UNMASK_WRITE); + if (ret < 0) { + LOG_ERR("Failed to write lock register."); + return ret; + } + + ret = regulator_max20362_reg_write(&config->bus, MAX20362_REG_BBST_LCK, + lock ? MAX20362_LOCK_BB : MAX20362_UNLOCK_BB); + if (ret < 0) { + LOG_ERR("Failed to write buck-boost lock register."); + return ret; + } + + if (lock) { + ret = regulator_max20362_reg_write(&config->bus, MAX20362_REG_WRTE_LCK, + MAX20362_MASK_WRITE); + if (ret < 0) { + LOG_ERR("Failed to re-mask buck-boost lock register."); + return ret; + } + } + + return 0; +} + +static int regulator_max20362_set_enable(const struct device *dev, bool enable) +{ + const struct regulator_max20362_config *config = dev->config; + + if (config->source == MAX20362_PMIC_SOURCE_CAP) { + LOG_ERR("Regulator source %d does not support set_enable.", config->source); + return -ENOTSUP; + } + + return regulator_max20362_reg_update(&config->bus, config->desc->cfg_reg, + config->desc->enable_mask, enable); +} + +static int regulator_max20362_get_ldo_enable_status(const struct device *dev, bool *enabled) +{ + const struct regulator_max20362_config *config = dev->config; + uint8_t val; + int ret; + + if (config->source == MAX20362_PMIC_SOURCE_CAP) { + LOG_ERR("Regulator source %d does not support get_ldo_enable_status.", + config->source); + return -ENOTSUP; + } + + ret = regulator_max20362_reg_read(&config->bus, config->desc->cfg_reg, &val); + if (ret < 0) { + LOG_ERR("Failed to read enable register."); + return ret; + } + + *enabled = (FIELD_GET(config->desc->enable_mask, val) != 0); + + return 0; +} + +static inline int regulator_max20362_enable(const struct device *dev) +{ + return regulator_max20362_set_enable(dev, true); +} + +static inline int regulator_max20362_disable(const struct device *dev) +{ + return regulator_max20362_set_enable(dev, false); +} + +static unsigned int regulator_max20362_count_voltages(const struct device *dev) +{ + const struct regulator_max20362_config *config = dev->config; + + return linear_range_group_values_count(config->desc->uv_range, config->desc->uv_range_size); +} + +static int regulator_max20362_list_voltage(const struct device *dev, unsigned int idx, + int32_t *volt_uv) +{ + const struct regulator_max20362_config *config = dev->config; + + return linear_range_group_get_value(config->desc->uv_range, config->desc->uv_range_size, + idx, volt_uv); +} + +static int regulator_max20362_set_rail_voltage(const struct device *dev, int32_t min_uv, + int32_t max_uv, const struct linear_range *range, + uint8_t range_size) +{ + const struct regulator_max20362_config *config = dev->config; + uint16_t idx; + int ret = 0; + + for (int i = 0; i < range_size; i++) { + ret = linear_range_get_win_index(&range[i], min_uv, max_uv, &idx); + if (ret < 0) { + continue; + } + + if (config->source == MAX20362_PMIC_SOURCE_CAP) { + ret = regulator_max20362_reg_update(&config->bus, MAX20362_REG_CAP_VST, + MAX20362_CAP_STEP_MASK, i); + if (ret < 0) { + LOG_ERR("Failed to update supported CAP voltage range."); + return ret; + } + } + break; + } + if (ret < 0) { + LOG_ERR("Invalid voltage range: min_uv=%d, max_uv=%d.", min_uv, max_uv); + return ret; + } + + return regulator_max20362_reg_update(&config->bus, config->desc->vsel_reg, + config->desc->vset_mask, idx); +} + +static int regulator_max20362_get_rail_voltage(const struct device *dev, + const struct linear_range *range, uint8_t range_size, + int32_t *volt_uv) +{ + const struct regulator_max20362_config *config = dev->config; + uint8_t vsel_reg_value = 0; + uint8_t cap_sel = 0; + uint8_t idx; + int ret; + + ret = regulator_max20362_reg_read(&config->bus, config->desc->vsel_reg, &vsel_reg_value); + if (ret < 0) { + LOG_ERR("Failed to read voltage register."); + return ret; + } + + idx = FIELD_GET(config->desc->vset_mask, vsel_reg_value); + + if (config->source == MAX20362_PMIC_SOURCE_CAP) { + cap_sel = FIELD_GET(MAX20362_CAP_STEP_MASK, vsel_reg_value); + + if (cap_sel >= config->desc->uv_range_size) { + LOG_ERR("Invalid/reserved CAP step selection: %u", cap_sel); + return -EINVAL; + } + + return linear_range_get_value(&range[cap_sel], idx, volt_uv); + } + + return linear_range_group_get_value(range, range_size, idx, volt_uv); +} + +static int regulator_max20362_get_voltage(const struct device *dev, int32_t *volt_uv) +{ + const struct regulator_max20362_config *config = dev->config; + + return regulator_max20362_get_rail_voltage(dev, config->desc->uv_range, + config->desc->uv_range_size, volt_uv); +} + +static int regulator_max20362_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) +{ + const struct regulator_max20362_config *config = dev->config; + bool to_enable = false; + int ret; + + switch (config->source) { + case MAX20362_PMIC_SOURCE_BBOOST: + ret = regulator_max20362_set_lock(dev, false); + if (ret < 0) { + LOG_ERR("Failed to unlock write mask."); + return ret; + } + break; + case MAX20362_PMIC_SOURCE_LDO: + ret = regulator_max20362_get_ldo_enable_status(dev, &to_enable); + if (ret < 0) { + LOG_ERR("Failed to read LDO enable state."); + return ret; + } + if (to_enable) { + ret = regulator_max20362_set_enable(dev, false); + if (ret < 0) { + LOG_ERR("Failed to disable LDO regulator."); + return ret; + } + } + break; + default: + break; + } + + ret = regulator_max20362_set_rail_voltage(dev, min_uv, max_uv, config->desc->uv_range, + config->desc->uv_range_size); + if (ret < 0) { + LOG_ERR("Failed to set regulator rail voltage."); + return ret; + } + + switch (config->source) { + case MAX20362_PMIC_SOURCE_BBOOST: + ret = regulator_max20362_set_lock(dev, true); + if (ret < 0) { + LOG_ERR("Failed to re-lock write mask."); + return ret; + } + break; + case MAX20362_PMIC_SOURCE_LDO: + if (to_enable) { + ret = regulator_max20362_set_enable(dev, true); + if (ret < 0) { + LOG_ERR("Failed to enable LDO regulator."); + return ret; + } + } + break; + default: + break; + } + + return 0; +} + +static unsigned int regulator_max20362_count_current_limits(const struct device *dev) +{ + const struct regulator_max20362_config *config = dev->config; + + if (config->source != MAX20362_PMIC_SOURCE_CAP) { + LOG_ERR("Regulator source %d does not support count_current_limits.", + config->source); + return -ENOTSUP; + } + + return linear_range_values_count(config->desc->ua_range); +} + +static int regulator_max20362_list_current_limit(const struct device *dev, unsigned int idx, + int32_t *current_ua) +{ + const struct regulator_max20362_config *config = dev->config; + + if (config->source != MAX20362_PMIC_SOURCE_CAP) { + LOG_ERR("Regulator source %d does not support list_current_limit.", config->source); + return -ENOTSUP; + } + + return linear_range_get_value(config->desc->ua_range, idx, current_ua); +} + +static int regulator_max20362_set_current_limit(const struct device *dev, int32_t min_ua, + int32_t max_ua) +{ + const struct regulator_max20362_config *config = dev->config; + uint16_t idx; + int ret; + + if (config->source != MAX20362_PMIC_SOURCE_CAP) { + LOG_ERR("Regulator source %d does not support set_current_limit.", config->source); + return -ENOTSUP; + } + + ret = linear_range_get_win_index(config->desc->ua_range, min_ua, max_ua, &idx); + if (ret < 0) { + LOG_ERR("Invalid current range: min_ua=%d, max_ua=%d.", min_ua, max_ua); + return ret; + } + + return regulator_max20362_reg_update(&config->bus, config->desc->csel_reg, + config->desc->cset_mask, idx); +} + +static int regulator_max20362_set_active_discharge(const struct device *dev, bool active_discharge) +{ + const struct regulator_max20362_config *config = dev->config; + + return regulator_max20362_reg_update(&config->bus, config->desc->discharge_reg, + config->desc->act_dscrge_mask, active_discharge); +} + +static int regulator_max20362_get_active_discharge(const struct device *dev, bool *active_discharge) +{ + const struct regulator_max20362_config *config = dev->config; + uint8_t val; + int ret; + + ret = regulator_max20362_reg_read(&config->bus, config->desc->discharge_reg, &val); + if (ret < 0) { + LOG_ERR("Failed to read active discharge register."); + return ret; + } + + *active_discharge = FIELD_GET(config->desc->act_dscrge_mask, val); + + return 0; +} + +/* Interrupt handling functions */ + +int regulator_max20362_set_int_mask(const struct device *dev, uint8_t mask) +{ + const struct regulator_max20362_common_config *config = dev->config; + + return regulator_max20362_reg_write(&config->bus, MAX20362_REG_INT_MASK, mask); +} + +int regulator_max20362_set_ingen_int_mask(const struct device *dev, uint8_t mask) +{ + const struct regulator_max20362_common_config *config = dev->config; + + return regulator_max20362_reg_write(&config->bus, MAX20362_REG_INGEN_INT_MASK, mask); +} + +int regulator_max20362_set_ldo_int_mask(const struct device *dev, uint8_t mask) +{ + const struct regulator_max20362_common_config *config = dev->config; + + return regulator_max20362_reg_write(&config->bus, MAX20362_REG_LDO_INT_MASK, mask); +} + +static int regulator_max20362_set_bat_bbin_vdrop(const struct device *dev, uint8_t vdrop) +{ + const struct regulator_max20362_common_config *config = dev->config; + + return regulator_max20362_reg_update(&config->bus, MAX20362_REG_IGN_CFG, + MAX20362_BBVDROP_MASK, vdrop); +} + +static int regulator_max20362_set_dvs_interface_source(const struct device *dev, uint8_t source) +{ + const struct regulator_max20362_common_config *config = dev->config; + int ret; + + ret = regulator_max20362_reg_update(&config->bus, MAX20362_REG_DVS_CFG, MAX20362_DVS_MASK, + source); + if (ret < 0) { + return ret; + } + k_usleep(MAX20362_DVS_SETTLE_TIME_US); + + return 0; +} + +static int regulator_max20362_set_ldo_input_source(const struct device *dev, uint8_t source) +{ + const struct regulator_max20362_common_config *config = dev->config; + + if (source == MAX20362_LDO_SRC_BBOUT) { + return regulator_max20362_reg_update(&config->bus, MAX20362_REG_LDO_CFG, + MAX20362_BBLDO_MASK, MAX20362_LDO_WAIT_FOR_BB); + } else { + return regulator_max20362_reg_update(&config->bus, MAX20362_REG_LDO_CFG, + MAX20362_BBLDO_MASK, + MAX20362_LDO_NO_WAIT_FOR_BB); + } +} + +static int regulator_max20362_init(const struct device *dev) +{ + const struct regulator_max20362_config *config = dev->config; + + if (!i2c_is_ready_dt(&config->bus)) { + LOG_ERR_DEVICE_NOT_READY(config->bus.bus); + return -ENODEV; + } + + regulator_common_data_init(dev); + + return regulator_common_init(dev, false); +} + +static int regulator_max20362_common_init(const struct device *dev) +{ + const struct regulator_max20362_common_config *common_config = dev->config; + uint8_t val; + int ret; + + if (!i2c_is_ready_dt(&common_config->bus)) { + LOG_ERR_DEVICE_NOT_READY(common_config->bus.bus); + return -ENODEV; + } + + ret = i2c_reg_read_byte_dt(&common_config->bus, MAX20362_REG_CHIP_ID, &val); + if (ret < 0) { + LOG_ERR("Failed to read CHIP ID register."); + return ret; + } + + if (val != MAX20362_CHIP_ID_VAL) { + LOG_ERR("Mismatched CHIP ID register value returned."); + return -ENODEV; + } + + ret = regulator_max20362_set_bat_bbin_vdrop(dev, common_config->bbat_vdrop); + if (ret < 0) { + LOG_ERR("Failed to set voltage drop from BAT to BBIN"); + return ret; + } + + ret = regulator_max20362_set_dvs_interface_source(dev, common_config->dvs_source); + if (ret < 0) { + LOG_ERR("Failed to set DVS source"); + return ret; + } + + ret = regulator_max20362_set_ldo_input_source(dev, common_config->ldo_source); + if (ret < 0) { + LOG_ERR("Failed to set LDO input source"); + return ret; + } + + return 0; +} + +static DEVICE_API(regulator, api) = { + .enable = regulator_max20362_enable, + .disable = regulator_max20362_disable, + .count_voltages = regulator_max20362_count_voltages, + .list_voltage = regulator_max20362_list_voltage, + .set_voltage = regulator_max20362_set_voltage, + .get_voltage = regulator_max20362_get_voltage, + .count_current_limits = regulator_max20362_count_current_limits, + .list_current_limit = regulator_max20362_list_current_limit, + .set_current_limit = regulator_max20362_set_current_limit, + .set_active_discharge = regulator_max20362_set_active_discharge, + .get_active_discharge = regulator_max20362_get_active_discharge, +}; + +#define REGULATOR_MAX20362_DEFINE(node_id, id, child_name, _source) \ + static const struct regulator_max20362_config regulator_max20362_config_##id = { \ + .common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id), \ + .bus = I2C_DT_SPEC_GET(DT_PARENT(node_id)), \ + .desc = &child_name##_desc, \ + .source = _source, \ + }; \ + \ + static struct regulator_max20362_data regulator_max20362_data_##id; \ + DEVICE_DT_DEFINE(node_id, regulator_max20362_init, NULL, ®ulator_max20362_data_##id, \ + ®ulator_max20362_config_##id, POST_KERNEL, \ + CONFIG_REGULATOR_ADI_MAX20362_INIT_PRIORITY, &api); + +#define REGULATOR_MAX20362_DEFINE_COND(inst, child, source) \ + COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \ + (REGULATOR_MAX20362_DEFINE(DT_INST_CHILD(inst, child), \ + child##inst, child, source)), \ + ()) + +#define REGULATOR_MAX20362_DEFINE_ALL(inst) \ + static const struct regulator_max20362_common_config common_config_##inst = { \ + .bus = I2C_DT_SPEC_INST_GET(inst), \ + .bbat_vdrop = DT_INST_PROP(inst, bat_bbin_vdrop), \ + .dvs_source = DT_INST_PROP(inst, dvs_src), \ + .ldo_source = DT_INST_PROP(inst, ldo_src), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, regulator_max20362_common_init, NULL, NULL, \ + &common_config_##inst, POST_KERNEL, \ + CONFIG_REGULATOR_ADI_MAX20362_COMMON_INIT_PRIORITY, NULL); \ + \ + REGULATOR_MAX20362_DEFINE_COND(inst, bboost, MAX20362_PMIC_SOURCE_BBOOST) \ + REGULATOR_MAX20362_DEFINE_COND(inst, cap, MAX20362_PMIC_SOURCE_CAP) \ + REGULATOR_MAX20362_DEFINE_COND(inst, ldo, MAX20362_PMIC_SOURCE_LDO) + +DT_INST_FOREACH_STATUS_OKAY(REGULATOR_MAX20362_DEFINE_ALL) diff --git a/dts/bindings/regulator/adi,max20362-regulator.yaml b/dts/bindings/regulator/adi,max20362-regulator.yaml new file mode 100644 index 000000000000..2c60f2aa6619 --- /dev/null +++ b/dts/bindings/regulator/adi,max20362-regulator.yaml @@ -0,0 +1,102 @@ +# Copyright (c) 2025 Analog Devices Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: | + Analog Devices MAX20362 PMIC + + The PMIC has a bidirectional buck-boost converter, a CAP output and an LDO. + Each rail that is used must be defined as a child node, strictly following the + bboost, cap and ldo node names. + +compatible: "adi,max20362-regulator" + +include: [i2c-device.yaml, base.yaml] + +properties: + reg: + required: true + + bat-bbin-vdrop: + type: int + required: true + enum: + - 0 + - 1 + - 2 + - 3 + description: | + Voltage drop from BAT to BBIN in millivolts. Refer to the macros from + : + - 0: 55 mV (MAX20362_BAT_BBIN_VDROP_55MV) + - 1: 100 mV (MAX20362_BAT_BBIN_VDROP_100MV) + - 2: 150 mV (MAX20362_BAT_BBIN_VDROP_150MV) + - 3: 200 mV (MAX20362_BAT_BBIN_VDROP_200MV) + + dvs-src: + type: int + required: true + enum: + - 0 + - 1 + - 2 + description: | + Interface for DVS. Refer to the macros from + : + - 0: I2C (MAX20362_DVS_SRC_I2C) + - 1: Pseudo-SPI (MAX20362_DVS_SRC_PSEUDO_SPI) + - 2: Round-Robin (MAX20362_DVS_SRC_ROUND_ROBIN) + + ldo-src: + type: int + required: true + enum: + - 0 + - 1 + - 2 + description: | + LDO input source. If the source is buck-boost, the LDO control + can wait for the buck to be on before turning on the LDO. + Refer to the macros from : + - 0: BBOUT (MAX20362_LDO_SRC_BBOUT) + - 1: CAP (MAX20362_LDO_SRC_CAP) + - 2: BATT (MAX20362_LDO_SRC_BATT) + +child-binding: + description: | + Each rail that is used must be defined as a child node, strictly following + these node names, otherwise it will not be recognized: + - bboost + - cap + - ldo + include: + - name: regulator.yaml + property-allowlist: + - regulator-init-microvolt + - regulator-min-microvolt + - regulator-max-microvolt + - regulator-init-microamp + - regulator-max-microamp + - regulator-always-on + - regulator-boot-on + - regulator-initial-mode + - regulator-allowed-modes + +examples: + - | + max20362_regulators: max20362@68 { + compatible = "adi,max20362-regulator"; + reg = <0x68>; + bat-bbin-vdrop = <0>; + dvs-src = <0>; + ldo-src = <1>; + + bboost { + /* all properties for BBOOST */ + }; + cap { + /* all properties for CAP */ + }; + ldo { + /* all properties for LDO */ + }; + }; diff --git a/include/zephyr/drivers/regulator/max20362.h b/include/zephyr/drivers/regulator/max20362.h new file mode 100644 index 000000000000..30b82fd32410 --- /dev/null +++ b/include/zephyr/drivers/regulator/max20362.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @ingroup regulator_parent_max20362 + * @brief Public API for the MAX20362 PMIC regulator driver. + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_REGULATOR_MAX20362_H_ +#define ZEPHYR_INCLUDE_DRIVERS_REGULATOR_MAX20362_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/** + * @defgroup regulator_parent_max20362 MAX20362 API + * @ingroup regulator_parent_interface + * @brief Public API for the MAX20362 regulator driver. + * @{ + */ + +/** + * @name MAX20362 INT / INT_MASK register bits (0x14 / 0x18) + * @{ + */ +/** @brief Cap over-voltage lockout */ +#define MAX20362_INT_CAPOVLO_MASK BIT(7) +/** @brief Cap under-voltage lockout */ +#define MAX20362_INT_CAPUVLO_MASK BIT(6) +/** @brief Buck-boost on */ +#define MAX20362_INT_BBSTON_MASK BIT(5) +/** @brief Buck-boost off */ +#define MAX20362_INT_BBSTOFF_MASK BIT(4) +/** @brief Buck-boost input UVLO */ +#define MAX20362_INT_BBINUVLO_MASK BIT(3) +/** @brief Undervoltage lockout */ +#define MAX20362_INT_UVLO_MASK BIT(2) +/** @brief Boost fault */ +#define MAX20362_INT_BSTFLT_MASK BIT(1) +/** @brief Thermal fault */ +#define MAX20362_INT_THMFLT_MASK BIT(0) +/** @} */ + +/** + * @name MAX20362 LDO_INT / LDO_INT_MASK register bits (0x17 / 0x1B) + * @{ + */ +/** @brief Divider fault */ +#define MAX20362_LDOINT_DIV_MASK BIT(3) +/** @brief Short circuit */ +#define MAX20362_LDOINT_SHR_MASK BIT(2) +/** @brief Thermal fault */ +#define MAX20362_LDOINT_THM_MASK BIT(1) +/** @brief Current limit / clipping fault */ +#define MAX20362_LDOINT_CLP_MASK BIT(0) +/** @} */ + +/** + * @name MAX20362 INGEN_INT / INGEN_INT_MASK register bits (0x16 / 0x1A) + * @{ + */ +/** @brief Output timeout */ +#define MAX20362_INGENINT_OUTTMO_MASK BIT(4) +/** @brief Droop min */ +#define MAX20362_INGENINT_DRPMIN_MASK BIT(3) +/** @brief Tank timeout */ +#define MAX20362_INGENINT_TNKTMO_MASK BIT(2) +/** @brief SIMO pin */ +#define MAX20362_INGENINT_SIMOPIN_MASK BIT(1) +/** @brief Droop max */ +#define MAX20362_INGENINT_DRPMAX_MASK BIT(0) +/** @} */ + +/** + * @name MAX20362 interrupt mask convenience values + * @{ + */ +/** @brief Mask all INT bits */ +#define MAX20362_INT_MASK_ALL 0xFF +/** @brief Mask all LDO_INT bits (DIV | SHR | THM | CLP) */ +#define MAX20362_LDO_INT_MASK_ALL 0x0F +/** @brief Mask all INGEN_INT bits (OUTTMO | DRPMIN | TNKTMO | SIMOPIN | DRPMAX) */ +#define MAX20362_INGEN_INT_MASK_ALL 0x1F +/** @} */ + +/** + * @brief Sets the main interrupt mask (INT_MASK register) of the MAX20362 device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param mask Interrupt mask value to write (1 = masked/disabled, 0 = enabled). + * @return 0 on success, negative errno code on failure. + */ +int regulator_max20362_set_int_mask(const struct device *dev, uint8_t mask); + +/** + * @brief Sets the Ingenuity interrupt mask (INGEN_INT_MASK register) of the MAX20362 device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param mask Interrupt mask value to write (1 = masked/disabled, 0 = enabled). + * @return 0 on success, negative errno code on failure. + */ +int regulator_max20362_set_ingen_int_mask(const struct device *dev, uint8_t mask); + +/** + * @brief Sets the LDO interrupt mask (LDO_INT_MASK register) of the MAX20362 device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param mask Interrupt mask value to write (1 = masked/disabled, 0 = enabled). + * @return 0 on success, negative errno code on failure. + */ +int regulator_max20362_set_ldo_int_mask(const struct device *dev, uint8_t mask); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_REGULATOR_MAX20362_H_ */ diff --git a/include/zephyr/dt-bindings/regulator/max20362.h b/include/zephyr/dt-bindings/regulator/max20362.h new file mode 100644 index 000000000000..e58cdb2b7d9f --- /dev/null +++ b/include/zephyr/dt-bindings/regulator/max20362.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @ingroup regulator_max20362 + * @brief Header file for MAX20362 Devicetree helpers. + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_MAX20362_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_MAX20362_H_ + +/** + * @defgroup regulator_max20362 MAX20362 Devicetree helpers + * @brief Analog Devices MAX20362 PMIC regulator driver Devicetree helpers + * @ingroup devicetree-regulator + * @{ + */ + +/** + * @name MAX20362 BAT to BBIN voltage drop + * @{ + */ +/** 55 mV drop */ +#define MAX20362_BAT_BBIN_VDROP_55MV 0 +/** 100 mV drop */ +#define MAX20362_BAT_BBIN_VDROP_100MV 1 +/** 150 mV drop */ +#define MAX20362_BAT_BBIN_VDROP_150MV 2 +/** 200 mV drop */ +#define MAX20362_BAT_BBIN_VDROP_200MV 3 +/** @} */ + +/** + * @name MAX20362 DVS interface source + * @{ + */ +/** I2C interface */ +#define MAX20362_DVS_SRC_I2C 0 +/** Pseudo-SPI interface */ +#define MAX20362_DVS_SRC_PSEUDO_SPI 1 +/** Round-Robin interface */ +#define MAX20362_DVS_SRC_ROUND_ROBIN 2 +/** @} */ + +/** + * @name MAX20362 LDO input source + * @{ + */ +/** Buck-boost output (BBOUT) */ +#define MAX20362_LDO_SRC_BBOUT 0 +/** Charge pump output (CAP) */ +#define MAX20362_LDO_SRC_CAP 1 +/** Battery (BATT) */ +#define MAX20362_LDO_SRC_BATT 2 +/** @} */ + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_REGULATOR_MAX20362_H_ */ diff --git a/tests/drivers/build_all/regulator/boards/native_sim_i2c.dtsi b/tests/drivers/build_all/regulator/boards/native_sim_i2c.dtsi index 6e88de2124c5..3b1892a0d05f 100644 --- a/tests/drivers/build_all/regulator/boards/native_sim_i2c.dtsi +++ b/tests/drivers/build_all/regulator/boards/native_sim_i2c.dtsi @@ -248,3 +248,17 @@ npm10xx@b { LDSW {}; }; }; + +max20362@c { + compatible = "adi,max20362-regulator"; + reg = <0xc>; + bat-bbin-vdrop = <0>; + dvs-src = <0>; + ldo-src = <1>; + + bboost {}; + + cap {}; + + ldo {}; +}; From dc295cc414aa742b9cb18842d2cadd3b510f4d4e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 5 Aug 2026 15:18:54 -0400 Subject: [PATCH 154/600] drivers: timer: renesas_rx_cmt: use the generic timer core Drop the hand-rolled tick accounting for the generic core. CMT1 keeps free-running as the cycle source and CMT0 keeps raising the tick interrupt. What goes away is everything between them: the announce baseline, the cycle-to-tick division in three places, the deadline arithmetic and the private spinlock. This is a RELOAD backend rather than a compare one, because the compare register belongs to a different counter than the cycle source. The core's absolute deadlines live in CMT1's domain, which says nothing about CMT0's CMCOR, so what CMT0 can be given is a relative delay. CMT0 exists only to raise the interrupt, nothing reads its count, so the arming primitive restarts it and CMCOR becomes exactly the delay the core asked for. Two CMCOR pathologies in the old sys_clock_set_timeout() go with it. It special-cased the maximal tick value by declining to reprogram at all, which left whatever period was already loaded in place forever, and it could compute a CMCOR of zero, which matches on every count. The core neither special-cases a tick value nor programs a reload below TIMER_CORE_ALARM_MIN_CYCLES, set here to two so CMCOR never lands on a just-cleared CMCNT. CMT1's software extension stays, so the core is given 32 bits rather than the raw 16. One CMT1 period is 10.9 ms at 6 MHz and a masked delta aliases past that, so a k_busy_wait() holding interrupts for longer would see the cycle counter go backwards and uptime stop. That is poor practice on the caller's part, but it has to keep working for well over a second. Folding the wrap in on each read is what keeps the extension alive while no ISR can run, and it makes the read stateful, hence TIMER_CORE_COUNTER_NONATOMIC. A wrap is still only caught if something reads within the period, which the arming bound and any busy-wait loop both ensure. The arm range stays 16 bits, all CMT0's CMCOR holds. Tested on qemu_rx/r5f562n8, kernel and arch suites, and build-tested on rsk_rx130@512kb/r5f51308axfp. Signed-off-by: Nicolas Pitre --- drivers/timer/renesas_rx_cmt.c | 216 +++++++++------------------------ 1 file changed, 60 insertions(+), 156 deletions(-) diff --git a/drivers/timer/renesas_rx_cmt.c b/drivers/timer/renesas_rx_cmt.c index 1ea5b88a8e6b..48a43ba11d83 100644 --- a/drivers/timer/renesas_rx_cmt.c +++ b/drivers/timer/renesas_rx_cmt.c @@ -19,23 +19,14 @@ #define CMT0_NODE DT_NODELABEL(cmt0) #define CMT1_NODE DT_NODELABEL(cmt1) -#define CMT1_IRQN DT_IRQN(CMT1_NODE) +#define CMT0_IRQ_NUM DT_IRQ_BY_NAME(CMT0_NODE, cmi, irq) +#define CMT1_IRQN DT_IRQN(CMT1_NODE) #define ICU_NODE DT_NODELABEL(icu) - -#define ICU_IR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IR) -#define ICU_IR ((volatile uint8_t *)ICU_IR_ADDR) - -#define CMT0_IRQ_NUM DT_IRQ_BY_NAME(CMT0_NODE, cmi, irq) -#define CMT1_IRQ_NUM DT_IRQ_BY_NAME(CMT1_NODE, cmi, irq) +#define ICU_IR ((volatile uint8_t *)DT_REG_ADDR_BY_NAME(ICU_NODE, IR)) #define COUNTER_MAX 0x0000ffff - -#define CYCLES_PER_SEC (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) -#define TICKS_PER_SEC (CONFIG_SYS_CLOCK_TICKS_PER_SEC) -#define CYCLES_PER_TICK (CYCLES_PER_SEC / TICKS_PER_SEC) -#define MAX_TICKS ((k_ticks_t)(COUNTER_MAX / CYCLES_PER_TICK) - 1) - +/* CMT1 counts 0..CMCOR then resets, so a whole period is one more than that. */ #define CYCLES_CYCLE_TIMER (COUNTER_MAX + 1) static const struct clock_control_rx_subsys_cfg cmt_clk_cfg = { @@ -62,120 +53,76 @@ static const struct timer_rx_cfg cycle_timer_cfg = { .cmcnt = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCNT), .cmcor = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCOR)}; -#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER -typedef uint64_t cycle_t; -#define CYCLE_COUNT_MAX (0xffffffffffffffff) -#else -typedef uint32_t cycle_t; -#define CYCLE_COUNT_MAX (0xffffffff) +#if defined(CONFIG_TEST) +const int32_t z_sys_timer_irq_for_test = CMT0_IRQ_NUM; #endif -static cycle_t cycle_count; -static uint16_t clock_cycles_per_tick; +/* + * Two timers: CMT1 free-runs as the cycle source, widened by the core from the + * announce baseline, while CMT0 raises the tick interrupt. CMT0's CMCOR is a + * period rather than a deadline, since a match resets CMCNT, so this is the + * RELOAD backend. + */ +#define TIMER_CORE_BACKEND_RELOAD + +/* CMCNT is only 16 bits, which is 10.9 ms at 6 MHz, too short to be read + * straight: k_busy_wait() with interrupts masked has to keep working for well + * over a second, poor practice though that is, and the core's masked delta + * aliases past one period. So the count is extended in software here, and the + * core is handed 32 bits. Each read folds a wrap in, which is what keeps the + * extension alive while interrupts are masked and no ISR can run. Reading + * mutates that state, hence TIMER_CORE_COUNTER_NONATOMIC. + * + * The alarm is unaffected: CMT0's CMCOR is still 16 bits, so + * TIMER_CORE_ALARM_MAX_CYCLES below keeps the armed delay inside one period. + */ +#define TIMER_CORE_COUNTER_WIDTH 32 +#define TIMER_CORE_COUNTER_NONATOMIC -static volatile cycle_t announced_cycle_count; +/* One reload cannot express more than CMT0's 16-bit CMCOR holds. */ +#define TIMER_CORE_ALARM_MAX_CYCLES COUNTER_MAX -static struct k_spinlock lock; +/* A reload of one would program CMCOR == 0 against a just-cleared CMCNT, which + * matches on every count. Keep the floor one above that. + */ +#define TIMER_CORE_ALARM_MIN_CYCLES 2 -#if defined(CONFIG_TEST) -const int32_t z_sys_timer_irq_for_test = CMT0_IRQ_NUM; -#endif +/* Whole CMT1 periods consumed, the upper bits of the extended count. */ +static uint32_t cycle_count; -static cycle_t cmt1_elapsed(void) +static uint32_t timer_driver_cycle_get(void) { - uint32_t val1 = (uint32_t)(*cycle_timer_cfg.cmcnt); - uint8_t cmt_ir = ICU_IR[CMT1_IRQN]; - uint32_t val2 = (uint32_t)(*cycle_timer_cfg.cmcnt); + uint16_t val1 = *cycle_timer_cfg.cmcnt; + bool matched = ICU_IR[CMT1_IRQN] != 0; + uint16_t val2 = *cycle_timer_cfg.cmcnt; - if ((1 == cmt_ir) || (val1 > val2)) { + /* The compare-match flag catches a wrap that happened before this read, + * and val1 > val2 one that happened during it. + */ + if (matched || (val1 > val2)) { cycle_count += CYCLES_CYCLE_TIMER; - ICU_IR[CMT1_IRQN] = 0; } - return (val2 + cycle_count); + return cycle_count + val2; } -uint32_t sys_clock_cycle_get_32(void) +static inline void timer_driver_set_reload(uint32_t cycles) { - k_spinlock_key_t key = k_spin_lock(&lock); - - uint32_t ret = (uint32_t)cmt1_elapsed(); - - k_spin_unlock(&lock, key); - - return ret; -} - -#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER -uint64_t sys_clock_cycle_get_64(void) -{ - k_spinlock_key_t key = k_spin_lock(&lock); - - uint64_t ret = (uint64_t)cmt1_elapsed(); - - k_spin_unlock(&lock, key); - - return ret; + /* CMT0 exists only to raise the tick interrupt: the cycle domain is + * CMT1, so nothing reads CMT0's count and it can be restarted here. + * That makes CMCOR the plain relative delay the core asks for, with no + * need to bias it by the count in flight. + */ + *tick_timer_cfg.cmcnt = 0; + *tick_timer_cfg.cmcor = (uint16_t)(cycles - 1U); } -#endif /* CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER */ - -uint32_t sys_clock_elapsed(void) -{ - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { - /* Always return 0 for tickful operation */ - return 0; - } - - k_spinlock_key_t key = k_spin_lock(&lock); - - uint32_t ret; - cycle_t current_cycle_count; - - current_cycle_count = cmt1_elapsed(); - if (current_cycle_count < announced_cycle_count) { - /* cycle_count overflowed */ - ret = (uint32_t)((current_cycle_count + - (CYCLE_COUNT_MAX - announced_cycle_count + 1)) / - CYCLES_PER_TICK); - } else { - ret = (uint32_t)((current_cycle_count - announced_cycle_count) / CYCLES_PER_TICK); - } - - k_spin_unlock(&lock, key); - - return ret; -} +#include "system_timer_generic.h" static void cmt0_isr(void) { - k_ticks_t dticks; - cycle_t current_cycle_count; - - k_spinlock_key_t key = k_spin_lock(&lock); - - current_cycle_count = cmt1_elapsed(); - - if (current_cycle_count < announced_cycle_count) { - /* cycle_count overflowed */ - dticks = (k_ticks_t)((current_cycle_count + - (CYCLE_COUNT_MAX - announced_cycle_count + 1)) / - CYCLES_PER_TICK); - } else { - dticks = (k_ticks_t)((current_cycle_count - announced_cycle_count) / - CYCLES_PER_TICK); - } - - announced_cycle_count = (current_cycle_count / CYCLES_PER_TICK) * CYCLES_PER_TICK; - - k_spin_unlock(&lock, key); - - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { - sys_clock_announce(1); - } else { - sys_clock_announce(dticks); - } + timer_core_announce(); } static int sys_clock_driver_init(void) @@ -195,8 +142,10 @@ static int sys_clock_driver_init(void) *tick_timer_cfg.cmcr = 0x00C0; /* enable CMT0 interrupt */ *cycle_timer_cfg.cmcr = 0x00C0; /* enable CMT1 interrupt */ - clock_cycles_per_tick = (uint16_t)(CYCLES_PER_TICK); - *tick_timer_cfg.cmcor = clock_cycles_per_tick - 1; + /* A tickful kernel leaves the period alone after this, so set it here. + * A tickless one gets it from timer_core_init() below. + */ + *tick_timer_cfg.cmcor = (uint16_t)TIMER_CORE_CYC_PER_TICK - 1; *cycle_timer_cfg.cmcor = (uint16_t)COUNTER_MAX; IRQ_CONNECT(CMT0_IRQ_NUM, 0x01, cmt0_isr, NULL, 0); @@ -204,54 +153,9 @@ static int sys_clock_driver_init(void) *tick_timer_cfg.cmstr = 0x0003; /* start cmt0,1 */ - return 0; -} + timer_core_init(); -void sys_clock_set_timeout(uint32_t ticks, bool idle) -{ - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { - return; - } - - /* Nothing to do when the kernel has no near deadline to schedule. */ - if (ticks == SYS_CLOCK_MAX_WAIT) { - return; - } - - /* Preserve the original behavior even though it looks wrong; to be - * revisited. - */ - if (ticks >= 1) { - ticks -= 1; - } - if (ticks > MAX_TICKS) { - ticks = MAX_TICKS; - } - - k_spinlock_key_t key = k_spin_lock(&lock); - - cycle_t now = cmt1_elapsed(); - cycle_t elapsed; - - if (now < announced_cycle_count) { - /* cycle_count overflowed */ - elapsed = (cycle_t)(now + (CYCLE_COUNT_MAX - announced_cycle_count + 1)); - } else { - elapsed = (now - announced_cycle_count); - } - - cycle_t delay = (cycle_t)ticks * CYCLES_PER_TICK; - - delay += elapsed; - delay = DIV_ROUND_UP(delay, CYCLES_PER_TICK) * CYCLES_PER_TICK; - delay -= elapsed; - - uint16_t current = *tick_timer_cfg.cmcnt; - uint16_t new_cmcor = (uint16_t)(current + delay - 1U); - - *tick_timer_cfg.cmcor = new_cmcor; - - k_spin_unlock(&lock, key); + return 0; } SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); From 5b36ad890885d20ee85d5a5aba12367f37cc5b57 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Mon, 24 Aug 2026 17:45:55 +0530 Subject: [PATCH 155/600] include: dt-bindings: clock: alif: add i2c clock IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add I2C peripheral clock identifiers for Alif Balletto and Ensemble SoCs so DesignWare I2C nodes can reference the existing clock controller binding (alif,clockctrl). These macros mirror the CLKCTL_PER_SLV I2Cx control registers and provide SYST_PCLK-sourced clock entries for I2C0/I2C1 (Balletto) and I2C0–I2C3 (Ensemble). Signed-off-by: Shreehari HK --- .../dt-bindings/clock/alif-balletto-clocks.h | 12 ++++++++++ .../dt-bindings/clock/alif-ensemble-clocks.h | 22 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/zephyr/dt-bindings/clock/alif-balletto-clocks.h b/include/zephyr/dt-bindings/clock/alif-balletto-clocks.h index b8c11d3ea71f..a63b5108b130 100644 --- a/include/zephyr/dt-bindings/clock/alif-balletto-clocks.h +++ b/include/zephyr/dt-bindings/clock/alif-balletto-clocks.h @@ -24,6 +24,12 @@ /** UART control register offset in CLKCTL_PER_SLV */ #define ALIF_UART_CTRL_REG 0x08U +/** I2C0 control register offset in CLKCTL_PER_SLV */ +#define ALIF_I2C0_CTRL_REG 0x50U + +/** I2C1 control register offset in CLKCTL_PER_SLV */ +#define ALIF_I2C1_CTRL_REG 0x54U + /** @} */ /** @@ -50,6 +56,12 @@ #define ALIF_UART5_SYST_PCLK \ ALIF_CLK_CFG(CLKCTL_PER_SLV, UART_CTRL, 5U, 1U, 1U, 1U, 13U, ALIF_PARENT_CLK_SYST_PCLK) +/** I2C0 clock sourced from system PCLK */ +#define ALIF_I2C0_SYST_PCLK \ + ALIF_CLK_CFG(CLKCTL_PER_SLV, I2C0_CTRL, 0U, 1U, 0U, 0U, 0U, ALIF_PARENT_CLK_SYST_PCLK) +/** I2C1 clock sourced from system PCLK */ +#define ALIF_I2C1_SYST_PCLK \ + ALIF_CLK_CFG(CLKCTL_PER_SLV, I2C1_CTRL, 0U, 1U, 0U, 0U, 0U, ALIF_PARENT_CLK_SYST_PCLK) /** @} */ #endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ALIF_BALLETTO_CLOCKS_H_ */ diff --git a/include/zephyr/dt-bindings/clock/alif-ensemble-clocks.h b/include/zephyr/dt-bindings/clock/alif-ensemble-clocks.h index 756c4551fc0e..a7b68052e47d 100644 --- a/include/zephyr/dt-bindings/clock/alif-ensemble-clocks.h +++ b/include/zephyr/dt-bindings/clock/alif-ensemble-clocks.h @@ -25,6 +25,15 @@ /** UART control register offset in CLKCTL_PER_SLV */ #define ALIF_UART_CTRL_REG 0x08U +/** I2C0 control register offset in CLKCTL_PER_SLV */ +#define ALIF_I2C0_CTRL_REG 0x50U +/** I2C1 control register offset in CLKCTL_PER_SLV */ +#define ALIF_I2C1_CTRL_REG 0x54U +/** I2C2 control register offset in CLKCTL_PER_SLV */ +#define ALIF_I2C2_CTRL_REG 0x58U +/** I2C3 control register offset in CLKCTL_PER_SLV */ +#define ALIF_I2C3_CTRL_REG 0x5CU + /** @} */ /** @@ -56,7 +65,18 @@ /** UART7 clock sourced from system PCLK */ #define ALIF_UART7_SYST_PCLK \ ALIF_CLK_CFG(CLKCTL_PER_SLV, UART_CTRL, 7U, 1U, 1U, 1U, 15U, ALIF_PARENT_CLK_SYST_PCLK) - +/** I2C0 clock sourced from system PCLK */ +#define ALIF_I2C0_SYST_PCLK \ + ALIF_CLK_CFG(CLKCTL_PER_SLV, I2C0_CTRL, 0U, 1U, 0U, 0U, 0U, ALIF_PARENT_CLK_SYST_PCLK) +/** I2C1 clock sourced from system PCLK */ +#define ALIF_I2C1_SYST_PCLK \ + ALIF_CLK_CFG(CLKCTL_PER_SLV, I2C1_CTRL, 0U, 1U, 0U, 0U, 0U, ALIF_PARENT_CLK_SYST_PCLK) +/** I2C2 clock sourced from system PCLK */ +#define ALIF_I2C2_SYST_PCLK \ + ALIF_CLK_CFG(CLKCTL_PER_SLV, I2C2_CTRL, 0U, 1U, 0U, 0U, 0U, ALIF_PARENT_CLK_SYST_PCLK) +/** I2C3 clock sourced from system PCLK */ +#define ALIF_I2C3_SYST_PCLK \ + ALIF_CLK_CFG(CLKCTL_PER_SLV, I2C3_CTRL, 0U, 1U, 0U, 0U, 0U, ALIF_PARENT_CLK_SYST_PCLK) /** @} */ #endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ALIF_ENSEMBLE_CLOCKS_H_ */ From 821583244590bb9af810b3192c25f453080d5d23 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Mon, 24 Aug 2026 19:23:21 +0530 Subject: [PATCH 156/600] dts: arm: alif: add designWare i2c devicetree nodes Describe Alif Balletto and Ensemble I2C controllers as disabled snps,designware-i2c nodes so boards can enable them through the existing upstream I2C_DW driver without adding a new driver. Each node includes register base, interrupt routing (NVIC/GIC as appropriate), clock phandle, and standard DesignWare properties such as clock-frequency and timing offsets. Controllers remain disabled by default until board devicetree enables the instances required by each devkit. Signed-off-by: Shreehari HK --- .../alif/balletto/common/balletto_common.dtsi | 37 +++++++++++++++++++ .../alif/ensemble/common/ensemble_common.dtsi | 35 ++++++++++++++++++ .../common/ensemble_common_gic_irq.dtsi | 10 +++++ .../common/ensemble_common_nvic_irq.dtsi | 10 +++++ .../ensemble/common/ensemble_e4_e6_e8.dtsi | 34 +++++++++++++++++ .../common/ensemble_e4_e6_e8_gic_irq.dtsi | 10 +++++ .../common/ensemble_e4_e6_e8_nvic_irq.dtsi | 10 +++++ 7 files changed, 146 insertions(+) diff --git a/dts/arm/alif/balletto/common/balletto_common.dtsi b/dts/arm/alif/balletto/common/balletto_common.dtsi index 891763ab70a6..6304451f4861 100644 --- a/dts/arm/alif/balletto/common/balletto_common.dtsi +++ b/dts/arm/alif/balletto/common/balletto_common.dtsi @@ -8,6 +8,7 @@ #include #include #include +#include / { cpus { @@ -151,6 +152,42 @@ current-speed = <115200>; status = "disabled"; }; + + /* + * DesignWare I2C controllers. + * hcnt-offset / lcnt-offset are the tuning offsets applied on top + * of the values computed from the input clock and target speed. + * Verify against the target board layout and adjust per instance if needed. + */ + i2c0: i2c@49010000 { + compatible = "snps,designware-i2c"; + clocks = <&clockctrl ALIF_I2C0_SYST_PCLK>; + clock-frequency = ; + hcnt-offset = <0>; + lcnt-offset = <0>; + fs-spike-len = <5>; + hs-spike-len = <1>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <132 0>; + reg = <0x49010000 0x1000>; + status = "disabled"; + }; + + i2c1: i2c@49011000 { + compatible = "snps,designware-i2c"; + clocks = <&clockctrl ALIF_I2C1_SYST_PCLK>; + clock-frequency = ; + hcnt-offset = <0>; + lcnt-offset = <0>; + fs-spike-len = <5>; + hs-spike-len = <1>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <133 0>; + reg = <0x49011000 0x1000>; + status = "disabled"; + }; }; }; diff --git a/dts/arm/alif/ensemble/common/ensemble_common.dtsi b/dts/arm/alif/ensemble/common/ensemble_common.dtsi index 5b160aa3d787..572a949fa183 100644 --- a/dts/arm/alif/ensemble/common/ensemble_common.dtsi +++ b/dts/arm/alif/ensemble/common/ensemble_common.dtsi @@ -8,6 +8,7 @@ #include #include #include +#include /* * Interrupt routing for the peripherals defined here is intentionally kept @@ -218,5 +219,39 @@ #gpio-cells = <2>; status = "disabled"; }; + + /* + * DesignWare I2C controllers. + * hcnt-offset / lcnt-offset are the tuning offsets applied on top + * of the values computed from the input clock and target speed. + * Verify against the target board layout and adjust per instance if needed. + */ + i2c0: i2c@49010000 { + compatible = "snps,designware-i2c"; + clocks = <&clockctrl ALIF_I2C0_SYST_PCLK>; + clock-frequency = ; + hcnt-offset = <0>; + lcnt-offset = <0>; + fs-spike-len = <5>; + hs-spike-len = <1>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x49010000 0x1000>; + status = "disabled"; + }; + + i2c1: i2c@49011000 { + compatible = "snps,designware-i2c"; + clocks = <&clockctrl ALIF_I2C1_SYST_PCLK>; + clock-frequency = ; + hcnt-offset = <0>; + lcnt-offset = <0>; + fs-spike-len = <5>; + hs-spike-len = <1>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x49011000 0x1000>; + status = "disabled"; + }; }; }; diff --git a/dts/arm/alif/ensemble/common/ensemble_common_gic_irq.dtsi b/dts/arm/alif/ensemble/common/ensemble_common_gic_irq.dtsi index cc39f087185a..812237011438 100644 --- a/dts/arm/alif/ensemble/common/ensemble_common_gic_irq.dtsi +++ b/dts/arm/alif/ensemble/common/ensemble_common_gic_irq.dtsi @@ -154,3 +154,13 @@ , ; }; + +&i2c0 { + interrupt-parent = <&gic>; + interrupts = ; +}; + +&i2c1 { + interrupt-parent = <&gic>; + interrupts = ; +}; diff --git a/dts/arm/alif/ensemble/common/ensemble_common_nvic_irq.dtsi b/dts/arm/alif/ensemble/common/ensemble_common_nvic_irq.dtsi index 0230bdb5c7dd..ba019ae29f48 100644 --- a/dts/arm/alif/ensemble/common/ensemble_common_nvic_irq.dtsi +++ b/dts/arm/alif/ensemble/common/ensemble_common_nvic_irq.dtsi @@ -151,3 +151,13 @@ <249 0>, <250 0>; }; + +&i2c0 { + interrupt-parent = <&nvic>; + interrupts = <132 0>; +}; + +&i2c1 { + interrupt-parent = <&nvic>; + interrupts = <133 0>; +}; diff --git a/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8.dtsi b/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8.dtsi index 4e9b0d0b2436..a56d5aca9600 100644 --- a/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8.dtsi +++ b/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8.dtsi @@ -84,6 +84,40 @@ #gpio-cells = <2>; status = "disabled"; }; + + /* + * DesignWare I2C controllers. + * hcnt-offset / lcnt-offset are the tuning offsets applied on top + * of the values computed from the input clock and target speed. + * Verify against the target board layout and adjust per instance if needed. + */ + i2c2: i2c@49012000 { + compatible = "snps,designware-i2c"; + clocks = <&clockctrl ALIF_I2C2_SYST_PCLK>; + clock-frequency = ; + hcnt-offset = <0>; + lcnt-offset = <0>; + fs-spike-len = <5>; + hs-spike-len = <1>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x49012000 0x1000>; + status = "disabled"; + }; + + i2c3: i2c@49013000 { + compatible = "snps,designware-i2c"; + clocks = <&clockctrl ALIF_I2C3_SYST_PCLK>; + clock-frequency = ; + hcnt-offset = <0>; + lcnt-offset = <0>; + fs-spike-len = <5>; + hs-spike-len = <1>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x49013000 0x1000>; + status = "disabled"; + }; }; /* These devices carry the full eight pin wide LPGPIO. */ diff --git a/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_gic_irq.dtsi b/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_gic_irq.dtsi index 77b99594e762..8efcefb53026 100644 --- a/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_gic_irq.dtsi +++ b/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_gic_irq.dtsi @@ -103,3 +103,13 @@ , ; }; + +&i2c2 { + interrupt-parent = <&gic>; + interrupts = ; +}; + +&i2c3 { + interrupt-parent = <&gic>; + interrupts = ; +}; diff --git a/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_nvic_irq.dtsi b/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_nvic_irq.dtsi index 365cafdf447d..30a74fb95970 100644 --- a/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_nvic_irq.dtsi +++ b/dts/arm/alif/ensemble/common/ensemble_e4_e6_e8_nvic_irq.dtsi @@ -103,3 +103,13 @@ <297 0>, <298 0>; }; + +&i2c2 { + interrupt-parent = <&nvic>; + interrupts = <134 0>; +}; + +&i2c3 { + interrupt-parent = <&nvic>; + interrupts = <135 0>; +}; From 1d7028a82f376c206fb4148acf576fc57f846469 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Mon, 24 Aug 2026 19:40:54 +0530 Subject: [PATCH 157/600] soc: alif: enable designWare i2c clock optimization Default I2C_DW_IC_CLK_FREQ_OPTIMIZATION for Alif Balletto and Ensemble. The Alif I2C blocks are clocked from SYST_PCLK through the SoC clock controller; enabling the driver's IC clock frequency optimization allows correct timing register calculation on these platforms. Signed-off-by: Shreehari HK --- soc/alif/balletto/Kconfig.defconfig | 3 +++ soc/alif/ensemble/Kconfig.defconfig | 3 +++ 2 files changed, 6 insertions(+) diff --git a/soc/alif/balletto/Kconfig.defconfig b/soc/alif/balletto/Kconfig.defconfig index a5fec16f3097..0b6850a0d9a0 100644 --- a/soc/alif/balletto/Kconfig.defconfig +++ b/soc/alif/balletto/Kconfig.defconfig @@ -15,4 +15,7 @@ configdefault CLOCK_CONTROL configdefault CACHE_MANAGEMENT default y +configdefault I2C_DW_IC_CLK_FREQ_OPTIMIZATION + default y + endif # SOC_FAMILY_BALLETTO diff --git a/soc/alif/ensemble/Kconfig.defconfig b/soc/alif/ensemble/Kconfig.defconfig index 77f2fded5f93..980be430124b 100644 --- a/soc/alif/ensemble/Kconfig.defconfig +++ b/soc/alif/ensemble/Kconfig.defconfig @@ -15,4 +15,7 @@ config CLOCK_CONTROL configdefault CACHE_MANAGEMENT default y +configdefault I2C_DW_IC_CLK_FREQ_OPTIMIZATION + default y + endif # SOC_FAMILY_ENSEMBLE From efa4f12588da97fd34c487b0f3477d8cebcb72b3 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Mon, 24 Aug 2026 20:09:31 +0530 Subject: [PATCH 158/600] boards: alif: wire i2c pinctrl and dw clock speed for devkits Connect Alif devkit I2C pins to the existing DesignWare I2C nodes and provide board-level defaults required by the I2C_DW driver. Add I2C pinmux definitions for Balletto B1, Ensemble E1C, and Ensemble E8 development kits, and set I2C_DW_CLOCK_SPEED to match the controller input clock on these boards. This lets board overlays or shields enable I2C without additional driver changes. Signed-off-by: Shreehari HK --- boards/alif/balletto_b1_dk/Kconfig.defconfig | 11 ++++++ .../balletto_b1_dk-pinctrl.dtsi | 18 ++++++++++ .../balletto_b1_dk_ab1c1f4m51820ph0.yaml | 2 ++ boards/alif/ensemble_e1c_dk/Kconfig.defconfig | 11 ++++++ .../ensemble_e1c_dk-pinctrl.dtsi | 18 ++++++++++ ...emble_e1c_dk_ae1c1f4051920ph0_rtss_he.yaml | 2 ++ boards/alif/ensemble_e8_dk/Kconfig.defconfig | 11 ++++++ .../ensemble_e8_dk-pinctrl.dtsi | 36 +++++++++++++++++++ ...semble_e8_dk_ae402fa0e5597le0_rtss_he.yaml | 2 ++ ...semble_e8_dk_ae402fa0e5597le0_rtss_hp.yaml | 2 ++ ...semble_e8_dk_ae612fa0e5597ls0_rtss_he.yaml | 2 ++ ...semble_e8_dk_ae612fa0e5597ls0_rtss_hp.yaml | 2 ++ .../ensemble_e8_dk_ae822fa0e5597ls0_apss.yaml | 2 ++ ...semble_e8_dk_ae822fa0e5597ls0_rtss_he.yaml | 2 ++ ...semble_e8_dk_ae822fa0e5597ls0_rtss_hp.yaml | 2 ++ 15 files changed, 123 insertions(+) create mode 100644 boards/alif/balletto_b1_dk/Kconfig.defconfig create mode 100644 boards/alif/ensemble_e1c_dk/Kconfig.defconfig create mode 100644 boards/alif/ensemble_e8_dk/Kconfig.defconfig diff --git a/boards/alif/balletto_b1_dk/Kconfig.defconfig b/boards/alif/balletto_b1_dk/Kconfig.defconfig new file mode 100644 index 000000000000..5f682bad34e8 --- /dev/null +++ b/boards/alif/balletto_b1_dk/Kconfig.defconfig @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright Alif Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Default configurations applied to the B1 DK boards + +if BOARD_BALLETTO_B1_DK + +configdefault I2C_DW_CLOCK_SPEED + default 40 + +endif # BOARD_BALLETTO_B1_DK diff --git a/boards/alif/balletto_b1_dk/balletto_b1_dk-pinctrl.dtsi b/boards/alif/balletto_b1_dk/balletto_b1_dk-pinctrl.dtsi index c4f3218f572f..8e8ae0aa0f3c 100644 --- a/boards/alif/balletto_b1_dk/balletto_b1_dk-pinctrl.dtsi +++ b/boards/alif/balletto_b1_dk/balletto_b1_dk-pinctrl.dtsi @@ -16,4 +16,22 @@ pinmux = ; }; }; + + pinctrl_i2c0: pinctrl_i2c0 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; + + pinctrl_i2c1: pinctrl_i2c1 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; }; diff --git a/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.yaml b/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.yaml index af0e23190f85..fc667a85984a 100644 --- a/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.yaml +++ b/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e1c_dk/Kconfig.defconfig b/boards/alif/ensemble_e1c_dk/Kconfig.defconfig new file mode 100644 index 000000000000..99fb3c4f3fd3 --- /dev/null +++ b/boards/alif/ensemble_e1c_dk/Kconfig.defconfig @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright Alif Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Default configurations applied to the E1C DK boards + +if BOARD_ENSEMBLE_E1C_DK + +configdefault I2C_DW_CLOCK_SPEED + default 40 + +endif # BOARD_ENSEMBLE_E1C_DK diff --git a/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk-pinctrl.dtsi b/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk-pinctrl.dtsi index ea9b7f750c64..f74753f2d6c3 100644 --- a/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk-pinctrl.dtsi +++ b/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk-pinctrl.dtsi @@ -16,4 +16,22 @@ pinmux = ; }; }; + + pinctrl_i2c0: pinctrl_i2c0 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; + + pinctrl_i2c1: pinctrl_i2c1 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; }; diff --git a/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.yaml b/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.yaml index c911c3d63bd4..ab0c44b338ed 100644 --- a/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.yaml +++ b/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/Kconfig.defconfig b/boards/alif/ensemble_e8_dk/Kconfig.defconfig new file mode 100644 index 000000000000..486787189d32 --- /dev/null +++ b/boards/alif/ensemble_e8_dk/Kconfig.defconfig @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright Alif Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Default configurations applied to the E8 DK boards + +if BOARD_ENSEMBLE_E8_DK + +configdefault I2C_DW_CLOCK_SPEED + default 100 + +endif # BOARD_ENSEMBLE_E8_DK diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk-pinctrl.dtsi b/boards/alif/ensemble_e8_dk/ensemble_e8_dk-pinctrl.dtsi index 8be170d45319..962982bce570 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk-pinctrl.dtsi +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk-pinctrl.dtsi @@ -94,4 +94,40 @@ pinmux = ; }; }; + + pinctrl_i2c0: pinctrl_i2c0 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; + + pinctrl_i2c1: pinctrl_i2c1 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; + + pinctrl_i2c2: pinctrl_i2c2 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; + + pinctrl_i2c3: pinctrl_i2c3 { + group0 { + pinmux = , + ; + input-enable; + drive-strength = <12>; + }; + }; }; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.yaml index 96f490805e81..8d772d703003 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.yaml index 08a52f2680f1..3ae2d3e63679 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.yaml index e63282a1daf7..1c9343a996f1 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.yaml index f9c37e97893a..eb46177d1aa7 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.yaml index 3f149cd2ce55..dcf821193351 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.yaml @@ -8,3 +8,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.yaml index f491b93bf7cf..2c48bc559f38 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.yaml @@ -9,3 +9,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.yaml b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.yaml index da85814b0bc4..a2a8d3502fb2 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.yaml +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.yaml @@ -9,3 +9,5 @@ arch: arm vendor: alif toolchain: - zephyr +supported: + - i2c From bc885b6843925751c1973d3f4a7fe380a68efca6 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Thu, 27 Aug 2026 13:51:14 +0530 Subject: [PATCH 159/600] boards: alif: ensemble_e8_dk: add mikrobus i2c connector support Expose mikrobus_i2c aliases on Alif Ensemble E8 development kits so mikroBUS Click shields can use the existing DesignWare I2C driver and standard shield overlays. E8_DK boards get a mikrobus devicetree fragment that assigns the click-header I2C instance, applies the board I2C pinmux. This allows Click boards such as the Mikroe Weather Click (BME280) to be used with --shield without board-specific sample overlays. Signed-off-by: Shreehari HK --- ...ensemble_e8_dk_ae402fa0e5597le0_rtss_he.dts | 1 + ...ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.dts | 1 + ...ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.dts | 1 + ...ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.dts | 1 + .../ensemble_e8_dk_ae822fa0e5597ls0_apss.dts | 1 + ...ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.dts | 1 + ...ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.dts | 1 + .../ensemble_e8_dk/ensemble_e8_dk_common.dtsi | 18 ++++++++++++++++++ 8 files changed, 25 insertions(+) create mode 100644 boards/alif/ensemble_e8_dk/ensemble_e8_dk_common.dtsi diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.dts index 1c0fc6bf9815..8e773cf5236e 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_he.dts @@ -9,6 +9,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae402fa0e5597le0-rtss-he"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.dts index d0eb6e8f8e67..1ca0ccfbc951 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae402fa0e5597le0_rtss_hp.dts @@ -9,6 +9,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae402fa0e5597le0-rtss-hp"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.dts index bc588e2f29ec..b79076bbfcc7 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_he.dts @@ -9,6 +9,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae612fa0e5597ls0-rtss-he"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.dts index 0e9ac76c170e..89bb6640105a 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae612fa0e5597ls0_rtss_hp.dts @@ -9,6 +9,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae612fa0e5597ls0-rtss-hp"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.dts index 75ca0ee8dfd5..86607122cfd1 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_apss.dts @@ -9,6 +9,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae822fa0e5597ls0-apss"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.dts index 6f312cb14c49..ea922173e8c5 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_he.dts @@ -10,6 +10,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae822fa0e5597ls0-rtss-he"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.dts b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.dts index 1b3549a3a8c6..38ee903709dd 100644 --- a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.dts +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_ae822fa0e5597ls0_rtss_hp.dts @@ -10,6 +10,7 @@ #include #include #include "ensemble_e8_dk-pinctrl.dtsi" +#include "ensemble_e8_dk_common.dtsi" / { compatible = "alif,ensemble-e8-dk-ae822fa0e5597ls0-rtss-hp"; diff --git a/boards/alif/ensemble_e8_dk/ensemble_e8_dk_common.dtsi b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_common.dtsi new file mode 100644 index 000000000000..d947b748a129 --- /dev/null +++ b/boards/alif/ensemble_e8_dk/ensemble_e8_dk_common.dtsi @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: Copyright Alif Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * On-board peripherals shared by every ensemble_e8_dk board target. Include + * this after the SoC dtsi, the cluster dtsi and the board pinctrl dtsi: every + * node referenced below comes from one of those. + */ + +&i2c0 { + status = "okay"; + pinctrl-0 = <&pinctrl_i2c0>; + pinctrl-names = "default"; +}; + +mikrobus_i2c: &i2c0 {}; From 050d19e8b65ceae2087b3c623d94805d3307ac09 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Thu, 27 Aug 2026 13:55:05 +0530 Subject: [PATCH 160/600] boards: alif: ensemble_e1c_dk: add mikrobus i2c connector support Expose mikrobus_i2c aliases on Alif Ensemble E1C development kit so mikroBUS Click shields can use the existing DesignWare I2C driver and standard shield overlays. E1C_DK board gets a mikrobus devicetree fragment that assigns the click-header I2C instance, applies the board I2C pinmux. This allows Click boards such as the Mikroe Weather Click (BME280) to be used with --shield without board-specific sample overlays. Signed-off-by: Shreehari HK --- .../ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.dts b/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.dts index a2c6d980725f..98c133401530 100644 --- a/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.dts +++ b/boards/alif/ensemble_e1c_dk/ensemble_e1c_dk_ae1c1f4051920ph0_rtss_he.dts @@ -25,3 +25,11 @@ pinctrl-0 = <&pinctrl_uart2>; pinctrl-names = "default"; }; + +&i2c1 { + status = "okay"; + pinctrl-0 = <&pinctrl_i2c1>; + pinctrl-names = "default"; +}; + +mikrobus_i2c: &i2c1 {}; From f064d432d69837729593fc34cd880025491f82c3 Mon Sep 17 00:00:00 2001 From: Shreehari HK Date: Thu, 27 Aug 2026 13:57:05 +0530 Subject: [PATCH 161/600] boards: alif: balletto_b1_dk: add mikrobus i2c connector support Expose mikrobus_i2c aliases on Alif Balletto B1 development kit so mikroBUS Click shields can use the existing DesignWare I2C driver and standard shield overlays. B1_DK board gets a mikrobus devicetree fragment that assigns the click-header I2C instance, applies the board I2C pinmux. This allows Click boards such as the Mikroe Weather Click (BME280) to be used with --shield without board-specific sample overlays. Signed-off-by: Shreehari HK --- .../balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.dts b/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.dts index b171f427c651..2b6a9196b3db 100644 --- a/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.dts +++ b/boards/alif/balletto_b1_dk/balletto_b1_dk_ab1c1f4m51820ph0.dts @@ -24,3 +24,11 @@ pinctrl-0 = <&pinctrl_uart2>; pinctrl-names = "default"; }; + +&i2c1 { + status = "okay"; + pinctrl-0 = <&pinctrl_i2c1>; + pinctrl-names = "default"; +}; + +mikrobus_i2c: &i2c1 {}; From 2fa0c977b367451a7b4bba5b58d61e37594d1994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 01:47:56 +0200 Subject: [PATCH 162/600] cmake: west: read the topdir through west's Python API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running 'west topdir' pays for west's whole CLI import chain - argparse setup, command discovery, west.app.main - only to print one path. Call west.util.west_topdir() directly instead, which saves around 100 ms per configure. The output is unchanged: west's own CLI computes it the same way, and PurePath().as_posix() reproduces the formatting it applies before printing. The two failure paths are also untouched - a west version import failure stays fatal, while a missing topdir stays tolerated so a tree without a west workspace still configures. 'west build' already passes -DWEST_TOPDIR, so this helps plain cmake invocations and twister, which configure without it. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- cmake/modules/west.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/modules/west.cmake b/cmake/modules/west.cmake index 9b516513e6af..65ab7e85d79a 100644 --- a/cmake/modules/west.cmake +++ b/cmake/modules/west.cmake @@ -101,7 +101,10 @@ if(WEST_VERSION) if(NOT WEST_TOPDIR) execute_process( - COMMAND ${WEST} topdir + COMMAND + ${PYTHON_EXECUTABLE} + -c + "import west.util; from pathlib import PurePath; print(PurePath(west.util.west_topdir()).as_posix())" OUTPUT_VARIABLE WEST_TOPDIR ERROR_QUIET RESULT_VARIABLE west_topdir_result From d33000ddf43a76a0d66dbc4226cfc6b131784486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Thu, 20 Aug 2026 17:47:56 +0200 Subject: [PATCH 163/600] scripts: twister: let ccache reuse objects across test build directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Twister builds every test in its own directory, and ccache keys on that directory: it appears in the compiler command line, and in the hash that keeps debug info apart because Zephyr always builds with -g. An object compiled for one test is therefore never reused by another. Set base_dir and hash_dir for the builds twister spawns. Building 121 qemu_x86 configurations from an empty cache, cache hits go from 1.5% to 70% and wall time from 727 s to 488 s. The gain grows with the number of tests built, as more of them find an object a sibling already compiled. Anything already set in the environment is left alone. A reused object records the build directory it was first compiled in, so its debug info points there; Zephyr passes absolute source paths, so in practice only files generated into the build directory are affected. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Benjamin Cabé --- scripts/pylib/twister/twisterlib/environment.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/pylib/twister/twisterlib/environment.py b/scripts/pylib/twister/twisterlib/environment.py index 70c5a24e0971..26e688eda576 100644 --- a/scripts/pylib/twister/twisterlib/environment.py +++ b/scripts/pylib/twister/twisterlib/environment.py @@ -1116,6 +1116,7 @@ def __init__(self, options : argparse.Namespace, default_options=None) -> None: else: self.board_roots = options.board_root self.outdir = os.path.abspath(options.outdir) + self._setup_ccache() self.snippet_roots = [Path(ZEPHYR_BASE)] self.soc_roots = [Path(ZEPHYR_BASE), Path(ZEPHYR_BASE) / 'subsys' / 'testsuite'] @@ -1145,6 +1146,21 @@ def __init__(self, options : argparse.Namespace, default_options=None) -> None: self.alt_config_root = options.alt_config_root + def _setup_ccache(self) -> None: + """Let ccache reuse objects between the per-test build directories. + + The build directory otherwise appears in the compiler command line and, + because Zephyr builds with -g, in the hash that separates debug info. + The trade-off is that a reused object carries the debug info of the + build directory it came from. That is of little consequence for test + builds, and is limited to files generated into the build directory, as + Zephyr refers to the rest by absolute path. Settings already in the + environment win. + """ + os.environ.setdefault("CCACHE_BASEDIR", self.outdir) + if not {"CCACHE_HASHDIR", "CCACHE_NOHASHDIR"} & os.environ.keys(): + os.environ["CCACHE_NOHASHDIR"] = "true" + def non_default_options(self) -> dict: """Returns current command line options which are set to non-default values.""" diff = {} From ca98838836e6f5c8ec4e6be5e59397eeaa5a6444 Mon Sep 17 00:00:00 2001 From: Corey Wharton Date: Fri, 21 Aug 2026 15:51:53 -0700 Subject: [PATCH 164/600] drivers: i3c: dw: assert config is not NULL in dw_i3c_configure() The I3C_CONFIG_TARGET branch casts the config argument and writes through it without a NULL check. Assert on it up front. Signed-off-by: Corey Wharton --- drivers/i3c/i3c_dw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i3c/i3c_dw.c b/drivers/i3c/i3c_dw.c index eb2eb326ff4d..b3b2ffa084cb 100644 --- a/drivers/i3c/i3c_dw.c +++ b/drivers/i3c/i3c_dw.c @@ -2418,6 +2418,8 @@ static int dw_i3c_configure(const struct device *dev, enum i3c_config_type type, const struct dw_i3c_config *dev_config = dev->config; #endif /* CONFIG_I3C_TARGET */ + __ASSERT((config != NULL), "Configuration should not be NULL"); + if (type == I3C_CONFIG_CONTROLLER) { #ifdef CONFIG_I3C_CONTROLLER ret = dw_i3c_init_scl_timing(dev, config); From 1656e94103fed3af0a10230d475ceb1e9a650fa8 Mon Sep 17 00:00:00 2001 From: Corey Wharton Date: Fri, 21 Aug 2026 16:52:25 -0700 Subject: [PATCH 165/600] drivers: i3c: dw: apply the requested I3C rate immediately dw_i3c_configure() validates a new controller configuration by calling dw_i3c_init_scl_timing() before storing it, but the push-pull timing read scl.i3c back from data->common.ctrl_config, which still held the previous value. A new I3C SCL rate therefore only took effect on the following call. Read it from the passed-in configuration, as the open drain minimums already are. Signed-off-by: Corey Wharton --- drivers/i3c/i3c_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/i3c_dw.c b/drivers/i3c/i3c_dw.c index b3b2ffa084cb..6fdc1328bec7 100644 --- a/drivers/i3c/i3c_dw.c +++ b/drivers/i3c/i3c_dw.c @@ -1739,7 +1739,7 @@ static int dw_i3c_init_scl_timing(const struct device *dev, struct i3c_config_co hcnt = DIV_ROUND_UP(I3C_BUS_THIGH_MAX_NS * (uint64_t)core_rate, I3C_PERIOD_NS) - 1; hcnt = CLAMP(hcnt, SCL_I3C_TIMING_CNT_MIN, SCL_I3C_TIMING_CNT_MAX); - lcnt = DIV_ROUND_UP(core_rate, data->common.ctrl_config.scl.i3c) - hcnt; + lcnt = DIV_ROUND_UP(core_rate, ctrl_cfg->scl.i3c) - hcnt; lcnt = CLAMP(lcnt, SCL_I3C_TIMING_CNT_MIN, SCL_I3C_TIMING_CNT_MAX); scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt); From 214b2f69791057a7e2acb0d150912031ef5c9980 Mon Sep 17 00:00:00 2001 From: Corey Wharton Date: Fri, 21 Aug 2026 15:54:44 -0700 Subject: [PATCH 166/600] drivers: i3c: dw: honor i2c-scl-hz when programming FM timing The i2c-scl-hz property was parsed into ctrl_config.scl.i2c and never read; SCL_I2C_FM_TIMING was always computed for the fixed I3C_BUS_I2C_FM_SCL_RATE. Derive it from the configured rate, holding tLOW at the spec floor for the mode that rate falls in, 4.7us at or below 100 kHz and 1.3us above. Fall back to the attached devices' LVRs when unset, and clamp HCNT and LCNT to their 16-bit fields, which the register macros otherwise mask silently. Signed-off-by: Corey Wharton --- drivers/i3c/i3c_dw.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/i3c/i3c_dw.c b/drivers/i3c/i3c_dw.c index 6fdc1328bec7..1af90cacc321 100644 --- a/drivers/i3c/i3c_dw.c +++ b/drivers/i3c/i3c_dw.c @@ -284,6 +284,7 @@ LOG_MODULE_REGISTER(i3c_dw, CONFIG_I3C_DW_LOG_LEVEL); #define SCL_I2C_FM_TIMING 0xbc #define SCL_I2C_FM_TIMING_HCNT(x) (((x) << 16) & GENMASK(31, 16)) #define SCL_I2C_FM_TIMING_LCNT(x) ((x) & GENMASK(15, 0)) +#define SCL_I2C_FM_TIMING_CNT_MAX 0xffff #define SCL_I2C_FMP_TIMING 0xc0 #define SCL_I2C_FMP_TIMING_HCNT(x) (((x) << 16) & GENMASK(23, 16)) @@ -350,6 +351,7 @@ LOG_MODULE_REGISTER(i3c_dw, CONFIG_I3C_DW_LOG_LEVEL); #define I3C_BUS_SDR2_SCL_RATE 6000000 #define I3C_BUS_SDR3_SCL_RATE 4000000 #define I3C_BUS_SDR4_SCL_RATE 2000000 +#define I3C_BUS_I2C_SM_TLOW_MIN_NS 4700 #define I3C_BUS_I2C_FM_TLOW_MIN_NS 1300 #define I3C_BUS_I2C_FMP_TLOW_MIN_NS 500 #define I3C_BUS_THIGH_MAX_NS 41 @@ -361,6 +363,7 @@ LOG_MODULE_REGISTER(i3c_dw, CONFIG_I3C_DW_LOG_LEVEL); #define I3C_BUS_TYP_I3C_SCL_RATE 12500000 #define I3C_BUS_I2C_FM_PLUS_SCL_RATE 1000000 #define I3C_BUS_I2C_FM_SCL_RATE 400000 +#define I3C_BUS_I2C_SM_SCL_RATE 100000 #define I3C_BUS_TLOW_OD_MIN_NS 200 #define I3C_HOT_JOIN_ADDR 0x02 @@ -1708,7 +1711,7 @@ static int dw_i3c_init_scl_timing(const struct device *dev, struct i3c_config_co struct dw_i3c_data *data = dev->data; uint32_t core_rate, scl_timing; #ifdef CONFIG_I3C_CONTROLLER - uint32_t hcnt, lcnt, fmlcnt, fmplcnt, free_cnt; + uint32_t hcnt, lcnt, fmlcnt, fmplcnt, free_cnt, i2c_scl_hz, tlow_min_ns; #endif /* CONFIG_I3C_CONTROLLER */ if (clock_control_get_rate(config->clock, config->clock_subsys, &core_rate) != 0) { @@ -1763,8 +1766,24 @@ static int dw_i3c_init_scl_timing(const struct device *dev, struct i3c_config_co sys_write32(scl_timing, config->regs + SCL_I2C_FMP_TIMING); /* I2C FM */ - fmlcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS * (uint64_t)core_rate, I3C_PERIOD_NS); - hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - fmlcnt; + i2c_scl_hz = ctrl_cfg->scl.i2c; + if (i2c_scl_hz == 0) { + /* Not set in devicetree: derive from the LVRs of the attached I2C devices. */ + i2c_scl_hz = i3c_any_i2c_fast_mode(&config->common.dev_list) + ? I3C_BUS_I2C_FM_SCL_RATE + : I3C_BUS_I2C_FM_PLUS_SCL_RATE; + } + i2c_scl_hz = MIN(i2c_scl_hz, I3C_BUS_I2C_FM_SCL_RATE); + + if (i2c_scl_hz <= I3C_BUS_I2C_SM_SCL_RATE) { + tlow_min_ns = I3C_BUS_I2C_SM_TLOW_MIN_NS; + } else { + tlow_min_ns = I3C_BUS_I2C_FM_TLOW_MIN_NS; + } + + fmlcnt = DIV_ROUND_UP(tlow_min_ns * (uint64_t)core_rate, I3C_PERIOD_NS); + fmlcnt = MIN(fmlcnt, SCL_I2C_FM_TIMING_CNT_MAX); + hcnt = MIN(DIV_ROUND_UP(core_rate, i2c_scl_hz) - fmlcnt, SCL_I2C_FM_TIMING_CNT_MAX); scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) | SCL_I2C_FM_TIMING_LCNT(fmlcnt); sys_write32(scl_timing, config->regs + SCL_I2C_FM_TIMING); From ff090b4cfa1010ff25201939c5200e075cd94ae1 Mon Sep 17 00:00:00 2001 From: Corey Wharton Date: Fri, 21 Aug 2026 15:59:18 -0700 Subject: [PATCH 167/600] drivers: i3c: dw: implement i2c_configure() The driver provided no .i2c_api.configure hook, so the I2C bus speed could only be set from devicetree and i2c_configure() faulted on the unset function pointer. Map the standard speed selectors onto ctrl_config.scl.i2c and reprogram the SCL timing, following cdns_i3c_i2c_api_configure(). Speeds above fast mode plus are rejected; the IP has no timing register beyond SCL_I2C_FMP_TIMING. Signed-off-by: Corey Wharton --- drivers/i3c/i3c_dw.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/i3c/i3c_dw.c b/drivers/i3c/i3c_dw.c index 1af90cacc321..b922216ea71e 100644 --- a/drivers/i3c/i3c_dw.c +++ b/drivers/i3c/i3c_dw.c @@ -1192,6 +1192,50 @@ static int dw_i3c_i2c_api_transfer(const struct device *dev, struct i2c_msg *msg return dw_i3c_i2c_transfer(dev, i2c_dev, msgs, num_msgs); } + +static int dw_i3c_init_scl_timing(const struct device *dev, struct i3c_config_controller *ctrl_cfg); + +/** + * @brief Configure I2C operation of a host controller. + * + * @see i2c_configure + * + * @param dev Pointer to device driver instance. + * @param dev_config @see i2c_configure + * + * @return @see i2c_configure + */ +static int dw_i3c_i2c_api_configure(const struct device *dev, uint32_t dev_config) +{ + struct dw_i3c_data *data = dev->data; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; + uint32_t i2c_scl_hz; + int ret; + + /* Note: this only affects devices configured for FM in the device tree. */ + switch (I2C_SPEED_GET(dev_config)) { + case I2C_SPEED_STANDARD: + i2c_scl_hz = 100000; + break; + case I2C_SPEED_FAST: + i2c_scl_hz = 400000; + break; + case I2C_SPEED_FAST_PLUS: + i2c_scl_hz = 1000000; + break; + default: + return -EINVAL; + } + + k_mutex_lock(&data->mt, K_FOREVER); + + ctrl_config->scl.i2c = i2c_scl_hz; + ret = dw_i3c_init_scl_timing(dev, ctrl_config); + + k_mutex_unlock(&data->mt); + + return ret; +} #endif /* CONFIG_I3C_CONTROLLER */ #ifdef CONFIG_I3C_USE_IBI #ifdef CONFIG_I3C_CONTROLLER @@ -2918,6 +2962,7 @@ static int dw_i3c_pm_ctrl(const struct device *dev, enum pm_device_action action static DEVICE_API(i3c, dw_i3c_api) = { #ifdef CONFIG_I3C_CONTROLLER + .i2c_api.configure = dw_i3c_i2c_api_configure, .i2c_api.transfer = dw_i3c_i2c_api_transfer, .i2c_api.recover_bus = dw_i3c_recover_bus, #ifdef CONFIG_I2C_RTIO From 775594bc5ba3901cc64c9bcf2af5a9532d16dbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 14 Aug 2026 09:39:48 +0000 Subject: [PATCH 168/600] drivers: i2c: dw: use inclusive terminology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update comment 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 DesignWare databook (e.g. IC_CON union bitfields, IC_TAR union bitfield ic_10bitaddr_master, IC_TX_ABRT_SOURCE union bitfields) are kept unchanged. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/i2c/i2c_dw.c | 92 ++++++++++++++++++++++---------------------- drivers/i2c/i2c_dw.h | 12 +++--- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/drivers/i2c/i2c_dw.c b/drivers/i2c/i2c_dw.c index e04aadb55ccc..1b8e1baafc47 100644 --- a/drivers/i2c/i2c_dw.c +++ b/drivers/i2c/i2c_dw.c @@ -516,8 +516,8 @@ static inline void i2c_dw_transfer_complete(const struct device *dev) #ifdef CONFIG_I2C_TARGET static inline uint8_t i2c_dw_read_byte_non_blocking(const struct device *dev); static inline void i2c_dw_write_byte_non_blocking(const struct device *dev, uint8_t data); -static void i2c_dw_slave_read_clear_intr_bits(const struct device *dev, - union ic_interrupt_register intr_stat); +static void i2c_dw_target_read_clear_intr_bits(const struct device *dev, + union ic_interrupt_register intr_stat); #endif static void i2c_dw_isr(const struct device *port) @@ -548,7 +548,7 @@ static void i2c_dw_isr(const struct device *port) LOG_DBG("I2C: interrupt received"); - /* Check if we are configured as a master device */ + /* Check if we are configured as a controller device */ if (test_bit_con_master_mode(reg_base)) { #ifdef CONFIG_I2C_DW_LPSS_DMA uint32_t stat = sys_read32(reg_base + IDMA_REG_INTR_STS); @@ -615,43 +615,43 @@ static void i2c_dw_isr(const struct device *port) } else { #ifdef CONFIG_I2C_TARGET - const struct i2c_target_callbacks *slave_cb = dw->slave_cfg->callbacks; - uint32_t slave_activity = test_bit_status_activity(reg_base); + const struct i2c_target_callbacks *target_cb = dw->target_cfg->callbacks; + uint32_t target_activity = test_bit_status_activity(reg_base); uint8_t data; - i2c_dw_slave_read_clear_intr_bits(port, intr_stat); + i2c_dw_target_read_clear_intr_bits(port, intr_stat); if (intr_stat.bits.rx_full) { if (dw->state != I2C_DW_CMD_SEND) { dw->state = I2C_DW_CMD_SEND; dw->read_in_progress = false; - if (slave_cb->write_requested) { - slave_cb->write_requested(dw->slave_cfg); + if (target_cb->write_requested) { + target_cb->write_requested(dw->target_cfg); } } /* FIFO needs to be drained here so we don't miss the next interrupt */ do { data = i2c_dw_read_byte_non_blocking(port); - if (slave_cb->write_received) { - slave_cb->write_received(dw->slave_cfg, data); + if (target_cb->write_received) { + target_cb->write_received(dw->target_cfg, data); } } while (test_bit_status_rfne(reg_base)); } if (intr_stat.bits.rd_req) { - if (slave_activity) { + if (target_activity) { read_clr_rd_req(reg_base); dw->state = I2C_DW_CMD_RECV; if (!dw->read_in_progress) { - if (slave_cb->read_requested) { - slave_cb->read_requested(dw->slave_cfg, &data); + if (target_cb->read_requested) { + target_cb->read_requested(dw->target_cfg, &data); i2c_dw_write_byte_non_blocking(port, data); } dw->read_in_progress = true; } else { - if (slave_cb->read_processed) { - slave_cb->read_processed(dw->slave_cfg, &data); + if (target_cb->read_processed) { + target_cb->read_processed(dw->target_cfg, &data); i2c_dw_write_byte_non_blocking(port, data); } } @@ -660,8 +660,8 @@ static void i2c_dw_isr(const struct device *port) if (intr_stat.bits.stop_det) { read_clr_stop_det(reg_base); - if (slave_cb->stop) { - slave_cb->stop(dw->slave_cfg); + if (target_cb->stop) { + target_cb->stop(dw->target_cfg); } dw->state = I2C_DW_STATE_READY; dw->read_in_progress = false; @@ -675,7 +675,7 @@ static void i2c_dw_isr(const struct device *port) i2c_dw_transfer_complete(port); } -static int i2c_dw_setup(const struct device *dev, uint16_t slave_address) +static int i2c_dw_setup(const struct device *dev, uint16_t target_address) { struct i2c_dw_dev_config *const dw = dev->data; uint32_t value; @@ -685,9 +685,9 @@ static int i2c_dw_setup(const struct device *dev, uint16_t slave_address) #if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS if (!dw->need_setup) { - /* If slave address changed setup is still needed */ + /* If target address changed setup is still needed */ ic_tar.raw = read_tar(reg_base); - if (ic_tar.bits.ic_tar == slave_address) { + if (ic_tar.bits.ic_tar == target_address) { return 0; } } @@ -709,13 +709,13 @@ static int i2c_dw_setup(const struct device *dev, uint16_t slave_address) /* Clear interrupts */ value = read_clr_intr(reg_base); - /* Set master or slave mode - (initialization = slave) */ + /* Set controller or target mode - (initialization = target) */ if (I2C_MODE_CONTROLLER & dw->app_config) { /* * Make sure to set both the master_mode and slave_disable_bit * to both 0 or both 1 */ - LOG_DBG("I2C: host configured as Master Device"); + LOG_DBG("I2C: host configured as Controller Device"); ic_con.bits.master_mode = 1U; ic_con.bits.slave_disable = 1U; } else { @@ -794,11 +794,11 @@ static int i2c_dw_setup(const struct device *dev, uint16_t slave_address) ic_tar.raw = read_tar(reg_base); if (test_bit_con_master_mode(reg_base)) { - /* Set address of target slave */ - ic_tar.bits.ic_tar = slave_address; + /* Set address of target */ + ic_tar.bits.ic_tar = target_address; } else { - /* Set slave address for device */ - write_sar(slave_address, reg_base); + /* Set target address for device */ + write_sar(target_address, reg_base); } /* If I2C_DYNAMIC_TAR_UPDATE configuration parameter is set to Yes (1), @@ -839,7 +839,7 @@ bool i2c_dw_is_busy(const struct device *dev) } static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, - uint16_t slave_address) + uint16_t target_address) { const struct i2c_dw_rom_config *const rom = dev->config; struct i2c_dw_dev_config *const dw = dev->data; @@ -890,7 +890,7 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8 dw->state |= I2C_DW_BUSY; - ret = i2c_dw_setup(dev, slave_address); + ret = i2c_dw_setup(dev, target_address); if (ret) { goto error; } @@ -953,11 +953,12 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8 /* Enable interrupts to trigger ISR */ if (test_bit_con_master_mode(reg_base)) { /* Enable necessary interrupts */ - write_intr_mask((DW_ENABLE_TX_INT_I2C_MASTER | DW_ENABLE_RX_INT_I2C_MASTER), + write_intr_mask((DW_ENABLE_TX_INT_I2C_CONTROLLER | + DW_ENABLE_RX_INT_I2C_CONTROLLER), reg_base); } else { /* Enable necessary interrupts */ - write_intr_mask(DW_ENABLE_TX_INT_I2C_SLAVE, reg_base); + write_intr_mask(DW_ENABLE_TX_INT_I2C_TARGET, reg_base); } /* Wait for transfer to be done */ @@ -1090,9 +1091,10 @@ static int i2c_dw_configure(const struct device *dev, uint32_t config) value = read_clr_intr(reg_base); /* - * TEMPORARY HACK - The I2C does not work in any mode other than Master - * currently. This "hack" forces us to always be configured for master - * mode, until we can verify that Slave mode works correctly. + * TEMPORARY HACK - The I2C does not work in any mode other than + * Controller currently. This "hack" forces us to always be configured + * for controller mode, until we can verify that Target mode works + * correctly. */ dw->app_config |= I2C_MODE_CONTROLLER; @@ -1138,7 +1140,7 @@ static inline void i2c_dw_write_byte_non_blocking(const struct device *dev, uint write_cmd_data(data, reg_base); } -static int i2c_dw_set_master_mode(const struct device *dev) +static int i2c_dw_set_controller_mode(const struct device *dev) { union ic_comp_param_1_register ic_comp_param_1; mm_reg_t reg_base = DEVICE_MMIO_GET(dev); @@ -1161,7 +1163,7 @@ static int i2c_dw_set_master_mode(const struct device *dev) return 0; } -static int i2c_dw_set_slave_mode(const struct device *dev, struct i2c_target_config *cfg) +static int i2c_dw_set_target_mode(const struct device *dev, struct i2c_target_config *cfg) { mm_reg_t reg_base = DEVICE_MMIO_GET(dev); union ic_con_register ic_con; @@ -1191,12 +1193,12 @@ static int i2c_dw_set_slave_mode(const struct device *dev, struct i2c_target_con write_tx_tl(0, reg_base); write_rx_tl(0, reg_base); - LOG_DBG("I2C: Host registered as Slave Device"); + LOG_DBG("I2C: Host registered as Target Device"); return 0; } -static int i2c_dw_slave_register(const struct device *dev, struct i2c_target_config *cfg) +static int i2c_dw_target_register(const struct device *dev, struct i2c_target_config *cfg) { struct i2c_dw_dev_config *const dw = dev->data; mm_reg_t reg_base = DEVICE_MMIO_GET(dev); @@ -1208,8 +1210,8 @@ static int i2c_dw_slave_register(const struct device *dev, struct i2c_target_con } dw->read_in_progress = false; - dw->slave_cfg = cfg; - ret = i2c_dw_set_slave_mode(dev, cfg); + dw->target_cfg = cfg; + ret = i2c_dw_set_target_mode(dev, cfg); write_intr_mask(DW_INTR_MASK_RX_FULL | DW_INTR_MASK_RD_REQ | DW_INTR_MASK_TX_ABRT | DW_INTR_MASK_STOP_DET | DW_INTR_MASK_START_DET, @@ -1218,21 +1220,21 @@ static int i2c_dw_slave_register(const struct device *dev, struct i2c_target_con return ret; } -static int i2c_dw_slave_unregister(const struct device *dev, struct i2c_target_config *cfg) +static int i2c_dw_target_unregister(const struct device *dev, struct i2c_target_config *cfg) { struct i2c_dw_dev_config *const dw = dev->data; int ret; dw->state = I2C_DW_STATE_READY; - ret = i2c_dw_set_master_mode(dev); + ret = i2c_dw_set_controller_mode(dev); pm_device_runtime_put(dev); return ret; } -static void i2c_dw_slave_read_clear_intr_bits(const struct device *dev, - union ic_interrupt_register intr_stat) +static void i2c_dw_target_read_clear_intr_bits(const struct device *dev, + union ic_interrupt_register intr_stat) { struct i2c_dw_dev_config *const dw = dev->data; mm_reg_t reg_base = DEVICE_MMIO_GET(dev); @@ -1466,8 +1468,8 @@ static DEVICE_API(i2c, funcs) = { .configure = i2c_dw_runtime_configure, .transfer = i2c_dw_transfer, #ifdef CONFIG_I2C_TARGET - .target_register = i2c_dw_slave_register, - .target_unregister = i2c_dw_slave_unregister, + .target_register = i2c_dw_target_register, + .target_unregister = i2c_dw_target_unregister, #endif /* CONFIG_I2C_TARGET */ #ifdef CONFIG_I2C_RTIO .iodev_submit = i2c_iodev_submit_fallback, diff --git a/drivers/i2c/i2c_dw.h b/drivers/i2c/i2c_dw.h index 4691497dabbe..0e1061dd13d7 100644 --- a/drivers/i2c/i2c_dw.h +++ b/drivers/i2c/i2c_dw.h @@ -54,21 +54,21 @@ typedef int (*i2c_api_check_bus_t)(const struct device *dev); #define I2C_DW_STUCK_ERR_MASK (I2C_DW_SCL_STUCK | I2C_DW_SDA_STUCK | I2C_DW_USER_ABRT) #ifdef CONFIG_I2C_DW_EXTENDED_SUPPORT -#define DW_ENABLE_TX_INT_I2C_MASTER \ +#define DW_ENABLE_TX_INT_I2C_CONTROLLER \ (DW_INTR_STAT_TX_OVER | DW_INTR_STAT_TX_EMPTY | DW_INTR_STAT_TX_ABRT | \ DW_INTR_STAT_STOP_DET | DW_INTR_STAT_SCL_STUCK_LOW) #else -#define DW_ENABLE_TX_INT_I2C_MASTER \ +#define DW_ENABLE_TX_INT_I2C_CONTROLLER \ (DW_INTR_STAT_TX_OVER | DW_INTR_STAT_TX_EMPTY | DW_INTR_STAT_TX_ABRT | \ DW_INTR_STAT_STOP_DET) #endif -#define DW_ENABLE_RX_INT_I2C_MASTER \ +#define DW_ENABLE_RX_INT_I2C_CONTROLLER \ (DW_INTR_STAT_RX_UNDER | DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_FULL | \ DW_INTR_STAT_STOP_DET) -#define DW_ENABLE_TX_INT_I2C_SLAVE \ +#define DW_ENABLE_TX_INT_I2C_TARGET \ (DW_INTR_STAT_RD_REQ | DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_STOP_DET) -#define DW_ENABLE_RX_INT_I2C_SLAVE (DW_INTR_STAT_RX_FULL | DW_INTR_STAT_STOP_DET) +#define DW_ENABLE_RX_INT_I2C_TARGET (DW_INTR_STAT_RX_FULL | DW_INTR_STAT_STOP_DET) #define DW_DISABLE_ALL_I2C_INT 0x00000000 @@ -212,7 +212,7 @@ struct i2c_dw_dev_config { bool xfr_status; #endif - struct i2c_target_config *slave_cfg; + struct i2c_target_config *target_cfg; i2c_api_recover_bus_t recover_bus_cb; struct device *recover_bus_dev; From b9ad72159d33b9f04ddea3df24408c1e0d749310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 14 Aug 2026 09:39:49 +0000 Subject: [PATCH 169/600] drivers: i2c: ite: use inclusive terminology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update comments to use the controller/target terminology ratified by coding guideline A.2 and already used by the Zephyr I2C API. Identifiers mirroring the ITE datasheet (e.g. ITE SMBus host/target register mnemonics SMB_* and their verbatim datasheet register-title comments, ITE IT8XXX2_SMB_* / IT8XXX2_I2C_* register accessor macros, ITE command-queue datasheet bit names) are kept unchanged. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- drivers/i2c/i2c_ite_enhance.c | 6 +++--- drivers/i2c/i2c_ite_it51xxx.c | 2 +- drivers/i2c/i2c_ite_it8xxx2.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/i2c_ite_enhance.c b/drivers/i2c/i2c_ite_enhance.c index eb780acd2142..c5a422f738e1 100644 --- a/drivers/i2c/i2c_ite_enhance.c +++ b/drivers/i2c/i2c_ite_enhance.c @@ -53,7 +53,7 @@ LOG_MODULE_REGISTER(i2c_ite_enhance, CONFIG_I2C_LOG_LEVEL); #define I2C_CQ_CMD_L_P BIT(5) /* E (End) is this device end flag. */ #define I2C_CQ_CMD_L_E BIT(4) -/* LA (Last ACK) is Last ACK in master receiver. */ +/* LA (Last ACK) is Last ACK in controller receiver. */ #define I2C_CQ_CMD_L_LA BIT(3) /* bit[2:0] are number of transfer out or receive data which depends on R/W. */ #define I2C_CQ_CMD_L_NUM_BIT_2_0 GENMASK(2, 0) @@ -194,7 +194,7 @@ enum enhanced_i2c_ctl { E_RX_MODE = 0x80, /* State reset and hardware reset */ E_STS_AND_HW_RST = (E_STS_RST | E_HW_RST), - /* Generate start condition and transmit slave address */ + /* Generate start condition and transmit target address */ E_START_ID = (E_INT_EN | E_MODE_SEL | E_ACK | E_START | E_HW_RST), /* Generate stop condition */ E_FINISH = (E_INT_EN | E_MODE_SEL | E_ACK | E_STOP | E_HW_RST), @@ -468,7 +468,7 @@ static void i2c_pio_trans_data(const struct device *dev, uint32_t nack = 0; if (first_byte) { - /* First byte must be slave address. */ + /* First byte must be target address. */ IT8XXX2_I2C_DTR(base) = trans_data | (direct == RX_DIRECT ? BIT(0) : 0); /* start or repeat start signal. */ diff --git a/drivers/i2c/i2c_ite_it51xxx.c b/drivers/i2c/i2c_ite_it51xxx.c index 68d79d260f78..6fbac14cf6c0 100644 --- a/drivers/i2c/i2c_ite_it51xxx.c +++ b/drivers/i2c/i2c_ite_it51xxx.c @@ -1956,7 +1956,7 @@ static int i2c_it51xxx_init(const struct device *dev) /* Enable SMBus function */ sys_write8(SMB_SMD_TO_EN | SMB_SMH_EN, config->host_base + SMB_HOCTL2); - /* Kill SMBus host transaction. And enable the interrupt for the master interface */ + /* Kill SMBus host transaction. And enable the interrupt for the controller interface */ sys_write8(SMB_KILL | SMB_INTREN, config->host_base + SMB_HOCTL); sys_write8(SMB_INTREN, config->host_base + SMB_HOCTL); /* W/C host status register */ diff --git a/drivers/i2c/i2c_ite_it8xxx2.c b/drivers/i2c/i2c_ite_it8xxx2.c index 237a7147c281..47ffbadbcaa5 100644 --- a/drivers/i2c/i2c_ite_it8xxx2.c +++ b/drivers/i2c/i2c_ite_it8xxx2.c @@ -766,7 +766,7 @@ int __soc_ram_code i2c_tran_read(const struct device *dev) IT8XXX2_SMB_SMHEN; /* * bit0, Direction of the host transfer. - * bit[1:7}, Address of the targeted slave. + * bit[1:7}, Address of the target. */ IT8XXX2_SMB_TRASLA(base) = (uint8_t)(data->addr_16bit << 1) | IT8XXX2_SMB_DIR; @@ -848,7 +848,7 @@ int __soc_ram_code i2c_tran_write(const struct device *dev) IT8XXX2_SMB_SMHEN; /* * bit0, Direction of the host transfer. - * bit[1:7}, Address of the targeted slave. + * bit[1:7}, Address of the target. */ IT8XXX2_SMB_TRASLA(base) = (uint8_t)data->addr_16bit << 1; /* Send first byte */ @@ -1123,13 +1123,13 @@ static int i2c_it8xxx2_init(const struct device *dev) * bit1, Enable to communicate with I2C device * and support I2C-compatible cycles. * bit4, This bit controls the reset mechanism - * of SMBus master to handle the SMDAT + * of SMBus controller to handle the SMDAT * line low if 25ms reg timeout. */ IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | IT8XXX2_SMB_SMHEN; /* * bit1, Kill SMBus host transaction. - * bit0, Enable the interrupt for the master interface. + * bit0, Enable the interrupt for the controller interface. */ IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_KILL | IT8XXX2_SMB_SMHEN; IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SMHEN; From c658938e586ea84c2be1963d08d4a3df01a9db07 Mon Sep 17 00:00:00 2001 From: Sven Ginka Date: Tue, 25 Aug 2026 11:26:38 +0200 Subject: [PATCH 170/600] board: sensry: ganymed bob/sk - adding random MAC address With this commit, we set a random mac adress for the ganymed boards. Signed-off-by: Sven Ginka --- boards/sensry/ganymed_bob/ganymed_bob_sy120_gbm.dts | 2 +- boards/sensry/ganymed_bob/ganymed_bob_sy120_gen1.dts | 2 +- boards/sensry/ganymed_sk/ganymed_sk_sy120_gbm.dts | 2 +- boards/sensry/ganymed_sk/ganymed_sk_sy120_gen1.dts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/boards/sensry/ganymed_bob/ganymed_bob_sy120_gbm.dts b/boards/sensry/ganymed_bob/ganymed_bob_sy120_gbm.dts index 7583efe99ca3..7a12cd41993b 100644 --- a/boards/sensry/ganymed_bob/ganymed_bob_sy120_gbm.dts +++ b/boards/sensry/ganymed_bob/ganymed_bob_sy120_gbm.dts @@ -36,6 +36,6 @@ ð0 { status = "okay"; - + zephyr,random-mac-address; phy-handle = <&pyh0>; }; diff --git a/boards/sensry/ganymed_bob/ganymed_bob_sy120_gen1.dts b/boards/sensry/ganymed_bob/ganymed_bob_sy120_gen1.dts index 7583efe99ca3..7a12cd41993b 100644 --- a/boards/sensry/ganymed_bob/ganymed_bob_sy120_gen1.dts +++ b/boards/sensry/ganymed_bob/ganymed_bob_sy120_gen1.dts @@ -36,6 +36,6 @@ ð0 { status = "okay"; - + zephyr,random-mac-address; phy-handle = <&pyh0>; }; diff --git a/boards/sensry/ganymed_sk/ganymed_sk_sy120_gbm.dts b/boards/sensry/ganymed_sk/ganymed_sk_sy120_gbm.dts index 3ef730a681fc..5387d1b8e172 100644 --- a/boards/sensry/ganymed_sk/ganymed_sk_sy120_gbm.dts +++ b/boards/sensry/ganymed_sk/ganymed_sk_sy120_gbm.dts @@ -49,6 +49,6 @@ ð0 { status = "okay"; - + zephyr,random-mac-address; phy-handle = <&pyh0>; }; diff --git a/boards/sensry/ganymed_sk/ganymed_sk_sy120_gen1.dts b/boards/sensry/ganymed_sk/ganymed_sk_sy120_gen1.dts index 3ef730a681fc..5387d1b8e172 100644 --- a/boards/sensry/ganymed_sk/ganymed_sk_sy120_gen1.dts +++ b/boards/sensry/ganymed_sk/ganymed_sk_sy120_gen1.dts @@ -49,6 +49,6 @@ ð0 { status = "okay"; - + zephyr,random-mac-address; phy-handle = <&pyh0>; }; From 41fd0932f36128b8b86d24b48ee667ca8889592c Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Thu, 27 Aug 2026 13:40:31 -0400 Subject: [PATCH 171/600] net: pkt: drop obsolete NULL argument log in unref net_pkt_unref() already returns early for a NULL packet, so passing NULL is a harmless no-op. In debug builds it also logged an error line for that case, which pushed callers to add their own NULL check just to keep the log quiet. That check duplicates the early return and the log no longer serves a purpose. Remove the log so a NULL argument is ignored silently and callers do not need to guard the call. Signed-off-by: Benjamin Perseghetti --- subsys/net/ip/net_pkt.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c index 7232327961d2..7bb50f847259 100644 --- a/subsys/net/ip/net_pkt.c +++ b/subsys/net/ip/net_pkt.c @@ -533,9 +533,6 @@ void net_pkt_unref(struct net_pkt *pkt) atomic_val_t ref; if (!pkt) { -#if NET_LOG_LEVEL >= LOG_LEVEL_DBG - NET_ERR("*** ERROR *** pkt %p (%s():%d)", pkt, caller, line); -#endif return; } From ef2580bc50e0d0abefb0210d8a4774a4fd54fbc4 Mon Sep 17 00:00:00 2001 From: Benjamin Perseghetti Date: Mon, 24 Aug 2026 00:06:24 -0400 Subject: [PATCH 172/600] net: gptp: release stranded Sync on port reset A Sync waiting for its transmit timestamp is stranded when the port that was to produce it goes down or stops being capable: the sync send state machine is left in SEND_FUP waiting for a timestamp that never comes. The reset path returns without releasing the held Sync or its registered timestamp callback, so the packet reference is dropped on the floor at the next SEND_SYNC, which loses a packet and its buffers from the pool for good, and the stale callback keeps pointing at a packet that is never transmitted again, leaving no later Sync timestamped. Unregister the timestamp callback through the new gptp_sync_timestamp_cb_unregister(), then release the pending Sync. The callback is removed first so a timestamp delivered in between cannot match a packet whose reference was already dropped. The next Sync then registers a callback of its own. Signed-off-by: Benjamin Perseghetti --- subsys/net/l2/ethernet/gptp/gptp_md.c | 13 +++++++++++++ subsys/net/l2/ethernet/gptp/gptp_messages.c | 10 ++++++++++ subsys/net/l2/ethernet/gptp/gptp_messages.h | 10 ++++++++++ 3 files changed, 33 insertions(+) diff --git a/subsys/net/l2/ethernet/gptp/gptp_md.c b/subsys/net/l2/ethernet/gptp/gptp_md.c index 678a4b3c9941..673444ad77d8 100644 --- a/subsys/net/l2/ethernet/gptp/gptp_md.c +++ b/subsys/net/l2/ethernet/gptp/gptp_md.c @@ -855,6 +855,19 @@ static void gptp_md_sync_send_state_machine(int port) port_ds = GPTP_PORT_DS(port); if ((!port_ds->ptt_port_enabled) || !port_ds->as_capable) { + /* A Sync waiting here for a transmit timestamp will never be + * given one: the port that was to produce it is down or no + * longer capable. Unregister the callback before dropping + * the packet reference so a late timestamp cannot match the + * packet, then release it, or its reference and the stale + * callback are leaked. + */ + gptp_sync_timestamp_cb_unregister(port); + + net_pkt_unref(state->sync_ptr); + state->sync_ptr = NULL; + + state->md_sync_timestamp_avail = false; state->rcvd_md_sync = false; state->state = GPTP_SYNC_SEND_INITIALIZING; diff --git a/subsys/net/l2/ethernet/gptp/gptp_messages.c b/subsys/net/l2/ethernet/gptp/gptp_messages.c index 5134b710208f..98137cc4ee12 100644 --- a/subsys/net/l2/ethernet/gptp/gptp_messages.c +++ b/subsys/net/l2/ethernet/gptp/gptp_messages.c @@ -822,6 +822,16 @@ void gptp_send_sync(int port, struct net_pkt *pkt) net_if_queue_tx(net_pkt_iface(pkt), pkt); } +void gptp_sync_timestamp_cb_unregister(int port) +{ + if (!sync_cb_registered[port - 1]) { + return; + } + + net_if_unregister_timestamp_cb(&sync_timestamp_cb[port - 1]); + sync_cb_registered[port - 1] = false; +} + void gptp_send_follow_up(int port, struct net_pkt *pkt) { GPTP_STATS_INC(port, tx_fup_count); diff --git a/subsys/net/l2/ethernet/gptp/gptp_messages.h b/subsys/net/l2/ethernet/gptp/gptp_messages.h index edabb6d959eb..7d45995096d8 100644 --- a/subsys/net/l2/ethernet/gptp/gptp_messages.h +++ b/subsys/net/l2/ethernet/gptp/gptp_messages.h @@ -450,6 +450,16 @@ void gptp_handle_signaling(int port, struct net_pkt *pkt); */ void gptp_send_sync(int port, struct net_pkt *pkt); +/** + * @brief Unregister the Sync transmit timestamp callback. + * + * Unregisters the timestamp callback of a Sync that will not be + * timestamped, so that the next Sync registers one of its own. + * + * @param port gPTP port number. + */ +void gptp_sync_timestamp_cb_unregister(int port); + /** * @brief Send a Follow Up message. * From 18d9280e4a400e14f96bcfceeb9017e62b4abefe Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Wed, 26 Aug 2026 10:07:37 +0300 Subject: [PATCH 173/600] soc: adi: max32: Use portable IRQ controls APIs Replace direct NVIC_SetPendingIRQ()/NVIC_EnableIRQ() calls with k_irq_set_pending() and k_irq_enable() to reduce the dependence on cmsis_core.h. Signed-off-by: Tahsin Mutlugun --- soc/adi/max32/power.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soc/adi/max32/power.c b/soc/adi/max32/power.c index 1fa0935cbefb..2be480ecc107 100644 --- a/soc/adi/max32/power.c +++ b/soc/adi/max32/power.c @@ -111,8 +111,8 @@ void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) MXC_LP_GetGPIOWakeupEnable(gpio_wakeup_sources[i].port) & MXC_LP_GetGPIOWakeupStatus(gpio_wakeup_sources[i].port); if (wakeup_status) { - NVIC_EnableIRQ(gpio_wakeup_sources[i].irq); - NVIC_SetPendingIRQ(gpio_wakeup_sources[i].irq); + k_irq_enable(gpio_wakeup_sources[i].irq); + k_irq_set_pending(gpio_wakeup_sources[i].irq); } } From f008ab3b23b53fbae5af82091f315864faeeb9c1 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Wed, 26 Aug 2026 09:13:04 +0200 Subject: [PATCH 174/600] west: blobs: verify the checksum of each downloaded URL A server can answer a blob download with a success status but a bogus payload, for example redirecting a raw file request for a missing repository to its sign-in page with a 200 status. Such a download passed the fetch step and made 'west blobs fetch' stop at the first URL, only to fail the checksum verification afterwards, without ever trying the remaining fallback URLs. Verify the SHA-256 digest of each download inside the URL loop and treat a mismatch like a failed download, so the next URL is tried. Success and failures are reported where they happen, returning early on the first verified download. If every URL yields a mismatch the last download is kept, preserving the detailed error message of the final verification step. Assisted-by: Claude:claude-fable-5 Signed-off-by: Pieter De Gendt --- scripts/west_commands/blobs.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/scripts/west_commands/blobs.py b/scripts/west_commands/blobs.py index 0d494a0b5b8a..026312b1ee89 100644 --- a/scripts/west_commands/blobs.py +++ b/scripts/west_commands/blobs.py @@ -193,14 +193,19 @@ def get_cached_blob(self, blob, cache_dirs: list) -> Path | None: return None def download_blob(self, blob, path): - '''Download a blob from its url to a given path.''' + '''Download a blob from its url to a given path. + + Each URL is tried in order until one provides a download with a + matching checksum. A download whose checksum does not match is + treated like a failed download, as a server may respond with a + bogus payload and a success status. + ''' urls = blob['url'] if not isinstance(urls, list): urls = (urls,) - valid_url = None - has_error = False - for url in urls: + downloaded = False + for i, url in enumerate(urls): scheme = blob.get('fetcher') or urlparse(url).scheme self.dbg(f'Fetching blob from url {url} with {scheme} to path: {path}') import fetchers @@ -218,15 +223,20 @@ def download_blob(self, blob, path): fetcher.fetch(self, single_url_blob, path) except ZephyrBlobException as e: self.wrn(e) - has_error = True - else: - valid_url = url - break + continue + + downloaded = True + if zephyr_module.get_blob_status(path, blob['sha256']) == zephyr_module.BLOB_PRESENT: + if i > 0: + self.inf(f'Fallback URL worked: {url}') + return + # Not necessarily an attack: a server can answer a raw file + # request with an HTML sign-in page and a 200 status. + self.wrn(f'Checksum mismatch for blob downloaded from {url}') - if valid_url is None: + if not downloaded: raise ZephyrBlobException('No URL worked for this blob') - if has_error: - self.inf(f'Fallback URL worked: {valid_url}') + # verify_blob() will report the detailed checksum error later. def fetch_blob(self, args, blob): """ From 12caf605bf822b5fec818beb88a763f057cb441e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 25 Aug 2026 23:01:49 +0000 Subject: [PATCH 175/600] net: sockets: skip recv callback re-registration on every send MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zsock_sendto_ctx() called net_context_recv() before every datagram or stream send to (re-)register zsock_received_cb. After the first registration this is a no-op: it takes the context lock, re-runs bind_default(), rewrites the callback pointers and re-copies the local/remote sockaddrs into the connection table entry on every single send() call. The registration parameters can only change via bind(), connect() or accept(), and those paths already (re-)register the callback themselves. Skip the per-send call when zsock_received_cb is already registered and a connection handler exists. Offloaded contexts never set conn_handler and therefore keep the previous per-send behaviour. Measured with callgrind on native_sim/native/64 (UDP loopback, 32-byte datagrams): zsock_sendto_ctx() drops from 9955 to 9523 instructions per call (-4.3%), removing one context mutex lock/unlock pair and one connection-table update per send. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- subsys/net/lib/sockets/sockets_inet.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/subsys/net/lib/sockets/sockets_inet.c b/subsys/net/lib/sockets/sockets_inet.c index 07eb1da2bc72..f95d6955a6ee 100644 --- a/subsys/net/lib/sockets/sockets_inet.c +++ b/subsys/net/lib/sockets/sockets_inet.c @@ -663,9 +663,11 @@ ssize_t zsock_sendto_ctx(struct net_context *ctx, const void *buf, size_t len, end = sys_timepoint_calc(timeout); /* Register the callback before sending in order to receive the response - * from the peer. + * from the peer. Once registered, a context with a connection handler + * needs no update. */ - if (!sock_is_eof(ctx)) { + if (!sock_is_eof(ctx) && + (ctx->recv_cb != zsock_received_cb || ctx->conn_handler == NULL)) { status = net_context_recv(ctx, zsock_received_cb, K_NO_WAIT, ctx->user_data); if (status < 0) { From 988b4083d26268ce7fb55de1c608f0adae5c319c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Tue, 25 Aug 2026 23:08:27 +0000 Subject: [PATCH 176/600] net: context: drop O(N) context scan from the per-packet RX path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit net_context_packet_received() and net_context_raw_packet_received() resolved the owning context of a connection with find_context(), a linear scan of the whole contexts[] array comparing conn_handler pointers, executed for every delivered UDP/RAW datagram. The connection layer already stores the owning context in conn->context (set at net_conn_register() time), so use it directly and keep the exact validity checks the scan performed: the context must be in use and the connection must still be its current handler. With the default CONFIG_NET_MAX_CONTEXTS=6 this saves a measured 10 instructions per received datagram (callgrind, native_sim loopback); the win grows linearly with CONFIG_NET_MAX_CONTEXTS on larger systems, and the RX fast path no longer depends on the context table size. Signed-off-by: Benjamin Cabé Assisted-by: Claude:fable-5 --- subsys/net/ip/net_context.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index bf5eb95989b7..84dbd9e4842d 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -1270,19 +1270,19 @@ int net_context_bind(struct net_context *context, const struct net_sockaddr *add static inline struct net_context *find_context(void *conn_handler) { - int i; + struct net_conn *conn = conn_handler; + struct net_context *context = conn->context; - for (i = 0; i < NET_MAX_CONTEXT; i++) { - if (!net_context_is_used(&contexts[i])) { - continue; - } - - if (contexts[i].conn_handler == conn_handler) { - return &contexts[i]; - } + /* The connection already points back at its owning context; just + * apply the same validity checks the previous linear scan of + * contexts[] performed. + */ + if (context == NULL || !net_context_is_used(context) || + context->conn_handler != conn_handler) { + return NULL; } - return NULL; + return context; } int net_context_listen(struct net_context *context, int backlog) From b39f86ea61d0a0fdd7e9f145110649394e2c22ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 26 Aug 2026 14:00:26 +0200 Subject: [PATCH 177/600] net: ethernet: handle forwarded packets without headroom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Packets allocated by another L2 do not have the Ethernet header headroom reserved by ethernet_l2_alloc(). Use a separate header fragment when a forwarded packet cannot accommodate the reserved Ethernet header in its first fragment. Assisted-by: Codex:GPT-5 Signed-off-by: Fin Maaß --- subsys/net/l2/ethernet/ethernet.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index 4d993ae42d58..3b525efe5785 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -867,10 +867,16 @@ static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx, net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC; reserve_ll_header = get_reserve_ll_header_size(is_vlan); - if (reserve_ll_header > 0) { + if ((reserve_ll_header > 0) && (reserve_ll_header <= net_buf_headroom(pkt->buffer))) { hdr_len = reserve_ll_header; hdr_frag = pkt->buffer; } else { + /* + * Packets can be allocated by a different L2 and forwarded to + * Ethernet. Such packets do not have the Ethernet header space + * reserved by ethernet_l2_alloc(), so use a separate fragment. + */ + reserve_ll_header = 0U; hdr_len = IS_ENABLED(CONFIG_NET_VLAN) ? sizeof(struct net_eth_vlan_hdr) : sizeof(struct net_eth_hdr); From 07aa7454e523598690254fb08361a5c31815f448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 26 Aug 2026 14:00:33 +0200 Subject: [PATCH 178/600] tests: net: route: cover Ethernet header reserve forwarding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an IPv6 route configuration that enables Ethernet header reservation for a packet received through another L2 and sent through Ethernet. This guards the fallback for forwarded packets without Ethernet headroom. Assisted-by: Codex:GPT-5 Signed-off-by: Fin Maaß --- tests/net/route/ipv6/tests.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/net/route/ipv6/tests.yaml b/tests/net/route/ipv6/tests.yaml index 1e8a01d41dbd..0ee4819a4f64 100644 --- a/tests/net/route/ipv6/tests.yaml +++ b/tests/net/route/ipv6/tests.yaml @@ -15,3 +15,10 @@ tests: - CONFIG_ETH_DRIVER=n - CONFIG_ASSERT=y - CONFIG_NET_IF_MAX_IPV6_COUNT=3 + net.route.ipv6.reserve_header: + extra_configs: + - CONFIG_NET_L2_ETHERNET=y + - CONFIG_NET_L2_ETHERNET_RESERVE_HEADER=y + - CONFIG_ETH_DRIVER=n + - CONFIG_ASSERT=y + - CONFIG_NET_IF_MAX_IPV6_COUNT=3 From ec17fcec5290ed2269db8422f1ace629555efebb Mon Sep 17 00:00:00 2001 From: Fabian Blatz Date: Wed, 26 Aug 2026 17:01:58 +0200 Subject: [PATCH 179/600] modules: lvgl: Format CMakeLists Format the CMakeLists to be compliant. Signed-off-by: Fabian Blatz --- modules/lvgl/CMakeLists.txt | 122 ++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/modules/lvgl/CMakeLists.txt b/modules/lvgl/CMakeLists.txt index 9fe3862c411c..d0acaf628de0 100644 --- a/modules/lvgl/CMakeLists.txt +++ b/modules/lvgl/CMakeLists.txt @@ -6,82 +6,82 @@ if(CONFIG_LVGL) -set(ZEPHYR_CURRENT_LIBRARY lvgl) -set(LVGL_DIR ${ZEPHYR_LVGL_MODULE_DIR}) + set(ZEPHYR_CURRENT_LIBRARY lvgl) + set(LVGL_DIR ${ZEPHYR_LVGL_MODULE_DIR}) -zephyr_interface_library_named(LVGL) -zephyr_library() + zephyr_interface_library_named(LVGL) + zephyr_library() -zephyr_include_directories(${LVGL_DIR}/src/) -zephyr_include_directories(include) + zephyr_include_directories(${LVGL_DIR}/src/) + zephyr_include_directories(include) add_subdirectory_ifdef(CONFIG_LV_USE_NEMA_GFX nemagfx) -zephyr_compile_definitions(LV_CONF_INCLUDE_SIMPLE=1) -zephyr_library_compile_definitions(_POSIX_C_SOURCE=200809L) -zephyr_compile_definitions(LV_CONF_PATH="${CMAKE_CURRENT_SOURCE_DIR}/include/lv_conf.h") + zephyr_compile_definitions(LV_CONF_INCLUDE_SIMPLE=1) + zephyr_library_compile_definitions(_POSIX_C_SOURCE=200809L) + zephyr_compile_definitions(LV_CONF_PATH="${CMAKE_CURRENT_SOURCE_DIR}/include/lv_conf.h") -file(GLOB_RECURSE LVGL_SRC_FILES CONFIGURE_DEPENDS + file(GLOB_RECURSE LVGL_SRC_FILES CONFIGURE_DEPENDS ${LVGL_DIR}/src/*.c -) + ) -set(LVGL_EXCLUDE_PATTERNS - "${LVGL_DIR}/src/osal/lv_cmsis_rtos2.c" - "${LVGL_DIR}/src/osal/lv_freertos.c" - "${LVGL_DIR}/src/osal/lv_linux.c" - "${LVGL_DIR}/src/osal/lv_mqx.c" - "${LVGL_DIR}/src/osal/lv_os_none.c" - "${LVGL_DIR}/src/osal/lv_pthread.c" - "${LVGL_DIR}/src/osal/lv_rtthread.c" - "${LVGL_DIR}/src/osal/lv_sdl2.c" - "${LVGL_DIR}/src/osal/lv_windows.c" + set(LVGL_EXCLUDE_PATTERNS + "${LVGL_DIR}/src/osal/lv_cmsis_rtos2.c" + "${LVGL_DIR}/src/osal/lv_freertos.c" + "${LVGL_DIR}/src/osal/lv_linux.c" + "${LVGL_DIR}/src/osal/lv_mqx.c" + "${LVGL_DIR}/src/osal/lv_os_none.c" + "${LVGL_DIR}/src/osal/lv_pthread.c" + "${LVGL_DIR}/src/osal/lv_rtthread.c" + "${LVGL_DIR}/src/osal/lv_sdl2.c" + "${LVGL_DIR}/src/osal/lv_windows.c" - "${LVGL_DIR}/src/drivers/*" + "${LVGL_DIR}/src/drivers/*" - # Remove libs/gltf since this leads to build warnings - "${LVGL_DIR}/src/libs/gltf/*" + # Remove libs/gltf since this leads to build warnings + "${LVGL_DIR}/src/libs/gltf/*" - "${LVGL_DIR}/src/stdlib/builtin/lv_mem_core_builtin.c" - "${LVGL_DIR}/src/stdlib/builtin/lv_sprintf_builtin.c" - "${LVGL_DIR}/src/stdlib/builtin/lv_string_builtin.c" - "${LVGL_DIR}/src/stdlib/clib/lv_mem_core_clib.c" + "${LVGL_DIR}/src/stdlib/builtin/lv_mem_core_builtin.c" + "${LVGL_DIR}/src/stdlib/builtin/lv_sprintf_builtin.c" + "${LVGL_DIR}/src/stdlib/builtin/lv_string_builtin.c" + "${LVGL_DIR}/src/stdlib/clib/lv_mem_core_clib.c" - "${LVGL_DIR}/src/stdlib/rtthread/*" - "${LVGL_DIR}/src/stdlib/micropython/*" - "${LVGL_DIR}/src/stdlib/uefi/*" + "${LVGL_DIR}/src/stdlib/rtthread/*" + "${LVGL_DIR}/src/stdlib/micropython/*" + "${LVGL_DIR}/src/stdlib/uefi/*" - "${LVGL_DIR}/src/debugging/test/*" -) + "${LVGL_DIR}/src/debugging/test/*" + ) -foreach(pattern IN LISTS LVGL_EXCLUDE_PATTERNS) + foreach(pattern IN LISTS LVGL_EXCLUDE_PATTERNS) file(GLOB_RECURSE LVGL_EXCLUDED CONFIGURE_DEPENDS ${pattern}) list(REMOVE_ITEM LVGL_SRC_FILES ${LVGL_EXCLUDED}) -endforeach() - -zephyr_library_sources(${LVGL_SRC_FILES}) - -zephyr_library_sources( - lvgl.c - lvgl_display.c - lvgl_display_mono.c - lvgl_display_8bit.c - lvgl_display_16bit.c - lvgl_display_24bit.c - lvgl_display_32bit.c - lvgl_zephyr_osal.c -) - -zephyr_library_sources_ifdef(CONFIG_LV_Z_USE_FILESYSTEM lvgl_fs.c) -zephyr_library_sources_ifdef(CONFIG_LV_Z_MEM_POOL_SYS_HEAP lvgl_mem.c) -zephyr_library_sources_ifdef(CONFIG_LV_Z_SHELL lvgl_shell.c) - -zephyr_library_sources(input/lvgl_common_input.c) -zephyr_library_sources_ifdef(CONFIG_LV_Z_POINTER_INPUT input/lvgl_pointer_input.c) -zephyr_library_sources_ifdef(CONFIG_LV_Z_BUTTON_INPUT input/lvgl_button_input.c) -zephyr_library_sources_ifdef(CONFIG_LV_Z_ENCODER_INPUT input/lvgl_encoder_input.c) -zephyr_library_sources_ifdef(CONFIG_LV_Z_KEYPAD_INPUT input/lvgl_keypad_input.c) - -zephyr_library_link_libraries(LVGL) -target_link_libraries(LVGL INTERFACE zephyr_interface) + endforeach() + + zephyr_library_sources(${LVGL_SRC_FILES}) + + zephyr_library_sources( + lvgl.c + lvgl_display.c + lvgl_display_mono.c + lvgl_display_8bit.c + lvgl_display_16bit.c + lvgl_display_24bit.c + lvgl_display_32bit.c + lvgl_zephyr_osal.c + ) + + zephyr_library_sources_ifdef(CONFIG_LV_Z_USE_FILESYSTEM lvgl_fs.c) + zephyr_library_sources_ifdef(CONFIG_LV_Z_MEM_POOL_SYS_HEAP lvgl_mem.c) + zephyr_library_sources_ifdef(CONFIG_LV_Z_SHELL lvgl_shell.c) + + zephyr_library_sources(input/lvgl_common_input.c) + zephyr_library_sources_ifdef(CONFIG_LV_Z_POINTER_INPUT input/lvgl_pointer_input.c) + zephyr_library_sources_ifdef(CONFIG_LV_Z_BUTTON_INPUT input/lvgl_button_input.c) + zephyr_library_sources_ifdef(CONFIG_LV_Z_ENCODER_INPUT input/lvgl_encoder_input.c) + zephyr_library_sources_ifdef(CONFIG_LV_Z_KEYPAD_INPUT input/lvgl_keypad_input.c) + + zephyr_library_link_libraries(LVGL) + target_link_libraries(LVGL INTERFACE zephyr_interface) endif() From b40c531fbda28baafb2056217610a8732b9e0c48 Mon Sep 17 00:00:00 2001 From: Fabian Blatz Date: Wed, 26 Aug 2026 17:03:20 +0200 Subject: [PATCH 180/600] modules: lvgl: Guard monochrome conversion buffer usage Make sure to omit the handler for monochrome displays if the conversion buffer is not configured. Signed-off-by: Fabian Blatz --- modules/lvgl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/lvgl/CMakeLists.txt b/modules/lvgl/CMakeLists.txt index d0acaf628de0..4dcacc5f2b98 100644 --- a/modules/lvgl/CMakeLists.txt +++ b/modules/lvgl/CMakeLists.txt @@ -63,13 +63,13 @@ if(CONFIG_LVGL) zephyr_library_sources( lvgl.c lvgl_display.c - lvgl_display_mono.c lvgl_display_8bit.c lvgl_display_16bit.c lvgl_display_24bit.c lvgl_display_32bit.c lvgl_zephyr_osal.c ) + zephyr_library_sources_ifdef(CONFIG_LV_Z_MONOCHROME_CONVERSION_BUFFER lvgl_display_mono.c) zephyr_library_sources_ifdef(CONFIG_LV_Z_USE_FILESYSTEM lvgl_fs.c) zephyr_library_sources_ifdef(CONFIG_LV_Z_MEM_POOL_SYS_HEAP lvgl_mem.c) From 1e1855620db4350bc8c686094e591a9d2bcb348b Mon Sep 17 00:00:00 2001 From: Zee Yudenko Date: Mon, 17 Aug 2026 16:55:26 +0200 Subject: [PATCH 181/600] drivers: can: mcan: Zero out frame struct when constructing new CAN frame Currently, the CAN frame is only zeroed at the start of the function, and if multiple frames are read in a single pass, any flags that were ORed into the flags field will be carried into later frames, even if the later frame should not actually have those flags set. Instead, leave the frame variable uninitialised and zero it out on each iteration loop. Signed-off-by: Zee Yudenko --- drivers/can/can_mcan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/can/can_mcan.c b/drivers/can/can_mcan.c index 2744e0c27e8f..0c3ca8d09417 100644 --- a/drivers/can/can_mcan.c +++ b/drivers/can/can_mcan.c @@ -705,7 +705,7 @@ static void can_mcan_get_message(const struct device *dev, uint16_t fifo_offset, const struct can_mcan_config *config = dev->config; const struct can_mcan_callbacks *cbs = config->callbacks; struct can_mcan_rx_fifo_hdr hdr; - struct can_frame frame = {0}; + struct can_frame frame; can_rx_callback_t cb; void *user_data; uint32_t get_idx; @@ -731,6 +731,8 @@ static void can_mcan_get_message(const struct device *dev, uint16_t fifo_offset, return; } + memset(&frame, 0, sizeof(frame)); + frame.dlc = hdr.dlc; if (hdr.rtr != 0) { From 74877287d0d5c3037a36d259d1f882e62ff86a18 Mon Sep 17 00:00:00 2001 From: Toon Stegen Date: Sat, 15 Aug 2026 11:40:24 +0200 Subject: [PATCH 182/600] soc: rpi_pico: fix binary info header placed before vector table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The binary info header is injected into rom_start with sort key '0x0binary_info_header', which sorts before '0x0vectors' (the ARM vector table). This places the header at XIP_BASE + 0x100, right where the RP2040 boot2 stage hardcodes VTOR. The vector table, pushed to 0x10000200 by ALIGN(0x100) after the header's 24 bytes, is never reached — the core loads the header marker as its stack pointer and faults immediately. Fix by using sort key '0x2binary_info_header' so the header emits after the vector table snippets ('0x0vectors' / '0x1vectors'). The vector table sits at 0x10000100 as boot2 expects, and the header ends at ~offset 0xbc — within the 256-byte limit picotool scans. Only affects RP2040 builds with CONFIG_RPI_PICO_BINARY_INFO=y. Signed-off-by: Toon Stegen --- soc/raspberrypi/rpi_pico/common/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soc/raspberrypi/rpi_pico/common/CMakeLists.txt b/soc/raspberrypi/rpi_pico/common/CMakeLists.txt index 391f2e0c4c28..23c427877bb4 100644 --- a/soc/raspberrypi/rpi_pico/common/CMakeLists.txt +++ b/soc/raspberrypi/rpi_pico/common/CMakeLists.txt @@ -15,7 +15,7 @@ zephyr_library_sources_ifdef(CONFIG_RPI_PICO_BINARY_INFO ) zephyr_linker_sources_ifdef(CONFIG_RPI_PICO_BINARY_INFO - ROM_START SORT_KEY 0x0binary_info_header binary_info_header.ld + ROM_START SORT_KEY 0x2binary_info_header binary_info_header.ld ) zephyr_linker_sources_ifdef(CONFIG_RPI_PICO_BINARY_INFO From 6c1486a5c7b98fe86c0c4692bbf18e7802d4c015 Mon Sep 17 00:00:00 2001 From: Emilio Bottoni Date: Wed, 26 Aug 2026 11:12:16 -0300 Subject: [PATCH 183/600] drivers: flash: mcux flexspi nor: add IS25LP512M reported JEDEC ID RDJDID (9Fh) on this part returns 9d 60 1a, so the existing 0x20609d case never matched and the driver fell through to the SFDP path. Add the ID the part reports, keeping 0x20609d alongside it. Signed-off-by: Emilio Bottoni --- drivers/flash/flash_mcux_flexspi_nor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/flash/flash_mcux_flexspi_nor.c b/drivers/flash/flash_mcux_flexspi_nor.c index 3d48687c46ad..fda9704a1984 100644 --- a/drivers/flash/flash_mcux_flexspi_nor.c +++ b/drivers/flash/flash_mcux_flexspi_nor.c @@ -1458,7 +1458,8 @@ static int flash_flexspi_nor_check_jedec(struct flash_flexspi_nor_data *data, /* Device uses bit 1 of status reg 2 for QE */ return flash_flexspi_nor_quad_enable(data, flexspi_lut, JESD216_DW15_QER_VAL_S2B1v5); - case 0x20609d: /* IS25LP512M */ + case 0x1a609d: /* IS25LP512M */ + case 0x20609d: /* * Keep the runtime LUT in 4-byte Quad I/O read mode while XIP * is active. From 24cc932861fc1b47c27a69fb78355095690e1ace Mon Sep 17 00:00:00 2001 From: Dylan Rowe Date: Wed, 26 Aug 2026 14:18:06 +0000 Subject: [PATCH 184/600] drivers: spi: esp32: fix quad line mode truncated to 16-bit spi_esp32_get_line_mode() takes the SPI operation as a uint16_t, but the line-mode bits SPI_LINES_DUAL/QUAD/OCTAL live at bit 16 and above of the spi_operation_t type, which is widened to uint32_t when CONFIG_SPI_EXTENDED_MODES is enabled. Passing the full operation into the uint16_t parameter truncates those bits, so operation & SPI_LINES_MASK is always 0 and the function always reports single-line mode. Any request for dual, quad or octal lines is silently downgraded to single-line, driving only the D0 data line. Widen the parameter to uint32_t so the line-mode bits survive, matching how every other SPI driver reads the operation. Verified on an ESP32-S3-Touch-AMOLED-1.8 QSPI panel, where quad transfers only produced correct colors with this fix. Signed-off-by: Dylan Rowe --- drivers/spi/spi_esp32_spim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi_esp32_spim.c b/drivers/spi/spi_esp32_spim.c index 0dcdb28a5302..2f32326f96f9 100644 --- a/drivers/spi/spi_esp32_spim.c +++ b/drivers/spi/spi_esp32_spim.c @@ -732,7 +732,7 @@ static int spi_esp32_init(const struct device *dev) return 0; } -static inline uint8_t spi_esp32_get_line_mode(uint16_t operation) +static inline uint8_t spi_esp32_get_line_mode(spi_operation_t operation) { if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES)) { switch (operation & SPI_LINES_MASK) { From f80761e49401497b5ee64ee9732dc2791055ec52 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 28 Aug 2026 13:25:23 +0200 Subject: [PATCH 185/600] net: shell: fix -Wuninitialized-const-pointer warnings Fix -Wuninitialized-const-pointer warnings in shell route and tcp commands, that are generated, if a unitialized variable of type struct net_sockaddr_storage is passed to net_sad() function. Signed-off-by: Tim Pambor --- subsys/net/lib/shell/route.c | 4 ++-- subsys/net/lib/shell/tcp.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/subsys/net/lib/shell/route.c b/subsys/net/lib/shell/route.c index 285b902ebf13..257e7928612f 100644 --- a/subsys/net/lib/shell/route.c +++ b/subsys/net/lib/shell/route.c @@ -355,7 +355,7 @@ static int cmd_net_route_add(const struct shell *sh, size_t argc, char *argv[]) struct net_if *iface = NULL; int idx; struct net_route_entry *route; - struct net_sockaddr_storage addr; + struct net_sockaddr_storage addr = { 0 }; const char *str; uint8_t mask_len; @@ -590,7 +590,7 @@ static int cmd_net_route_del(const struct shell *sh, size_t argc, char *argv[]) struct net_if *iface = NULL; int idx; struct net_route_entry *route = NULL; - struct net_sockaddr_storage addr; + struct net_sockaddr_storage addr = { 0 }; const char *str; uint8_t mask_len; diff --git a/subsys/net/lib/shell/tcp.c b/subsys/net/lib/shell/tcp.c index 8d82a1d0b118..32a0415a4079 100644 --- a/subsys/net/lib/shell/tcp.c +++ b/subsys/net/lib/shell/tcp.c @@ -102,8 +102,8 @@ static void tcp_connect(const struct shell *sh, char *host, uint16_t port, struct net_context **ctx) { struct net_if *iface = net_if_get_default(); - struct net_sockaddr_storage myaddr; - struct net_sockaddr_storage addr; + struct net_sockaddr_storage myaddr = { 0 }; + struct net_sockaddr_storage addr = { 0 }; struct net_sockaddr *my_sa = net_sad(&myaddr); struct net_sockaddr *sa = net_sad(&addr); struct net_nbr *nbr; From d438144f0612b8b1b8919cee45c44a460f51f2a8 Mon Sep 17 00:00:00 2001 From: Vinit Mehta Date: Tue, 14 Apr 2026 16:34:31 +0530 Subject: [PATCH 186/600] boards: shields: add nxp_arduino_aw510_wifi_bt shield -Add shield overlay to support BT over arduino interface -Add board overlay for frdm_mcxn947 -Add default kconfigs for BT modules and SoC as part of build param. -Add shield document Signed-off-by: Vinit Mehta --- .../nxp_arduino_wifi_bt/Kconfig.defconfig | 28 ++++++++++ .../nxp_arduino_wifi_bt/Kconfig.shield | 5 ++ .../boards/frdm_mcxn947_mcxn947_cpu0.overlay | 28 ++++++++++ .../shields/nxp_arduino_wifi_bt/doc/index.rst | 56 +++++++++++++++++++ .../nxp_arduino_aw510_wifi_bt.overlay | 30 ++++++++++ boards/shields/nxp_arduino_wifi_bt/shield.yml | 7 +++ 6 files changed, 154 insertions(+) create mode 100644 boards/shields/nxp_arduino_wifi_bt/Kconfig.defconfig create mode 100644 boards/shields/nxp_arduino_wifi_bt/Kconfig.shield create mode 100644 boards/shields/nxp_arduino_wifi_bt/boards/frdm_mcxn947_mcxn947_cpu0.overlay create mode 100644 boards/shields/nxp_arduino_wifi_bt/doc/index.rst create mode 100644 boards/shields/nxp_arduino_wifi_bt/nxp_arduino_aw510_wifi_bt.overlay create mode 100644 boards/shields/nxp_arduino_wifi_bt/shield.yml diff --git a/boards/shields/nxp_arduino_wifi_bt/Kconfig.defconfig b/boards/shields/nxp_arduino_wifi_bt/Kconfig.defconfig new file mode 100644 index 000000000000..f06e8a05d4b2 --- /dev/null +++ b/boards/shields/nxp_arduino_wifi_bt/Kconfig.defconfig @@ -0,0 +1,28 @@ +# Copyright 2026 NXP +# SPDX-License-Identifier: Apache-2.0 + +# Set default NXP BT module when shield is used with BT +if SHIELD_NXP_ARDUINO_AW510_WIFI_BT && BT + +choice BT_NXP_MODULE + default BT_NXP_IW416 +endchoice + +endif # SHIELD_NXP_ARDUINO_AW510_WIFI_BT && BT + +# Configure stack sizes for IW416 module when BT is enabled +if BT && (BT_NXP_IW416 || NXP_IW416) + +config SYSTEM_WORKQUEUE_STACK_SIZE + default 2048 + +config BT_LONG_WQ_STACK_SIZE + default 2560 + +config MAIN_STACK_SIZE + default 2560 + +config SHELL_STACK_SIZE + default 4096 if SHELL + +endif # BT && (BT_NXP_IW416 || NXP_IW416) diff --git a/boards/shields/nxp_arduino_wifi_bt/Kconfig.shield b/boards/shields/nxp_arduino_wifi_bt/Kconfig.shield new file mode 100644 index 000000000000..b5a108f3b866 --- /dev/null +++ b/boards/shields/nxp_arduino_wifi_bt/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright 2026 NXP +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_NXP_ARDUINO_AW510_WIFI_BT + def_bool $(shields_list_contains,nxp_arduino_aw510_wifi_bt) diff --git a/boards/shields/nxp_arduino_wifi_bt/boards/frdm_mcxn947_mcxn947_cpu0.overlay b/boards/shields/nxp_arduino_wifi_bt/boards/frdm_mcxn947_mcxn947_cpu0.overlay new file mode 100644 index 000000000000..27e86a8366ed --- /dev/null +++ b/boards/shields/nxp_arduino_wifi_bt/boards/frdm_mcxn947_mcxn947_cpu0.overlay @@ -0,0 +1,28 @@ +/* + * Copyright 2026 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinmux_flexcomm2_lpuart { + group0 { + /delete-property/ bias-pull-up; + /delete-property/ pinmux; + pinmux = , , + , ; + }; +}; + +&m2_hci_bt_uart { + pinctrl-0 = <&pinmux_flexcomm2_lpuart>; + + bt_hci_uart: bt_hci_uart { + compatible = "zephyr,bt-hci-uart"; + + m2_bt_module: m2_bt_module { + compatible = "nxp,bt-hci-uart"; + sdio-reset-gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + w-disable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + }; + }; +}; diff --git a/boards/shields/nxp_arduino_wifi_bt/doc/index.rst b/boards/shields/nxp_arduino_wifi_bt/doc/index.rst new file mode 100644 index 000000000000..14ccab5bf3b2 --- /dev/null +++ b/boards/shields/nxp_arduino_wifi_bt/doc/index.rst @@ -0,0 +1,56 @@ +.. _nxp_arduino_wifi_bt: + +NXP Arduino WiFi and BT Shield +############################## + +Overview +******** + +The NXP Arduino WiFi/BT shield provides WiFi and Bluetooth connectivity +using NXP wireless SOC modules in Arduino form factor for MCXN based platforms. + +Supported Shields +***************** + +This shield supports the following configurations: + +- ``nxp_arduino_aw510_wifi_bt``: Arduino WiFi/BT Shield with AW510 module (IW416) + +Requirements +************ + +- Azurewave AW510 module +- UART interface for Bluetooth HCI +- SDIO interface for WiFi + +Connections and IOs +=================== + +The shield uses the following interfaces: + +- UART for Bluetooth HCI communication +- SDIO for WiFi communication +- GPIO for control signals (reset, power, wakeup) + +Programming +*********** + +Set ``--shield