drivers: entropy: add get_entropy_isr() for CC13x2/CC26x2

A non-blocking, isr-safe version of get_entropy() is necessary in order
to be called during boot time before POST_KERNEL initialization.
Otherwise a crash is seen as the existing get_entropy() implementation
uses k_sem and relies on interrupts.

Fixes #18629

Signed-off-by: Vincent Wan <vincent.wan@linaro.org>
This commit is contained in:
Vincent Wan 2020-05-21 14:06:33 -07:00 committed by Carles Cufí
commit e7a85ddef9

View file

@ -16,6 +16,11 @@
#include <driverlib/prcm.h>
#include <driverlib/trng.h>
#define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
#define US_PER_SAMPLE (1000000ULL * \
CONFIG_ENTROPY_CC13XX_CC26XX_SAMPLES_PER_CYCLE / CPU_FREQ + 1ULL)
struct entropy_cc13xx_cc26xx_data {
struct k_sem lock;
struct k_sem sync;
@ -31,6 +36,23 @@ get_dev_data(struct device *dev)
return dev->driver_data;
}
static void handle_shutdown_ovf(void)
{
uint32_t off;
/* Clear shutdown */
TRNGIntClear(TRNG_FRO_SHUTDOWN);
/* Disabled FROs */
off = sys_read32(TRNG_BASE + TRNG_O_ALARMSTOP);
/* Clear alarms */
sys_write32(0, TRNG_BASE + TRNG_O_ALARMMASK);
sys_write32(0, TRNG_BASE + TRNG_O_ALARMSTOP);
/* De-tune FROs */
sys_write32(off, TRNG_BASE + TRNG_O_FRODETUNE);
/* Re-enable FROs */
sys_write32(off, TRNG_BASE + TRNG_O_FROEN);
}
static int entropy_cc13xx_cc26xx_get_entropy(struct device *dev, uint8_t *buf,
uint16_t len)
{
@ -58,7 +80,7 @@ static int entropy_cc13xx_cc26xx_get_entropy(struct device *dev, uint8_t *buf,
static void entropy_cc13xx_cc26xx_isr(void *arg)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(arg);
uint32_t src, cnt, off;
uint32_t src, cnt;
uint32_t num[2];
/* Interrupt service routine as described in TRM section 18.6.1.3.2 */
@ -83,20 +105,73 @@ static void entropy_cc13xx_cc26xx_isr(void *arg)
* prevent further locking on to the sampling clock frequncy.
*/
if (src & TRNG_FRO_SHUTDOWN) {
/* Clear shutdown */
TRNGIntClear(TRNG_FRO_SHUTDOWN);
/* Disabled FROs */
off = sys_read32(TRNG_BASE + TRNG_O_ALARMSTOP);
/* Clear alarms */
sys_write32(0, TRNG_BASE + TRNG_O_ALARMMASK);
sys_write32(0, TRNG_BASE + TRNG_O_ALARMSTOP);
/* De-tune FROs */
sys_write32(off, TRNG_BASE + TRNG_O_FRODETUNE);
/* Re-enable FROs */
sys_write32(off, TRNG_BASE + TRNG_O_FROEN);
handle_shutdown_ovf();
}
}
static int entropy_cc13xx_cc26xx_get_entropy_isr(struct device *dev,
uint8_t *buf, uint16_t len, uint32_t flags)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
uint16_t cnt;
uint16_t read = len;
uint32_t src;
uint32_t num[2];
unsigned int key;
key = irq_lock();
cnt = ring_buf_get(&data->pool, buf, len);
irq_unlock(key);
if ((cnt != len) && ((flags & ENTROPY_BUSYWAIT) != 0U)) {
buf += cnt;
len -= cnt;
/* Allowed to busy-wait. We should use a polling approach */
while (len) {
key = irq_lock();
src = TRNGStatusGet();
if (src & TRNG_NUMBER_READY) {
/*
* This function acknowledges the ready
* status
*/
num[1] = TRNGNumberGet(TRNG_HI_WORD);
num[0] = TRNGNumberGet(TRNG_LOW_WORD);
ring_buf_put(&data->pool, (uint8_t *)num,
sizeof(num));
}
/*
* If interrupts were enabled during busy wait, this
* would allow us to pick up anything that has been put
* in by the ISR as well.
*/
cnt = ring_buf_get(&data->pool, buf, len);
if (src & TRNG_FRO_SHUTDOWN) {
handle_shutdown_ovf();
}
irq_unlock(key);
if (cnt) {
buf += cnt;
len -= cnt;
} else {
k_busy_wait(US_PER_SAMPLE);
}
}
} else {
read = cnt;
}
return read;
}
static int entropy_cc13xx_cc26xx_init(struct device *dev)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
@ -159,6 +234,7 @@ static int entropy_cc13xx_cc26xx_init(struct device *dev)
static const struct entropy_driver_api entropy_cc13xx_cc26xx_driver_api = {
.get_entropy = entropy_cc13xx_cc26xx_get_entropy,
.get_entropy_isr = entropy_cc13xx_cc26xx_get_entropy_isr,
};
static struct entropy_cc13xx_cc26xx_data entropy_cc13xx_cc26xx_data = {