|  | /* | 
|  | * Copyright (c) 2018 Aurelien Jarno | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT atmel_sam_trng | 
|  |  | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/drivers/entropy.h> | 
|  | #include <errno.h> | 
|  | #include <zephyr/init.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <soc.h> | 
|  | #include <string.h> | 
|  |  | 
|  | struct trng_sam_dev_cfg { | 
|  | Trng *regs; | 
|  | }; | 
|  |  | 
|  | static inline bool _ready(Trng * const trng) | 
|  | { | 
|  | #ifdef TRNG_ISR_DATRDY | 
|  | return trng->TRNG_ISR & TRNG_ISR_DATRDY; | 
|  | #else | 
|  | return trng->INTFLAG.bit.DATARDY; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static inline uint32_t _data(Trng * const trng) | 
|  | { | 
|  | #ifdef REG_TRNG_DATA | 
|  | (void) trng; | 
|  | return TRNG->DATA.reg; | 
|  | #else | 
|  | return trng->TRNG_ODATA; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static int entropy_sam_wait_ready(Trng * const trng, uint32_t flags) | 
|  | { | 
|  | /* According to the reference manual, the generator provides | 
|  | * one 32-bit random value every 84 peripheral clock cycles. | 
|  | * MCK may not be smaller than HCLK/4, so it should not take | 
|  | * more than 336 HCLK ticks. Assuming the CPU can do 1 | 
|  | * instruction per HCLK the number of times to loop before | 
|  | * the TRNG is ready is less than 1000. And that is when | 
|  | * assuming the loop only takes 1 instruction. So looping a | 
|  | * million times should be more than enough. | 
|  | */ | 
|  | int timeout = 1000000; | 
|  |  | 
|  | while (!_ready(trng)) { | 
|  | if (timeout-- == 0) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | if ((flags & ENTROPY_BUSYWAIT) == 0U) { | 
|  | /* This internal function is used by both get_entropy, | 
|  | * and get_entropy_isr APIs. The later may call this | 
|  | * function with the ENTROPY_BUSYWAIT flag set. In | 
|  | * that case make no assumption that the kernel is | 
|  | * initialized when the function is called; so, just | 
|  | * do busy-wait for the random data to be ready. | 
|  | */ | 
|  | k_yield(); | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int entropy_sam_get_entropy_internal(const struct device *dev, | 
|  | uint8_t *buffer, | 
|  | uint16_t length, uint32_t flags) | 
|  | { | 
|  | const struct trng_sam_dev_cfg *config = dev->config; | 
|  | Trng *const trng = config->regs; | 
|  |  | 
|  | while (length > 0) { | 
|  | size_t to_copy; | 
|  | uint32_t value; | 
|  | int res; | 
|  |  | 
|  | res = entropy_sam_wait_ready(trng, flags); | 
|  | if (res < 0) { | 
|  | return res; | 
|  | } | 
|  |  | 
|  | value = _data(trng); | 
|  | to_copy = MIN(length, sizeof(value)); | 
|  |  | 
|  | memcpy(buffer, &value, to_copy); | 
|  | buffer += to_copy; | 
|  | length -= to_copy; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int entropy_sam_get_entropy(const struct device *dev, uint8_t *buffer, | 
|  | uint16_t length) | 
|  | { | 
|  | return entropy_sam_get_entropy_internal(dev, buffer, length, 0); | 
|  | } | 
|  |  | 
|  | static int entropy_sam_get_entropy_isr(const struct device *dev, | 
|  | uint8_t *buffer, | 
|  | uint16_t length, uint32_t flags) | 
|  | { | 
|  | uint16_t cnt = length; | 
|  |  | 
|  |  | 
|  | if ((flags & ENTROPY_BUSYWAIT) == 0U) { | 
|  | const struct trng_sam_dev_cfg *config = dev->config; | 
|  | /* No busy wait; return whatever data is available. */ | 
|  |  | 
|  | Trng * const trng = config->regs; | 
|  |  | 
|  | do { | 
|  | size_t to_copy; | 
|  | uint32_t value; | 
|  |  | 
|  | if (!_ready(trng)) { | 
|  |  | 
|  | /* Data not ready */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | value = _data(trng); | 
|  | to_copy = MIN(length, sizeof(value)); | 
|  |  | 
|  | memcpy(buffer, &value, to_copy); | 
|  | buffer += to_copy; | 
|  | length -= to_copy; | 
|  |  | 
|  | } while (length > 0); | 
|  |  | 
|  | return cnt - length; | 
|  |  | 
|  | } else { | 
|  | /* Allowed to busy-wait */ | 
|  | int ret = | 
|  | entropy_sam_get_entropy_internal(dev, | 
|  | buffer, length, flags); | 
|  |  | 
|  | if (ret == 0) { | 
|  | /* Data retrieved successfully. */ | 
|  | return cnt; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int entropy_sam_init(const struct device *dev) | 
|  | { | 
|  | const struct trng_sam_dev_cfg *config = dev->config; | 
|  | Trng *const trng = config->regs; | 
|  |  | 
|  | #ifdef MCLK | 
|  | /* Enable the MCLK */ | 
|  | MCLK->APBCMASK.bit.TRNG_ = 1; | 
|  |  | 
|  | /* Enable the TRNG */ | 
|  | trng->CTRLA.bit.ENABLE = 1; | 
|  | #else | 
|  | /* Enable the user interface clock */ | 
|  | soc_pmc_peripheral_enable(DT_INST_PROP(0, peripheral_id)); | 
|  |  | 
|  | /* Enable the TRNG */ | 
|  | trng->TRNG_CR = TRNG_CR_KEY_PASSWD | TRNG_CR_ENABLE; | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct entropy_driver_api entropy_sam_api = { | 
|  | .get_entropy = entropy_sam_get_entropy, | 
|  | .get_entropy_isr = entropy_sam_get_entropy_isr | 
|  | }; | 
|  |  | 
|  | static const struct trng_sam_dev_cfg trng_sam_cfg = { | 
|  | .regs = (Trng *)DT_INST_REG_ADDR(0), | 
|  | }; | 
|  |  | 
|  | DEVICE_DT_INST_DEFINE(0, | 
|  | entropy_sam_init, NULL, | 
|  | NULL, &trng_sam_cfg, | 
|  | PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY, | 
|  | &entropy_sam_api); |