161 lines
3.9 KiB
C
161 lines
3.9 KiB
C
/*
|
|
* Copyright (c) 2020 Lemonbeat GmbH
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT silabs_gecko_trng
|
|
|
|
#include <zephyr/drivers/entropy.h>
|
|
#include <string.h>
|
|
#include "soc.h"
|
|
#include "em_cmu.h"
|
|
|
|
#if defined(CONFIG_CRYPTO_ACC_GECKO_TRNG)
|
|
|
|
/*
|
|
* Select the correct Crypto ACC FIFO memory base address.
|
|
*
|
|
* Problem: Gecko SDK doesn't provide macros that check if SL_TRUSTZONE is used or not for Crypto
|
|
* ACC RNGOUT FIFO memory base address, like it does for register address definitions.
|
|
*
|
|
* Solution: Check which register base address is used for the Crypto ACC peripheral and select an
|
|
* appropriate FIFO memory base address.
|
|
*/
|
|
#if (CRYPTOACC_BASE == CRYPTOACC_S_BASE)
|
|
#define S2_FIFO_BASE CRYPTOACC_RNGOUT_FIFO_S_MEM_BASE
|
|
#else
|
|
#define S2_FIFO_BASE CRYPTOACC_RNGOUT_FIFO_MEM_BASE
|
|
#endif
|
|
|
|
/**
|
|
* Series 2 SoCs have different TRNG register definitions
|
|
*/
|
|
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) /* xG22 */
|
|
#define S2_FIFO_LEVEL (CRYPTOACC_RNGCTRL->FIFOLEVEL)
|
|
#define S2_CTRL (CRYPTOACC_RNGCTRL->RNGCTRL)
|
|
#define S2_CTRL_ENABLE (CRYPTOACC_RNGCTRL_ENABLE)
|
|
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7) /* xG27 */
|
|
#define S2_FIFO_LEVEL (CRYPTOACC->NDRNG_FIFOLEVEL)
|
|
#define S2_CTRL (CRYPTOACC->NDRNG_CONTROL)
|
|
#define S2_CTRL_ENABLE (CRYPTOACC_NDRNG_CONTROL_ENABLE)
|
|
#else /* _SILICON_LABS_32B_SERIES_2_CONFIG_* */
|
|
#error "Building for unsupported Series 2 SoC"
|
|
#endif /* _SILICON_LABS_32B_SERIES_2_CONFIG_* */
|
|
#endif /* CONFIG_CRYPTO_ACC_GECKO_TRNG */
|
|
|
|
static void entropy_gecko_trng_read(uint8_t *output, size_t len)
|
|
{
|
|
#ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
|
|
uint32_t tmp;
|
|
uint32_t *data = (uint32_t *) output;
|
|
|
|
/* Read known good available data. */
|
|
while (len >= 4) {
|
|
*data++ = TRNG0->FIFO;
|
|
len -= 4;
|
|
}
|
|
if (len > 0) {
|
|
/* Handle the case where len is not a multiple of 4
|
|
* and FIFO data is available.
|
|
*/
|
|
tmp = TRNG0->FIFO;
|
|
memcpy(data, (const uint8_t *) &tmp, len);
|
|
}
|
|
#else
|
|
memcpy(output, ((const uint8_t *) S2_FIFO_BASE), len);
|
|
#endif
|
|
}
|
|
|
|
static int entropy_gecko_trng_get_entropy(const struct device *dev,
|
|
uint8_t *buffer,
|
|
uint16_t length)
|
|
{
|
|
size_t count = 0;
|
|
size_t available;
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
while (length) {
|
|
#ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
|
|
available = TRNG0->FIFOLEVEL * 4;
|
|
#else
|
|
available = S2_FIFO_LEVEL * 4;
|
|
#endif
|
|
if (available == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
count = SL_MIN(length, available);
|
|
entropy_gecko_trng_read(buffer, count);
|
|
buffer += count;
|
|
length -= count;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int entropy_gecko_trng_get_entropy_isr(const struct device *dev,
|
|
uint8_t *buf,
|
|
uint16_t len, uint32_t flags)
|
|
{
|
|
|
|
if ((flags & ENTROPY_BUSYWAIT) == 0U) {
|
|
|
|
/* No busy wait; return whatever data is available. */
|
|
size_t count;
|
|
#ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
|
|
size_t available = TRNG0->FIFOLEVEL * 4;
|
|
#else
|
|
size_t available = S2_FIFO_LEVEL * 4;
|
|
#endif
|
|
|
|
if (available == 0) {
|
|
return -ENODATA;
|
|
}
|
|
count = SL_MIN(len, available);
|
|
entropy_gecko_trng_read(buf, count);
|
|
return count;
|
|
|
|
} else {
|
|
/* Allowed to busy-wait */
|
|
int ret = entropy_gecko_trng_get_entropy(dev, buf, len);
|
|
|
|
if (ret == 0) {
|
|
/* Data retrieved successfully. */
|
|
return len;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static int entropy_gecko_trng_init(const struct device *dev)
|
|
{
|
|
/* Enable the TRNG0 clock. */
|
|
#ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
|
|
CMU_ClockEnable(cmuClock_TRNG0, true);
|
|
|
|
/* Enable TRNG0. */
|
|
TRNG0->CONTROL = TRNG_CONTROL_ENABLE;
|
|
#else
|
|
/* Enable the CRYPTO ACC clock. */
|
|
CMU_ClockEnable(cmuClock_CRYPTOACC, true);
|
|
|
|
/* Enable TRNG */
|
|
S2_CTRL |= S2_CTRL_ENABLE;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct entropy_driver_api entropy_gecko_trng_api_funcs = {
|
|
.get_entropy = entropy_gecko_trng_get_entropy,
|
|
.get_entropy_isr = entropy_gecko_trng_get_entropy_isr
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(0,
|
|
entropy_gecko_trng_init, NULL,
|
|
NULL, NULL,
|
|
PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
|
|
&entropy_gecko_trng_api_funcs);
|