/* * Copyright (c) 2020 Lemonbeat GmbH * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT silabs_gecko_trng #include #include #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);