Skip to content

Commit 41e834f

Browse files
committed
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 <[email protected]>
1 parent bccf380 commit 41e834f

File tree

4 files changed

+270
-0
lines changed

4 files changed

+270
-0
lines changed

drivers/entropy/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_CAAM entropy_mcux_caa
2525
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNG entropy_mcux_rng.c)
2626
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNGA entropy_mcux_rnga.c)
2727
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_TRNG entropy_mcux_trng.c)
28+
zephyr_library_sources_ifdef(CONFIG_ENTROPY_MSPM0_TRNG entropy_mspm0_trng.c)
2829
zephyr_library_sources_ifdef(CONFIG_ENTROPY_NEORV32_TRNG entropy_neorv32_trng.c)
2930
zephyr_library_sources_ifdef(CONFIG_ENTROPY_NPCX_DRBG entropy_npcx_drbg.c)
3031
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
@@ -31,6 +31,7 @@ source "drivers/entropy/Kconfig.litex"
3131
source "drivers/entropy/Kconfig.max32"
3232
source "drivers/entropy/Kconfig.maxq10xx"
3333
source "drivers/entropy/Kconfig.mcux"
34+
source "drivers/entropy/Kconfig.mspm0_trng"
3435
source "drivers/entropy/Kconfig.native_sim"
3536
source "drivers/entropy/Kconfig.neorv32"
3637
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: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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_BUFFER_SIZE CONFIG_ENTROPY_MSPM0_TRNG_POOL_SIZE
21+
#define TRNG_DECIMATION_RATE CONFIG_ENTROPY_MSPM0_TRNG_DECIMATION_RATE
22+
#define TRNG_SAMPLE_SIZE 4
23+
24+
#define TRNG_CLOCK_DIVIDE_RATIO CONCAT(DL_TRNG_CLOCK_DIVIDE_, DT_INST_PROP(0, ti_clk_div))
25+
26+
#define TRNG_SAMPLE_GENERATE_TIME (1000000 * (32 * (TRNG_DECIMATION_RATE + 1)) \
27+
/ (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / (TRNG_CLOCK_DIVIDE_RATIO)))
28+
29+
struct entropy_mspm0_trng_config {
30+
TRNG_Regs *base;
31+
};
32+
33+
struct entropy_mspm0_trng_data {
34+
struct k_sem sem_lock;
35+
struct k_sem sem_sync;
36+
struct ring_buf entropy_pool;
37+
uint8_t pool_buffer[TRNG_BUFFER_SIZE];
38+
};
39+
40+
static void entropy_mspm0_trng_isr(const struct device *dev)
41+
{
42+
const struct entropy_mspm0_trng_config *config = dev->config;
43+
struct entropy_mspm0_trng_data *data = dev->data;
44+
uint32_t status;
45+
uint32_t entropy_data;
46+
uint8_t dig_test;
47+
uint8_t ana_test;
48+
uint32_t bytes_written;
49+
50+
status = DL_TRNG_getEnabledInterruptStatus(config->base,
51+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT |
52+
DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT |
53+
DL_TRNG_INTERRUPT_CMD_DONE_EVENT);
54+
55+
if ((status & DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT)) {
56+
DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT);
57+
DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_PWROFF);
58+
return;
59+
}
60+
61+
if (status & DL_TRNG_INTERRUPT_CMD_DONE_EVENT) {
62+
DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT);
63+
64+
/* Run DIG test */
65+
dig_test = DL_TRNG_getDigitalHealthTestResults(config->base);
66+
if (!dig_test) {
67+
DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_TEST_DIG);
68+
return;
69+
}
70+
71+
/* Run ANALOG test */
72+
ana_test = DL_TRNG_getAnalogHealthTestResults(config->base);
73+
if (!ana_test) {
74+
DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_TEST_ANA);
75+
return;
76+
}
77+
78+
/*
79+
* If both tests are successful, set DECIM RATE, enable IRQ_CAPTURE_RDY
80+
* and discard first sample from DATA_CAPTURE register
81+
*/
82+
if ((dig_test == DL_TRNG_DIGITAL_HEALTH_TEST_SUCCESS) &&
83+
(ana_test == DL_TRNG_ANALOG_HEALTH_TEST_SUCCESS)) {
84+
DL_TRNG_clearInterruptStatus(config->base,
85+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
86+
DL_TRNG_setDecimationRate(config->base,
87+
(DL_TRNG_DECIMATION_RATE)TRNG_DECIMATION_RATE);
88+
DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT);
89+
DL_TRNG_enableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
90+
DL_TRNG_getCapture(config->base);
91+
return;
92+
}
93+
}
94+
95+
if ((status & DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT)) {
96+
entropy_data = DL_TRNG_getCapture(config->base);
97+
bytes_written = ring_buf_put(&data->entropy_pool, (uint8_t *)&entropy_data,
98+
TRNG_SAMPLE_SIZE);
99+
100+
/* If the ring buf is exhausted, disable the interrupt in IMASK */
101+
if (bytes_written != TRNG_SAMPLE_SIZE) {
102+
DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
103+
}
104+
105+
DL_TRNG_clearInterruptStatus(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
106+
k_sem_give(&data->sem_sync);
107+
}
108+
}
109+
110+
static int entropy_mspm0_trng_get_entropy(const struct device *dev,
111+
uint8_t *buffer, uint16_t length)
112+
{
113+
const struct entropy_mspm0_trng_config *config = dev->config;
114+
struct entropy_mspm0_trng_data *data = dev->data;
115+
uint16_t bytes_read;
116+
117+
while (length) {
118+
k_sem_take(&data->sem_lock, K_FOREVER);
119+
bytes_read = ring_buf_get(&data->entropy_pool, buffer, length);
120+
k_sem_give(&data->sem_lock);
121+
122+
/*
123+
* If no bytes read, i.e ring buf is exhausted, enable the interrupt and
124+
* wait until the additional entropy is available in ring buf.
125+
*/
126+
if (bytes_read == 0U) {
127+
DL_TRNG_enableInterrupt(config->base,
128+
DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
129+
k_sem_take(&data->sem_sync, K_FOREVER);
130+
continue;
131+
}
132+
133+
buffer += bytes_read;
134+
length -= bytes_read;
135+
}
136+
137+
return 0;
138+
}
139+
140+
static int entropy_mspm0_trng_get_entropy_isr(const struct device *dev, uint8_t *buffer,
141+
uint16_t length, uint32_t flags)
142+
{
143+
const struct entropy_mspm0_trng_config *config = dev->config;
144+
struct entropy_mspm0_trng_data *data = dev->data;
145+
uint16_t bytes_read;
146+
uint16_t total_read;
147+
uint32_t entropy_data;
148+
unsigned int key;
149+
150+
/* Try to get entropy from existing ring buffer */
151+
bytes_read = ring_buf_get(&data->entropy_pool, buffer, length);
152+
total_read = bytes_read;
153+
154+
if ((bytes_read == length) || ((flags & ENTROPY_BUSYWAIT) == 0U)) {
155+
/* Either we got all requested data, or busy-waiting is not allowed */
156+
return total_read;
157+
}
158+
159+
/* Busy-wait for additional data (only if ENTROPY_BUSYWAIT is set) */
160+
buffer += bytes_read;
161+
length -= bytes_read;
162+
163+
while (length) {
164+
key = irq_lock();
165+
166+
/* Check if data is ready by checking IRQ_CAPTURED_RDY */
167+
if (DL_TRNG_isCaptureReady(config->base)) {
168+
entropy_data = DL_TRNG_getCapture(config->base);
169+
ring_buf_put(&data->entropy_pool, (uint8_t *)&entropy_data,
170+
TRNG_SAMPLE_SIZE);
171+
}
172+
173+
bytes_read = ring_buf_get(&data->entropy_pool, buffer, length);
174+
175+
irq_unlock(key);
176+
177+
if (bytes_read) {
178+
buffer += bytes_read;
179+
length -= bytes_read;
180+
total_read += bytes_read;
181+
} else {
182+
k_busy_wait(TRNG_SAMPLE_GENERATE_TIME);
183+
}
184+
}
185+
186+
return total_read;
187+
}
188+
189+
static int entropy_mspm0_trng_init(const struct device *dev)
190+
{
191+
const struct entropy_mspm0_trng_config *config = dev->config;
192+
struct entropy_mspm0_trng_data *data = dev->data;
193+
194+
/* Initialize ring buffer for entropy storage */
195+
ring_buf_init(&data->entropy_pool, sizeof(data->pool_buffer), data->pool_buffer);
196+
197+
/* Enable TRNG power */
198+
DL_TRNG_enablePower(config->base);
199+
200+
/* Configure TRNG clock divider */
201+
DL_TRNG_setClockDivider(config->base, TRNG_CLOCK_DIVIDE_RATIO);
202+
203+
/* Disable the CAPTURE_RDY IRQ until health tests are complete */
204+
DL_TRNG_disableInterrupt(config->base, DL_TRNG_INTERRUPT_CAPTURE_RDY_EVENT);
205+
DL_TRNG_enableInterrupt(config->base, DL_TRNG_INTERRUPT_CMD_DONE_EVENT |
206+
DL_TRNG_INTERRUPT_HEALTH_FAIL_EVENT);
207+
208+
/* Move TRNG from OFF to NORM FUNC state */
209+
DL_TRNG_sendCommand(config->base, DL_TRNG_CMD_NORM_FUNC);
210+
211+
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
212+
entropy_mspm0_trng_isr, DEVICE_DT_INST_GET(0), 0);
213+
irq_enable(DT_INST_IRQN(0));
214+
215+
return 0;
216+
}
217+
218+
static const struct entropy_driver_api entropy_mspm0_trng_driver_api = {
219+
.get_entropy = entropy_mspm0_trng_get_entropy,
220+
.get_entropy_isr = entropy_mspm0_trng_get_entropy_isr,
221+
};
222+
223+
static const struct entropy_mspm0_trng_config entropy_mspm0_trng_config = {
224+
.base = (TRNG_Regs *)DT_INST_REG_ADDR(0),
225+
};
226+
227+
static struct entropy_mspm0_trng_data entropy_mspm0_trng_data = {
228+
.sem_lock = Z_SEM_INITIALIZER(entropy_mspm0_trng_data.sem_lock, 1, 1),
229+
.sem_sync = Z_SEM_INITIALIZER(entropy_mspm0_trng_data.sem_sync, 0, 1),
230+
};
231+
232+
DEVICE_DT_INST_DEFINE(0, entropy_mspm0_trng_init, NULL,
233+
&entropy_mspm0_trng_data,
234+
&entropy_mspm0_trng_config, PRE_KERNEL_1,
235+
CONFIG_ENTROPY_INIT_PRIORITY,
236+
&entropy_mspm0_trng_driver_api);

0 commit comments

Comments
 (0)