blob: fd36a69f88b7a8e64b116434461851d84d2ea357 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2016, Linaro Limited
*/
#include <initcall.h>
#include <io.h>
#include <keep.h>
#include <kernel/interrupt.h>
#include <kernel/misc.h>
#include <kernel/spinlock.h>
#include <mm/core_memprot.h>
#include <mm/core_mmu.h>
#include <platform_config.h>
#include <rng_support.h>
#define RNG_OUTPUT_L 0x0000
#define RNG_OUTPUT_H 0x0004
#define RNG_STATUS 0x0008
# define RNG_READY BIT(0)
# define SHUTDOWN_OFLO BIT(1)
#define RNG_INTMASK 0x000C
#define RNG_INTACK 0x0010
#define RNG_CONTROL 0x0014
# define ENABLE_TRNG BIT(10)
#define RNG_CONFIG 0x0018
#define RNG_ALARMCNT 0x001C
#define RNG_FROENABLE 0x0020
#define RNG_FRODETUNE 0x0024
#define RNG_ALARMMASK 0x0028
#define RNG_ALARMSTOP 0x002C
#define RNG_LFSR_L 0x0030
#define RNG_LFSR_M 0x0034
#define RNG_LFSR_H 0x0038
#define RNG_COUNT 0x003C
#define RNG_OPTIONS 0x0078
#define RNG_EIP_REV 0x007C
#define RNG_MMR_STATUS_EN 0x1FD8
#define RNG_REV 0x1FE0
#define RNG_SYS_CONFIG_REG 0x1FE4
# define RNG_AUTOIDLE BIT(0)
#define RNG_MMR_STATUS_SET 0x1FEC
#define RNG_SOFT_RESET_REG 0x1FF0
# define RNG_SOFT_RESET BIT(0)
#define RNG_IRQ_EOI_REG 0x1FF4
#define RNG_IRQSTATUS 0x1FF8
#define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
#define RNG_CONTROL_STARTUP_CYCLES_MASK GENMASK_32(31, 16)
#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK GENMASK_32(31, 16)
#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK GENMASK_32(7, 0)
#define RNG_ALARMCNT_ALARM_TH_SHIFT 0
#define RNG_ALARMCNT_ALARM_TH_MASK GENMASK_32(7, 0)
#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
#define RNG_ALARMCNT_SHUTDOWN_TH_MASK GENMASK_32(20, 16)
#define RNG_CONTROL_STARTUP_CYCLES 0xff
#define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
#define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
#define RNG_ALARM_THRESHOLD 0xff
#define RNG_SHUTDOWN_THRESHOLD 0x4
#define RNG_FRO_MASK GENMASK_32(23, 0)
#define RNG_REG_SIZE 0x2000
register_phys_mem_pgdir(MEM_AREA_IO_SEC, RNG_BASE, RNG_REG_SIZE);
static unsigned int rng_lock = SPINLOCK_UNLOCK;
uint8_t hw_get_random_byte(void)
{
static int pos;
static union {
uint32_t val[2];
uint8_t byte[8];
} random;
vaddr_t rng = (vaddr_t)phys_to_virt(RNG_BASE, MEM_AREA_IO_SEC);
uint8_t ret;
uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL);
cpu_spin_lock(&rng_lock);
if (!pos) {
/* Is the result ready (available)? */
while (!(io_read32(rng + RNG_STATUS) & RNG_READY)) {
/* Is the shutdown threshold reached? */
if (io_read32(rng + RNG_STATUS) & SHUTDOWN_OFLO) {
uint32_t alarm = io_read32(rng + RNG_ALARMSTOP);
uint32_t tune = io_read32(rng + RNG_FRODETUNE);
/* Clear the alarm events */
io_write32(rng + RNG_ALARMMASK, 0x0);
io_write32(rng + RNG_ALARMSTOP, 0x0);
/* De-tune offending FROs */
io_write32(rng + RNG_FRODETUNE, tune ^ alarm);
/* Re-enable the shut down FROs */
io_write32(rng + RNG_FROENABLE, RNG_FRO_MASK);
/* Clear the shutdown overflow event */
io_write32(rng + RNG_INTACK, SHUTDOWN_OFLO);
DMSG("Fixed FRO shutdown\n");
}
}
/* Read random value */
random.val[0] = io_read32(rng + RNG_OUTPUT_L);
random.val[1] = io_read32(rng + RNG_OUTPUT_H);
/* Acknowledge read complete */
io_write32(rng + RNG_INTACK, RNG_READY);
}
ret = random.byte[pos];
pos = (pos + 1) % 8;
cpu_spin_unlock(&rng_lock);
thread_set_exceptions(exceptions);
return ret;
}
static TEE_Result dra7_rng_init(void)
{
vaddr_t rng = (vaddr_t)phys_to_virt(RNG_BASE, MEM_AREA_IO_SEC);
uint32_t val;
/* Execute a software reset */
io_write32(rng + RNG_SOFT_RESET_REG, RNG_SOFT_RESET);
/* Wait for the software reset completion by polling */
while (io_read32(rng + RNG_SOFT_RESET_REG) & RNG_SOFT_RESET)
;
/* Switch to low-power operating mode */
io_write32(rng + RNG_SYS_CONFIG_REG, RNG_AUTOIDLE);
/*
* Select the number of clock input cycles to the
* FROs between two samples
*/
val = 0;
/* Ensure initial latency */
val |= RNG_CONFIG_MIN_REFIL_CYCLES <<
RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
val |= RNG_CONFIG_MAX_REFIL_CYCLES <<
RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
io_write32(rng + RNG_CONFIG, val);
/* Configure the desired FROs */
io_write32(rng + RNG_FRODETUNE, 0x0);
/* Enable all FROs */
io_write32(rng + RNG_FROENABLE, 0xffffff);
/*
* Select the maximum number of samples after
* which if a repeating pattern is still detected, an
* alarm event is generated
*/
val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT;
/*
* Set the shutdown threshold to the number of FROs
* allowed to be shut downed
*/
val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT;
io_write32(rng + RNG_ALARMCNT, val);
/* Enable the RNG module */
val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
val |= ENABLE_TRNG;
io_write32(rng + RNG_CONTROL, val);
IMSG("DRA7x TRNG initialized");
return TEE_SUCCESS;
}
driver_init(dra7_rng_init);