Skip to content

Commit b804a5a

Browse files
SanjayyyVnashif
authored andcommitted
drivers: entropy: mspm0: Add a support for TI MSPM0 TRNG module
TI MSPM0 has a TRNG module to generate truly random bits. Add a support for TI MSPM0 TRNG module. Signed-off-by: Sanjay Vallimanalan <sanjay@linumiz.com>
1 parent b34efdd commit b804a5a

File tree

4 files changed

+295
-0
lines changed

4 files changed

+295
-0
lines changed

drivers/entropy/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_CAAM entropy_mcux_caa
2626
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNG entropy_mcux_rng.c)
2727
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNGA entropy_mcux_rnga.c)
2828
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_TRNG entropy_mcux_trng.c)
29+
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MSPM0_TRNG entropy_mspm0_trng.c)
2930
zephyr_library_sources_ifdef(CONFIG_ENTROPY_NEORV32_TRNG entropy_neorv32_trng.c)
3031
zephyr_library_sources_ifdef(CONFIG_ENTROPY_NPCX_DRBG entropy_npcx_drbg.c)
3132
zephyr_library_sources_ifdef(CONFIG_ENTROPY_NRF5_RNG entropy_nrf5.c)

drivers/entropy/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ source "drivers/entropy/Kconfig.litex"
3232
source "drivers/entropy/Kconfig.max32"
3333
source "drivers/entropy/Kconfig.maxq10xx"
3434
source "drivers/entropy/Kconfig.mcux"
35+
source "drivers/entropy/Kconfig.mspm0_trng"
3536
source "drivers/entropy/Kconfig.native_sim"
3637
source "drivers/entropy/Kconfig.neorv32"
3738
source "drivers/entropy/Kconfig.npcx"

drivers/entropy/Kconfig.mspm0_trng

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright (c) 2025, Linumiz GmbH
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config ENTROPY_MSPM0_TRNG
5+
bool "TI MSPM0 True Random Number Generator (TRNG)"
6+
default y
7+
depends on DT_HAS_TI_MSPM0_TRNG_ENABLED
8+
depends on SOC_FAMILY_TI_MSPM0
9+
select ENTROPY_HAS_DRIVER
10+
select RING_BUFFER
11+
help
12+
This option enables the driver for the True Random Number Generator (TRNG)
13+
for TI MSPM0 SoCs.
14+
15+
if ENTROPY_MSPM0_TRNG
16+
17+
config ENTROPY_MSPM0_TRNG_POOL_SIZE
18+
int "Entropy pool buffer size in bytes"
19+
default 256
20+
help
21+
The size in bytes of the ring buffer used to store entropy generated by the
22+
TRNG hardware.
23+
24+
config ENTROPY_MSPM0_TRNG_DECIMATION_RATE
25+
int "TRNG decimation rate (0-7)"
26+
default 4
27+
range 0 7
28+
help
29+
The decimation rate controls the sampling rate of the TRNG. Higher values
30+
provide better entropy quality. A decimation rate of 4 or greater is recommended per TRM.
31+
Valid values are 0-7.
32+
endif # ENTROPY_MSPM0_TRNG
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/*
2+
* Copyright (c) 2025 Linumiz GmbH
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_mspm0_trng
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/entropy.h>
12+
#include <zephyr/irq.h>
13+
#include <zephyr/logging/log.h>
14+
#include <zephyr/sys/ring_buffer.h>
15+
16+
/* TI Driverlib includes */
17+
#include <ti/driverlib/dl_trng.h>
18+
#include <ti/devices/msp/peripherals/hw_trng.h>
19+
20+
#define TRNG_DECIMATION_RATE CONFIG_ENTROPY_MSPM0_TRNG_DECIMATION_RATE
21+
#define TRNG_SAMPLE_SIZE 4
22+
23+
#define TRNG_CLOCK_DIVIDE_RATIO CONCAT(DL_TRNG_CLOCK_DIVIDE_, DT_INST_PROP(0, ti_clk_div))
24+
25+
#define TRNG_SAMPLE_GENERATE_TIME (1000000 * (32 * (TRNG_DECIMATION_RATE + 1)) \
26+
/ (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / (TRNG_CLOCK_DIVIDE_RATIO)))
27+
28+
struct entropy_mspm0_trng_config {
29+
TRNG_Regs *base;
30+
};
31+
32+
struct entropy_mspm0_trng_data {
33+
struct k_mutex mutex_lock;
34+
struct k_sem sem_sync;
35+
struct ring_buf entropy_pool;
36+
uint8_t pool_buffer[CONFIG_ENTROPY_MSPM0_TRNG_POOL_SIZE];
37+
};
38+
39+
static inline bool entropy_mspm0_trng_run_dig_test(TRNG_Regs *base)
40+
{
41+
uint8_t dig_test = DL_TRNG_getDigitalHealthTestResults(base);
42+
43+
if (dig_test == DL_TRNG_DIGITAL_HEALTH_TEST_SUCCESS) {
44+
return true;
45+
}
46+
47+
DL_TRNG_sendCommand(base, DL_TRNG_CMD_TEST_DIG);
48+
/* Test needs to run, return false to indicate ISR should return */
49+
return false;
50+
}
51+
52+
static inline bool entropy_mspm0_trng_run_ana_test(TRNG_Regs *base)
53+
{
54+
uint8_t ana_test = DL_TRNG_getAnalogHealthTestResults(base);
55+
56+
if (ana_test == DL_TRNG_ANALOG_HEALTH_TEST_SUCCESS) {
57+
return true;
58+
}
59+
60+
DL_TRNG_sendCommand(base, DL_TRNG_CMD_TEST_ANA);
61+
/* Test needs to run, return false to indicate ISR should return */
62+
return false;
63+
}
64+
65+
static void entropy_mspm0_trng_isr(const struct device *dev)
66+
{
67+
const struct entropy_mspm0_trng_config *config = dev->config;
68+
struct entropy_mspm0_trng_data *data = dev->data;
69+
uint32_t status;
70+
uint32_t entropy_data;
71+
uint32_t bytes_written;
72+
bool dig_test;
73+
bool ana_test;
74+
75+
status = DL_TRNG_getEnabledInterruptStatus(config->base,
76+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT |
77+
DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT |
78+
DL_TRNG_INTERRUPT_CMD_DONE_EVENT);
79+
80+
if (status & DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT) {
81+
DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT);
82+
DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_PWROFF);
83+
return;
84+
}
85+
86+
if (status & DL_TRNG_INTERRUPT_CMD_DONE_EVENT) {
87+
DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT);
88+
89+
/* Run DIG test */
90+
dig_test = entropy_mspm0_trng_run_dig_test(config->base);
91+
if (!dig_test) {
92+
return;
93+
}
94+
95+
/* Run ANALOG test */
96+
ana_test = entropy_mspm0_trng_run_ana_test(config->base);
97+
if (!ana_test) {
98+
return;
99+
}
100+
101+
/*
102+
* If both tests are successful, discard first sample from DATA_CAPTURE register
103+
* and set DECIM RATE, enable IRQ_CAPTURE_RDY
104+
*/
105+
if (dig_test && ana_test) {
106+
DL_TRNG_getCapture(config->base);
107+
DL_TRNG_clearInterruptStatus(config->base,
108+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
109+
DL_TRNG_setDecimationRate(config->base,
110+
(DL_TRNG_DECIMATION_RATE)TRNG_DECIMATION_RATE);
111+
DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT);
112+
DL_TRNG_enableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
113+
return;
114+
}
115+
}
116+
117+
if (status & DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT) {
118+
entropy_data = DL_TRNG_getCapture(config->base);
119+
bytes_written = ring_buf_put(&data->entropy_pool, (uint8_t *)&entropy_data,
120+
TRNG_SAMPLE_SIZE);
121+
122+
/* If the ring buf is exhausted, disable the interrupt in IMASK */
123+
if (bytes_written < TRNG_SAMPLE_SIZE) {
124+
DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
125+
}
126+
127+
DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
128+
k_sem_give(&data->sem_sync);
129+
}
130+
}
131+
132+
static int entropy_mspm0_trng_get_entropy(const struct device *dev,
133+
uint8_t *buffer, uint16_t length)
134+
{
135+
const struct entropy_mspm0_trng_config *config = dev->config;
136+
struct entropy_mspm0_trng_data *data = dev->data;
137+
uint16_t bytes_read;
138+
139+
k_mutex_lock(&data->mutex_lock, K_FOREVER);
140+
141+
while (length) {
142+
bytes_read = ring_buf_get(&data->entropy_pool, buffer, length);
143+
144+
/*
145+
* If no bytes read, i.e ring buf is exhausted, enable the interrupt and
146+
* wait until the additional entropy is available in ring buf.
147+
*/
148+
if (bytes_read == 0U) {
149+
DL_TRNG_enableInterrupt(config->base,
150+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
151+
k_sem_take(&data->sem_sync, K_FOREVER);
152+
continue;
153+
}
154+
buffer += bytes_read;
155+
length -= bytes_read;
156+
}
157+
158+
k_mutex_unlock(&data->mutex_lock);
159+
160+
return 0;
161+
}
162+
163+
static int entropy_mspm0_trng_get_entropy_isr(const struct device *dev, uint8_t *buffer,
164+
uint16_t length, uint32_t flags)
165+
{
166+
const struct entropy_mspm0_trng_config *config = dev->config;
167+
struct entropy_mspm0_trng_data *data = dev->data;
168+
uint16_t bytes_read;
169+
uint16_t total_read;
170+
uint32_t entropy_data;
171+
unsigned int key;
172+
173+
/* Try to get entropy from existing ring buffer */
174+
key = irq_lock();
175+
bytes_read = ring_buf_get(&data->entropy_pool, buffer, length);
176+
total_read = bytes_read;
177+
178+
if ((bytes_read == length) || ((flags & ENTROPY_BUSYWAIT) == 0U)) {
179+
/* Either we got all requested data, or busy-waiting is not allowed */
180+
irq_unlock(key);
181+
return total_read;
182+
}
183+
184+
/* Busy-wait for additional data (only if ENTROPY_BUSYWAIT is set) */
185+
buffer += bytes_read;
186+
length -= bytes_read;
187+
188+
while (length) {
189+
/* Check if data is ready by checking IRQ_CAPTURED_RDY */
190+
if (DL_TRNG_isCaptureReady(config->base)) {
191+
entropy_data = DL_TRNG_getCapture(config->base);
192+
DL_TRNG_clearInterruptStatus(config->base,
193+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
194+
bytes_read = (length >= TRNG_SAMPLE_SIZE) ? TRNG_SAMPLE_SIZE : length;
195+
196+
for (uint8_t i = 0; i < bytes_read; i++) {
197+
buffer[i] = ((uint8_t *)&entropy_data)[i];
198+
}
199+
200+
buffer += bytes_read;
201+
length -= bytes_read;
202+
total_read += bytes_read;
203+
} else {
204+
k_busy_wait(TRNG_SAMPLE_GENERATE_TIME);
205+
}
206+
}
207+
208+
irq_unlock(key);
209+
210+
return total_read;
211+
}
212+
213+
static int entropy_mspm0_trng_init(const struct device *dev)
214+
{
215+
const struct entropy_mspm0_trng_config *config = dev->config;
216+
struct entropy_mspm0_trng_data *data = dev->data;
217+
218+
/* Initialize ring buffer for entropy storage */
219+
ring_buf_init(&data->entropy_pool, sizeof(data->pool_buffer), data->pool_buffer);
220+
221+
/* Enable TRNG power */
222+
DL_TRNG_enablePower(config->base);
223+
224+
/* Configure TRNG clock divider */
225+
DL_TRNG_setClockDivider(config->base, TRNG_CLOCK_DIVIDE_RATIO);
226+
227+
/* Disable the CAPTURE_RDY IRQ until health tests are complete */
228+
DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
229+
230+
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
231+
entropy_mspm0_trng_isr, DEVICE_DT_INST_GET(0), 0);
232+
irq_enable(DT_INST_IRQN(0));
233+
234+
DL_TRNG_enableInterrupt(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT |
235+
DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT);
236+
237+
/* Move TRNG from OFF to NORM FUNC state */
238+
DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_NORM_FUNC);
239+
240+
return 0;
241+
}
242+
243+
static DEVICE_API(entropy, entropy_mspm0_trng_driver_api) = {
244+
.get_entropy = entropy_mspm0_trng_get_entropy,
245+
.get_entropy_isr = entropy_mspm0_trng_get_entropy_isr,
246+
};
247+
248+
static const struct entropy_mspm0_trng_config entropy_mspm0_trng_config = {
249+
.base = (TRNG_Regs *)DT_INST_REG_ADDR(0),
250+
};
251+
252+
static struct entropy_mspm0_trng_data entropy_mspm0_trng_data = {
253+
.mutex_lock = Z_MUTEX_INITIALIZER(entropy_mspm0_trng_data.mutex_lock),
254+
.sem_sync = Z_SEM_INITIALIZER(entropy_mspm0_trng_data.sem_sync, 0, 1),
255+
};
256+
257+
DEVICE_DT_INST_DEFINE(0, entropy_mspm0_trng_init, NULL,
258+
&entropy_mspm0_trng_data,
259+
&entropy_mspm0_trng_config, PRE_KERNEL_1,
260+
CONFIG_ENTROPY_INIT_PRIORITY,
261+
&entropy_mspm0_trng_driver_api);

0 commit comments

Comments
 (0)