| // 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); |