xref: /optee_os/core/drivers/dra7_rng.c (revision 6594a67e27b93727546f46c71e292b0cfcdc18de)
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 
76*6594a67eSAndrew 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 
954d168941SAndrew F. Davis 			DMSG("Fixed FRO shutdown\n");
964d168941SAndrew F. Davis 		}
974d168941SAndrew F. Davis 	}
984d168941SAndrew F. Davis 	/* Read random value */
99*6594a67eSAndrew Davis 	*low_word = io_read32(rng + RNG_OUTPUT_L);
100*6594a67eSAndrew 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 
105*6594a67eSAndrew Davis uint8_t hw_get_random_byte(void)
106*6594a67eSAndrew Davis {
107*6594a67eSAndrew Davis 	static int pos;
108*6594a67eSAndrew Davis 	static union {
109*6594a67eSAndrew Davis 		uint32_t val[2];
110*6594a67eSAndrew Davis 		uint8_t byte[8];
111*6594a67eSAndrew Davis 	} random;
112*6594a67eSAndrew Davis 	uint8_t ret;
113*6594a67eSAndrew Davis 
114*6594a67eSAndrew Davis 	uint32_t exceptions = cpu_spin_lock_xsave(&rng_lock);
115*6594a67eSAndrew Davis 
116*6594a67eSAndrew Davis 	if (!pos)
117*6594a67eSAndrew Davis 		dra7_rng_read64(&random.val[0], &random.val[1]);
1184d168941SAndrew F. Davis 	ret = random.byte[pos];
1194d168941SAndrew F. Davis 
1204d168941SAndrew F. Davis 	pos = (pos + 1) % 8;
1214d168941SAndrew F. Davis 
122c6712bd0SAndrew Davis 	cpu_spin_unlock_xrestore(&rng_lock, exceptions);
1234d168941SAndrew F. Davis 
1244d168941SAndrew F. Davis 	return ret;
1254d168941SAndrew F. Davis }
1264d168941SAndrew F. Davis 
1274d168941SAndrew F. Davis static TEE_Result dra7_rng_init(void)
1284d168941SAndrew F. Davis {
1294d168941SAndrew F. Davis 	uint32_t val;
1304d168941SAndrew F. Davis 
131d11c29c3SAndrew Davis 	rng = (vaddr_t)phys_to_virt(RNG_BASE, MEM_AREA_IO_SEC, RNG_REG_SIZE);
132d11c29c3SAndrew Davis 
1334d168941SAndrew F. Davis 	/* Execute a software reset */
134918bb3a5SEtienne Carriere 	io_write32(rng + RNG_SOFT_RESET_REG, RNG_SOFT_RESET);
1354d168941SAndrew F. Davis 
1364d168941SAndrew F. Davis 	/* Wait for the software reset completion by polling */
137918bb3a5SEtienne Carriere 	while (io_read32(rng + RNG_SOFT_RESET_REG) & RNG_SOFT_RESET)
1384d168941SAndrew F. Davis 		;
1394d168941SAndrew F. Davis 
1404d168941SAndrew F. Davis 	/* Switch to low-power operating mode */
141918bb3a5SEtienne Carriere 	io_write32(rng + RNG_SYS_CONFIG_REG, RNG_AUTOIDLE);
1424d168941SAndrew F. Davis 
1434d168941SAndrew F. Davis 	/*
1444d168941SAndrew F. Davis 	 * Select the number of clock input cycles to the
1454d168941SAndrew F. Davis 	 * FROs between two samples
1464d168941SAndrew F. Davis 	 */
1474d168941SAndrew F. Davis 	val = 0;
1484d168941SAndrew F. Davis 
1494d168941SAndrew F. Davis 	/* Ensure initial latency */
1504d168941SAndrew F. Davis 	val |= RNG_CONFIG_MIN_REFIL_CYCLES <<
1514d168941SAndrew F. Davis 			RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
1524d168941SAndrew F. Davis 	val |= RNG_CONFIG_MAX_REFIL_CYCLES <<
1534d168941SAndrew F. Davis 			RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
154918bb3a5SEtienne Carriere 	io_write32(rng + RNG_CONFIG, val);
1554d168941SAndrew F. Davis 
1564d168941SAndrew F. Davis 	/* Configure the desired FROs */
157918bb3a5SEtienne Carriere 	io_write32(rng + RNG_FRODETUNE, 0x0);
1584d168941SAndrew F. Davis 
1594d168941SAndrew F. Davis 	/* Enable all FROs */
160918bb3a5SEtienne Carriere 	io_write32(rng + RNG_FROENABLE, 0xffffff);
1614d168941SAndrew F. Davis 
1624d168941SAndrew F. Davis 	/*
1634d168941SAndrew F. Davis 	 * Select the maximum number of samples after
1644d168941SAndrew F. Davis 	 * which if a repeating pattern is still detected, an
1654d168941SAndrew F. Davis 	 * alarm event is generated
1664d168941SAndrew F. Davis 	 */
1674d168941SAndrew F. Davis 	val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT;
1684d168941SAndrew F. Davis 
1694d168941SAndrew F. Davis 	/*
1704d168941SAndrew F. Davis 	 * Set the shutdown threshold to the number of FROs
1714d168941SAndrew F. Davis 	 * allowed to be shut downed
1724d168941SAndrew F. Davis 	 */
1734d168941SAndrew F. Davis 	val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT;
174918bb3a5SEtienne Carriere 	io_write32(rng + RNG_ALARMCNT, val);
1754d168941SAndrew F. Davis 
1764d168941SAndrew F. Davis 	/* Enable the RNG module */
1774d168941SAndrew F. Davis 	val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
1784d168941SAndrew F. Davis 	val |= ENABLE_TRNG;
179918bb3a5SEtienne Carriere 	io_write32(rng + RNG_CONTROL, val);
1804d168941SAndrew F. Davis 
1814d168941SAndrew F. Davis 	IMSG("DRA7x TRNG initialized");
1824d168941SAndrew F. Davis 
1834d168941SAndrew F. Davis 	return TEE_SUCCESS;
1844d168941SAndrew F. Davis }
1854d168941SAndrew F. Davis driver_init(dra7_rng_init);
186