11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
24d168941SAndrew F. Davis /*
34d168941SAndrew F. Davis * Copyright (c) 2016, Linaro Limited
44d168941SAndrew F. Davis */
54d168941SAndrew F. Davis
64d168941SAndrew F. Davis #include <initcall.h>
74d168941SAndrew F. Davis #include <io.h>
84d168941SAndrew F. Davis #include <keep.h>
94d168941SAndrew F. Davis #include <kernel/interrupt.h>
104d168941SAndrew F. Davis #include <kernel/misc.h>
114d168941SAndrew F. Davis #include <kernel/spinlock.h>
124d168941SAndrew F. Davis #include <mm/core_memprot.h>
134d168941SAndrew F. Davis #include <mm/core_mmu.h>
144d168941SAndrew F. Davis #include <platform_config.h>
154d168941SAndrew F. Davis #include <rng_support.h>
164d168941SAndrew F. Davis
174d168941SAndrew F. Davis #define RNG_OUTPUT_L 0x0000
184d168941SAndrew F. Davis #define RNG_OUTPUT_H 0x0004
194d168941SAndrew F. Davis #define RNG_STATUS 0x0008
204d168941SAndrew F. Davis # define RNG_READY BIT(0)
214d168941SAndrew F. Davis # define SHUTDOWN_OFLO BIT(1)
224d168941SAndrew F. Davis #define RNG_INTMASK 0x000C
234d168941SAndrew F. Davis #define RNG_INTACK 0x0010
244d168941SAndrew F. Davis #define RNG_CONTROL 0x0014
254d168941SAndrew F. Davis # define ENABLE_TRNG BIT(10)
264d168941SAndrew F. Davis #define RNG_CONFIG 0x0018
274d168941SAndrew F. Davis #define RNG_ALARMCNT 0x001C
284d168941SAndrew F. Davis #define RNG_FROENABLE 0x0020
294d168941SAndrew F. Davis #define RNG_FRODETUNE 0x0024
304d168941SAndrew F. Davis #define RNG_ALARMMASK 0x0028
314d168941SAndrew F. Davis #define RNG_ALARMSTOP 0x002C
324d168941SAndrew F. Davis #define RNG_LFSR_L 0x0030
334d168941SAndrew F. Davis #define RNG_LFSR_M 0x0034
344d168941SAndrew F. Davis #define RNG_LFSR_H 0x0038
354d168941SAndrew F. Davis #define RNG_COUNT 0x003C
364d168941SAndrew F. Davis #define RNG_OPTIONS 0x0078
374d168941SAndrew F. Davis #define RNG_EIP_REV 0x007C
384d168941SAndrew F. Davis #define RNG_MMR_STATUS_EN 0x1FD8
394d168941SAndrew F. Davis #define RNG_REV 0x1FE0
404d168941SAndrew F. Davis #define RNG_SYS_CONFIG_REG 0x1FE4
414d168941SAndrew F. Davis # define RNG_AUTOIDLE BIT(0)
424d168941SAndrew F. Davis #define RNG_MMR_STATUS_SET 0x1FEC
434d168941SAndrew F. Davis #define RNG_SOFT_RESET_REG 0x1FF0
444d168941SAndrew F. Davis # define RNG_SOFT_RESET BIT(0)
454d168941SAndrew F. Davis #define RNG_IRQ_EOI_REG 0x1FF4
464d168941SAndrew F. Davis #define RNG_IRQSTATUS 0x1FF8
474d168941SAndrew F. Davis
484d168941SAndrew F. Davis #define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
494d168941SAndrew F. Davis #define RNG_CONTROL_STARTUP_CYCLES_MASK GENMASK_32(31, 16)
504d168941SAndrew F. Davis
514d168941SAndrew F. Davis #define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
524d168941SAndrew F. Davis #define RNG_CONFIG_MAX_REFIL_CYCLES_MASK GENMASK_32(31, 16)
534d168941SAndrew F. Davis #define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
544d168941SAndrew F. Davis #define RNG_CONFIG_MIN_REFIL_CYCLES_MASK GENMASK_32(7, 0)
554d168941SAndrew F. Davis
564d168941SAndrew F. Davis #define RNG_ALARMCNT_ALARM_TH_SHIFT 0
574d168941SAndrew F. Davis #define RNG_ALARMCNT_ALARM_TH_MASK GENMASK_32(7, 0)
584d168941SAndrew F. Davis #define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
594d168941SAndrew F. Davis #define RNG_ALARMCNT_SHUTDOWN_TH_MASK GENMASK_32(20, 16)
604d168941SAndrew F. Davis
614d168941SAndrew F. Davis #define RNG_CONTROL_STARTUP_CYCLES 0xff
624d168941SAndrew F. Davis #define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
634d168941SAndrew F. Davis #define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
644d168941SAndrew F. Davis #define RNG_ALARM_THRESHOLD 0xff
654d168941SAndrew F. Davis #define RNG_SHUTDOWN_THRESHOLD 0x4
664d168941SAndrew F. Davis
674d168941SAndrew F. Davis #define RNG_FRO_MASK GENMASK_32(23, 0)
684d168941SAndrew F. Davis
694d168941SAndrew F. Davis #define RNG_REG_SIZE 0x2000
704d168941SAndrew F. Davis
71a5e82dc7SJerome Forissier register_phys_mem_pgdir(MEM_AREA_IO_SEC, RNG_BASE, RNG_REG_SIZE);
724d168941SAndrew F. Davis
734d168941SAndrew F. Davis static unsigned int rng_lock = SPINLOCK_UNLOCK;
74d11c29c3SAndrew Davis static vaddr_t rng;
754d168941SAndrew F. Davis
dra7_rng_read64(uint32_t * low_word,uint32_t * high_word)766594a67eSAndrew Davis static void dra7_rng_read64(uint32_t *low_word, uint32_t *high_word)
774d168941SAndrew F. Davis {
784d168941SAndrew F. Davis /* Is the result ready (available)? */
79918bb3a5SEtienne Carriere while (!(io_read32(rng + RNG_STATUS) & RNG_READY)) {
804d168941SAndrew F. Davis /* Is the shutdown threshold reached? */
81918bb3a5SEtienne Carriere if (io_read32(rng + RNG_STATUS) & SHUTDOWN_OFLO) {
82918bb3a5SEtienne Carriere uint32_t alarm = io_read32(rng + RNG_ALARMSTOP);
83918bb3a5SEtienne Carriere uint32_t tune = io_read32(rng + RNG_FRODETUNE);
84918bb3a5SEtienne Carriere
854d168941SAndrew F. Davis /* Clear the alarm events */
86918bb3a5SEtienne Carriere io_write32(rng + RNG_ALARMMASK, 0x0);
87918bb3a5SEtienne Carriere io_write32(rng + RNG_ALARMSTOP, 0x0);
884d168941SAndrew F. Davis /* De-tune offending FROs */
89918bb3a5SEtienne Carriere io_write32(rng + RNG_FRODETUNE, tune ^ alarm);
904d168941SAndrew F. Davis /* Re-enable the shut down FROs */
91918bb3a5SEtienne Carriere io_write32(rng + RNG_FROENABLE, RNG_FRO_MASK);
924d168941SAndrew F. Davis /* Clear the shutdown overflow event */
93918bb3a5SEtienne Carriere io_write32(rng + RNG_INTACK, SHUTDOWN_OFLO);
944d168941SAndrew F. Davis
95bce2f88aSVincent Mailhol DMSG("Fixed FRO shutdown");
964d168941SAndrew F. Davis }
974d168941SAndrew F. Davis }
984d168941SAndrew F. Davis /* Read random value */
996594a67eSAndrew Davis *low_word = io_read32(rng + RNG_OUTPUT_L);
1006594a67eSAndrew Davis *high_word = io_read32(rng + RNG_OUTPUT_H);
1014d168941SAndrew F. Davis /* Acknowledge read complete */
102918bb3a5SEtienne Carriere io_write32(rng + RNG_INTACK, RNG_READY);
1034d168941SAndrew F. Davis }
1044d168941SAndrew F. Davis
hw_get_random_bytes(void * buf,size_t len)105671dbd1eSAndrew Davis TEE_Result hw_get_random_bytes(void *buf, size_t len)
1066594a67eSAndrew Davis {
1076594a67eSAndrew Davis static union {
1086594a67eSAndrew Davis uint32_t val[2];
1096594a67eSAndrew Davis uint8_t byte[8];
110671dbd1eSAndrew Davis } fifo;
111671dbd1eSAndrew Davis static size_t fifo_pos;
112671dbd1eSAndrew Davis uint8_t *buffer = buf;
113671dbd1eSAndrew Davis size_t buffer_pos = 0;
1146594a67eSAndrew Davis
115*377f97baSAndrew Davis assert(rng);
116*377f97baSAndrew Davis
117671dbd1eSAndrew Davis while (buffer_pos < len) {
1186594a67eSAndrew Davis uint32_t exceptions = cpu_spin_lock_xsave(&rng_lock);
1196594a67eSAndrew Davis
120671dbd1eSAndrew Davis /* Refill our FIFO */
121671dbd1eSAndrew Davis if (fifo_pos == 0)
122671dbd1eSAndrew Davis dra7_rng_read64(&fifo.val[0], &fifo.val[1]);
1234d168941SAndrew F. Davis
124671dbd1eSAndrew Davis buffer[buffer_pos++] = fifo.byte[fifo_pos++];
125671dbd1eSAndrew Davis fifo_pos %= 8;
1264d168941SAndrew F. Davis
127c6712bd0SAndrew Davis cpu_spin_unlock_xrestore(&rng_lock, exceptions);
128671dbd1eSAndrew Davis }
1294d168941SAndrew F. Davis
130671dbd1eSAndrew Davis return TEE_SUCCESS;
1314d168941SAndrew F. Davis }
1324d168941SAndrew F. Davis
dra7_rng_init(void)1334d168941SAndrew F. Davis static TEE_Result dra7_rng_init(void)
1344d168941SAndrew F. Davis {
1354d168941SAndrew F. Davis uint32_t val;
1364d168941SAndrew F. Davis
137d11c29c3SAndrew Davis rng = (vaddr_t)phys_to_virt(RNG_BASE, MEM_AREA_IO_SEC, RNG_REG_SIZE);
138d11c29c3SAndrew Davis
1394d168941SAndrew F. Davis /* Execute a software reset */
140918bb3a5SEtienne Carriere io_write32(rng + RNG_SOFT_RESET_REG, RNG_SOFT_RESET);
1414d168941SAndrew F. Davis
1424d168941SAndrew F. Davis /* Wait for the software reset completion by polling */
143918bb3a5SEtienne Carriere while (io_read32(rng + RNG_SOFT_RESET_REG) & RNG_SOFT_RESET)
1444d168941SAndrew F. Davis ;
1454d168941SAndrew F. Davis
1464d168941SAndrew F. Davis /* Switch to low-power operating mode */
147918bb3a5SEtienne Carriere io_write32(rng + RNG_SYS_CONFIG_REG, RNG_AUTOIDLE);
1484d168941SAndrew F. Davis
1494d168941SAndrew F. Davis /*
1504d168941SAndrew F. Davis * Select the number of clock input cycles to the
1514d168941SAndrew F. Davis * FROs between two samples
1524d168941SAndrew F. Davis */
1534d168941SAndrew F. Davis val = 0;
1544d168941SAndrew F. Davis
1554d168941SAndrew F. Davis /* Ensure initial latency */
1564d168941SAndrew F. Davis val |= RNG_CONFIG_MIN_REFIL_CYCLES <<
1574d168941SAndrew F. Davis RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
1584d168941SAndrew F. Davis val |= RNG_CONFIG_MAX_REFIL_CYCLES <<
1594d168941SAndrew F. Davis RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
160918bb3a5SEtienne Carriere io_write32(rng + RNG_CONFIG, val);
1614d168941SAndrew F. Davis
1624d168941SAndrew F. Davis /* Configure the desired FROs */
163918bb3a5SEtienne Carriere io_write32(rng + RNG_FRODETUNE, 0x0);
1644d168941SAndrew F. Davis
1654d168941SAndrew F. Davis /* Enable all FROs */
166918bb3a5SEtienne Carriere io_write32(rng + RNG_FROENABLE, 0xffffff);
1674d168941SAndrew F. Davis
1684d168941SAndrew F. Davis /*
1694d168941SAndrew F. Davis * Select the maximum number of samples after
1704d168941SAndrew F. Davis * which if a repeating pattern is still detected, an
1714d168941SAndrew F. Davis * alarm event is generated
1724d168941SAndrew F. Davis */
1734d168941SAndrew F. Davis val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT;
1744d168941SAndrew F. Davis
1754d168941SAndrew F. Davis /*
1764d168941SAndrew F. Davis * Set the shutdown threshold to the number of FROs
1774d168941SAndrew F. Davis * allowed to be shut downed
1784d168941SAndrew F. Davis */
1794d168941SAndrew F. Davis val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT;
180918bb3a5SEtienne Carriere io_write32(rng + RNG_ALARMCNT, val);
1814d168941SAndrew F. Davis
1824d168941SAndrew F. Davis /* Enable the RNG module */
1834d168941SAndrew F. Davis val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
1844d168941SAndrew F. Davis val |= ENABLE_TRNG;
185918bb3a5SEtienne Carriere io_write32(rng + RNG_CONTROL, val);
1864d168941SAndrew F. Davis
1874d168941SAndrew F. Davis IMSG("DRA7x TRNG initialized");
1884d168941SAndrew F. Davis
1894d168941SAndrew F. Davis return TEE_SUCCESS;
1904d168941SAndrew F. Davis }
191*377f97baSAndrew Davis service_init_crypto(dra7_rng_init);
192