diff --git a/drivers/entropy/CMakeLists.txt b/drivers/entropy/CMakeLists.txt index 541be8478b169..f9226c71dfe8d 100644 --- a/drivers/entropy/CMakeLists.txt +++ b/drivers/entropy/CMakeLists.txt @@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_CAAM entropy_mcux_caa zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNG entropy_mcux_rng.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNGA entropy_mcux_rnga.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_TRNG entropy_mcux_trng.c) +zephyr_library_sources_ifdef(CONFIG_ENTROPY_MSPM0_TRNG entropy_mspm0_trng.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_NEORV32_TRNG entropy_neorv32_trng.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_NPCX_DRBG entropy_npcx_drbg.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_NRF5_RNG entropy_nrf5.c) diff --git a/drivers/entropy/Kconfig b/drivers/entropy/Kconfig index 59b93ccf022b2..021c47dc29198 100644 --- a/drivers/entropy/Kconfig +++ b/drivers/entropy/Kconfig @@ -32,6 +32,7 @@ source "drivers/entropy/Kconfig.litex" source "drivers/entropy/Kconfig.max32" source "drivers/entropy/Kconfig.maxq10xx" source "drivers/entropy/Kconfig.mcux" +source "drivers/entropy/Kconfig.mspm0_trng" source "drivers/entropy/Kconfig.native_sim" source "drivers/entropy/Kconfig.neorv32" source "drivers/entropy/Kconfig.npcx" diff --git a/drivers/entropy/Kconfig.mspm0_trng b/drivers/entropy/Kconfig.mspm0_trng new file mode 100644 index 0000000000000..0b6349632e448 --- /dev/null +++ b/drivers/entropy/Kconfig.mspm0_trng @@ -0,0 +1,32 @@ +# Copyright (c) 2025, Linumiz GmbH +# SPDX-License-Identifier: Apache-2.0 + +config ENTROPY_MSPM0_TRNG + bool "TI MSPM0 True Random Number Generator (TRNG)" + default y + depends on DT_HAS_TI_MSPM0_TRNG_ENABLED + depends on SOC_FAMILY_TI_MSPM0 + select ENTROPY_HAS_DRIVER + select RING_BUFFER + help + This option enables the driver for the True Random Number Generator (TRNG) + for TI MSPM0 SoCs. + +if ENTROPY_MSPM0_TRNG + +config ENTROPY_MSPM0_TRNG_POOL_SIZE + int "Entropy pool buffer size in bytes" + default 256 + help + The size in bytes of the ring buffer used to store entropy generated by the + TRNG hardware. + +config ENTROPY_MSPM0_TRNG_DECIMATION_RATE + int "TRNG decimation rate (0-7)" + default 4 + range 0 7 + help + The decimation rate controls the sampling rate of the TRNG. Higher values + provide better entropy quality. A decimation rate of 4 or greater is recommended per TRM. + Valid values are 0-7. +endif # ENTROPY_MSPM0_TRNG diff --git a/drivers/entropy/entropy_mspm0_trng.c b/drivers/entropy/entropy_mspm0_trng.c new file mode 100644 index 0000000000000..b06b3604dc660 --- /dev/null +++ b/drivers/entropy/entropy_mspm0_trng.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2025 Linumiz GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_mspm0_trng + +#include +#include +#include +#include +#include +#include + +/* TI Driverlib includes */ +#include +#include + +#define TRNG_DECIMATION_RATE CONFIG_ENTROPY_MSPM0_TRNG_DECIMATION_RATE +#define TRNG_SAMPLE_SIZE 4 + +#define TRNG_CLOCK_DIVIDE_RATIO CONCAT(DL_TRNG_CLOCK_DIVIDE_, DT_INST_PROP(0, ti_clk_div)) + +#define TRNG_SAMPLE_GENERATE_TIME (1000000 * (32 * (TRNG_DECIMATION_RATE + 1)) \ + / (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / (TRNG_CLOCK_DIVIDE_RATIO))) + +struct entropy_mspm0_trng_config { + TRNG_Regs *base; +}; + +struct entropy_mspm0_trng_data { + struct k_mutex mutex_lock; + struct k_sem sem_sync; + struct ring_buf entropy_pool; + uint8_t pool_buffer[CONFIG_ENTROPY_MSPM0_TRNG_POOL_SIZE]; +}; + +static inline bool entropy_mspm0_trng_run_dig_test(TRNG_Regs *base) +{ + uint8_t dig_test = DL_TRNG_getDigitalHealthTestResults(base); + + if (dig_test == DL_TRNG_DIGITAL_HEALTH_TEST_SUCCESS) { + return true; + } + + DL_TRNG_sendCommand(base, DL_TRNG_CMD_TEST_DIG); + /* Test needs to run, return false to indicate ISR should return */ + return false; +} + +static inline bool entropy_mspm0_trng_run_ana_test(TRNG_Regs *base) +{ + uint8_t ana_test = DL_TRNG_getAnalogHealthTestResults(base); + + if (ana_test == DL_TRNG_ANALOG_HEALTH_TEST_SUCCESS) { + return true; + } + + DL_TRNG_sendCommand(base, DL_TRNG_CMD_TEST_ANA); + /* Test needs to run, return false to indicate ISR should return */ + return false; +} + +static void entropy_mspm0_trng_isr(const struct device *dev) +{ + const struct entropy_mspm0_trng_config *config = dev->config; + struct entropy_mspm0_trng_data *data = dev->data; + uint32_t status; + uint32_t entropy_data; + uint32_t bytes_written; + bool dig_test; + bool ana_test; + + status = DL_TRNG_getEnabledInterruptStatus(config->base, + DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT | + DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT | + DL_TRNG_INTERRUPT_CMD_DONE_EVENT); + + if (status & DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT) { + DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT); + DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_PWROFF); + return; + } + + if (status & DL_TRNG_INTERRUPT_CMD_DONE_EVENT) { + DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT); + + /* Run DIG test */ + dig_test = entropy_mspm0_trng_run_dig_test(config->base); + if (!dig_test) { + return; + } + + /* Run ANALOG test */ + ana_test = entropy_mspm0_trng_run_ana_test(config->base); + if (!ana_test) { + return; + } + + /* + * If both tests are successful, discard first sample from DATA_CAPTURE register + * and set DECIM RATE, enable IRQ_CAPTURE_RDY + */ + if (dig_test && ana_test) { + DL_TRNG_getCapture(config->base); + DL_TRNG_clearInterruptStatus(config->base, + DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + DL_TRNG_setDecimationRate(config->base, + (DL_TRNG_DECIMATION_RATE)TRNG_DECIMATION_RATE); + DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT); + DL_TRNG_enableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + return; + } + } + + if (status & DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT) { + entropy_data = DL_TRNG_getCapture(config->base); + bytes_written = ring_buf_put(&data->entropy_pool, (uint8_t *)&entropy_data, + TRNG_SAMPLE_SIZE); + + /* If the ring buf is exhausted, disable the interrupt in IMASK */ + if (bytes_written < TRNG_SAMPLE_SIZE) { + DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + } + + DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + k_sem_give(&data->sem_sync); + } +} + +static int entropy_mspm0_trng_get_entropy(const struct device *dev, + uint8_t *buffer, uint16_t length) +{ + const struct entropy_mspm0_trng_config *config = dev->config; + struct entropy_mspm0_trng_data *data = dev->data; + uint16_t bytes_read; + + k_mutex_lock(&data->mutex_lock, K_FOREVER); + + while (length) { + bytes_read = ring_buf_get(&data->entropy_pool, buffer, length); + + /* + * If no bytes read, i.e ring buf is exhausted, enable the interrupt and + * wait until the additional entropy is available in ring buf. + */ + if (bytes_read == 0U) { + DL_TRNG_enableInterrupt(config->base, + DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + k_sem_take(&data->sem_sync, K_FOREVER); + continue; + } + buffer += bytes_read; + length -= bytes_read; + } + + k_mutex_unlock(&data->mutex_lock); + + return 0; +} + +static int entropy_mspm0_trng_get_entropy_isr(const struct device *dev, uint8_t *buffer, + uint16_t length, uint32_t flags) +{ + const struct entropy_mspm0_trng_config *config = dev->config; + struct entropy_mspm0_trng_data *data = dev->data; + uint16_t bytes_read; + uint16_t total_read; + uint32_t entropy_data; + unsigned int key; + + /* Try to get entropy from existing ring buffer */ + key = irq_lock(); + bytes_read = ring_buf_get(&data->entropy_pool, buffer, length); + total_read = bytes_read; + + if ((bytes_read == length) || ((flags & ENTROPY_BUSYWAIT) == 0U)) { + /* Either we got all requested data, or busy-waiting is not allowed */ + irq_unlock(key); + return total_read; + } + + /* Busy-wait for additional data (only if ENTROPY_BUSYWAIT is set) */ + buffer += bytes_read; + length -= bytes_read; + + while (length) { + /* Check if data is ready by checking IRQ_CAPTURED_RDY */ + if (DL_TRNG_isCaptureReady(config->base)) { + entropy_data = DL_TRNG_getCapture(config->base); + DL_TRNG_clearInterruptStatus(config->base, + DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + bytes_read = (length >= TRNG_SAMPLE_SIZE) ? TRNG_SAMPLE_SIZE : length; + + for (uint8_t i = 0; i < bytes_read; i++) { + buffer[i] = ((uint8_t *)&entropy_data)[i]; + } + + buffer += bytes_read; + length -= bytes_read; + total_read += bytes_read; + } else { + k_busy_wait(TRNG_SAMPLE_GENERATE_TIME); + } + } + + irq_unlock(key); + + return total_read; +} + +static int entropy_mspm0_trng_init(const struct device *dev) +{ + const struct entropy_mspm0_trng_config *config = dev->config; + struct entropy_mspm0_trng_data *data = dev->data; + + /* Initialize ring buffer for entropy storage */ + ring_buf_init(&data->entropy_pool, sizeof(data->pool_buffer), data->pool_buffer); + + /* Enable TRNG power */ + DL_TRNG_enablePower(config->base); + + /* Configure TRNG clock divider */ + DL_TRNG_setClockDivider(config->base, TRNG_CLOCK_DIVIDE_RATIO); + + /* Disable the CAPTURE_RDY IRQ until health tests are complete */ + DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT); + + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), + entropy_mspm0_trng_isr, DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQN(0)); + + DL_TRNG_enableInterrupt(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT | + DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT); + + /* Move TRNG from OFF to NORM FUNC state */ + DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_NORM_FUNC); + + return 0; +} + +static DEVICE_API(entropy, entropy_mspm0_trng_driver_api) = { + .get_entropy = entropy_mspm0_trng_get_entropy, + .get_entropy_isr = entropy_mspm0_trng_get_entropy_isr, +}; + +static const struct entropy_mspm0_trng_config entropy_mspm0_trng_config = { + .base = (TRNG_Regs *)DT_INST_REG_ADDR(0), +}; + +static struct entropy_mspm0_trng_data entropy_mspm0_trng_data = { + .mutex_lock = Z_MUTEX_INITIALIZER(entropy_mspm0_trng_data.mutex_lock), + .sem_sync = Z_SEM_INITIALIZER(entropy_mspm0_trng_data.sem_sync, 0, 1), +}; + +DEVICE_DT_INST_DEFINE(0, entropy_mspm0_trng_init, NULL, + &entropy_mspm0_trng_data, + &entropy_mspm0_trng_config, PRE_KERNEL_1, + CONFIG_ENTROPY_INIT_PRIORITY, + &entropy_mspm0_trng_driver_api); diff --git a/dts/arm/ti/mspm0/g/mspm0g.dtsi b/dts/arm/ti/mspm0/g/mspm0g.dtsi index d599264866148..0418643aec7ce 100644 --- a/dts/arm/ti/mspm0/g/mspm0g.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g.dtsi @@ -104,5 +104,13 @@ clk-div = <1>; status = "disabled"; }; + + trng: trng@40444000 { + compatible = "ti,mspm0-trng"; + reg = <0x40444000 0x2000>; + interrupts = <1 0>; + ti,clk-div = <1>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/rng/ti,mspm0-trng.yaml b/dts/bindings/rng/ti,mspm0-trng.yaml new file mode 100644 index 0000000000000..9f915b55e2dfd --- /dev/null +++ b/dts/bindings/rng/ti,mspm0-trng.yaml @@ -0,0 +1,30 @@ +# Copyright 2025 Linumiz GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: | + Texas Instruments MSPM0 Series True Random Number Generator (TRNG). + This peripheral provides a hardware-based source of entropy with built-in + health tests. + +compatible: "ti,mspm0-trng" + +include: base.yaml +properties: + reg: + required: true + + interrupts: + required: true + + ti,clk-div: + type: int + required: true + default: 1 + enum: + - 1 + - 2 + - 4 + - 6 + - 8 + description: | + Clock divider selction value.