You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
3.8 KiB
160 lines
3.8 KiB
/* |
|
* 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 DEVICE_API(entropy, 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);
|
|
|