| 
					
				 | 
			
			
				@@ -0,0 +1,422 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+From 8eff8eb83fc0ae8b5f76220e2bb8644d836e99ff Mon Sep 17 00:00:00 2001 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+From: Nicolas Frattaroli <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Date: Tue, 4 Feb 2025 16:35:50 +0100 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Subject: [PATCH] hwrng: rockchip - add support for rk3588's standalone TRNG 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The RK3588 SoC includes several TRNGs, one part of the Crypto IP block, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+and the other one (referred to as "trngv1") as a standalone new IP. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Add support for this new standalone TRNG to the driver by both 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+generalising it to support multiple different rockchip RNGs and then 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+implementing the required functionality for the new hardware. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+This work was partly based on the downstream vendor driver by Rockchip's 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Lin Jinhan, which is why they are listed as a Co-author. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+While the hardware does support notifying the CPU with an IRQ when the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+random data is ready, I've discovered while implementing the code to use 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+this interrupt that this results in significantly slower throughput of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+the TRNG even when under heavy CPU load. I assume this is because with 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+only 32 bytes of data per invocation, the overhead of reinitialising a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+completion, enabling the interrupt, sleeping and then triggering the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+completion in the IRQ handler is way more expensive than busylooping. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Speaking of busylooping, the poll interval for reading the ISTAT is an 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+atomic read with a delay of 0. In my testing, I've found that this gives 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+us the largest throughput, and it appears the random data is ready 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pretty much the moment we begin polling, as increasing the poll delay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+leads to a drop in throughput significant enough to not just be due to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+the poll interval missing the ideal timing by a microsecond or two. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+According to downstream, the IP should take 1024 clock cycles to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+generate 56 bits of random data, which at 150MHz should work out to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+6.8us. I did not test whether the data really does take 256/56*6.8us 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+to arrive, though changing the readl to a __raw_readl makes no 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+difference in throughput, and this data does pass the rngtest FIPS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+checks, so I'm not entirely sure what's going on but I presume it's got 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+something to do with the AHB bus speed and the memory barriers that 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+mainline's readl/writel functions insert. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The only other current SoC that uses this new IP is the Rockchip RV1106, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+but that SoC does not have mainline support as of the time of writing, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+so we make no effort to declare it as supported for now. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Co-developed-by: Lin Jinhan <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Signed-off-by: Lin Jinhan <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Signed-off-by: Nicolas Frattaroli <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Signed-off-by: Herbert Xu <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+--- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ drivers/char/hw_random/Kconfig        |   3 +- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ drivers/char/hw_random/rockchip-rng.c | 234 +++++++++++++++++++++++--- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 2 files changed, 216 insertions(+), 21 deletions(-) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+--- a/drivers/char/hw_random/Kconfig 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++++ b/drivers/char/hw_random/Kconfig 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -580,7 +580,8 @@ config HW_RANDOM_ROCKCHIP 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	default HW_RANDOM 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	help 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	  This driver provides kernel-side support for the True Random Number 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	  Generator hardware found on some Rockchip SoC like RK3566 or RK3568. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	  Generator hardware found on some Rockchip SoCs like RK3566, RK3568 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	  or RK3588. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	  To compile this driver as a module, choose M here: the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	  module will be called rockchip-rng. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+--- a/drivers/char/hw_random/rockchip-rng.c 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++++ b/drivers/char/hw_random/rockchip-rng.c 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -1,12 +1,14 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ // SPDX-License-Identifier: GPL-2.0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ /* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+- * rockchip-rng.c True Random Number Generator driver for Rockchip RK3568 SoC 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ * rockchip-rng.c True Random Number Generator driver for Rockchip SoCs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  * Copyright (c) 2022, Aurelien Jarno 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ * Copyright (c) 2025, Collabora Ltd. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  * Authors: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *  Lin Jinhan <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *  Aurelien Jarno <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ *  Nicolas Frattaroli <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #include <linux/clk.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #include <linux/hw_random.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -32,6 +34,9 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #define RK_RNG_SAMPLE_CNT		1000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* after how many bytes of output TRNGv1 implementations should be reseeded */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define RK_TRNG_V1_AUTO_RESEED_CNT	16000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ /* TRNG registers from RK3568 TRM-Part2, section 5.4.1 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #define TRNG_RST_CTL			0x0004 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #define TRNG_RNG_CTL			0x0400 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -49,25 +54,85 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #define TRNG_RNG_SAMPLE_CNT		0x0404 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ #define TRNG_RNG_DOUT			0x0410 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ * TRNG V1 register definitions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ * The TRNG V1 IP is a stand-alone TRNG implementation (not part of a crypto IP) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ * and can be found in the Rockchip RK3588 SoC 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_CTRL				0x0000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_CTRL_NOP			0x00 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_CTRL_RAND			0x01 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_CTRL_SEED			0x02 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_STAT				0x0004 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_STAT_SEEDED			BIT(9) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_STAT_GENERATING			BIT(30) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_STAT_RESEEDING			BIT(31) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_MODE				0x0008 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_MODE_128_BIT			(0x00 << 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_MODE_256_BIT			(0x01 << 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* Interrupt Enable register; unused because polling is faster */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_IE				0x0010 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_IE_GLBL_EN			BIT(31) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_IE_SEED_DONE_EN			BIT(1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_IE_RAND_RDY_EN			BIT(0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_ISTAT				0x0014 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_ISTAT_RAND_RDY			BIT(0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* RAND0 ~ RAND7 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_RAND0				0x0020 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_RAND7				0x003C 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* Auto Reseed Register */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_AUTO_RQSTS			0x0060 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_V1_VERSION				0x00F0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++#define TRNG_v1_VERSION_CODE			0x46bc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* end of TRNG_V1 register definitions */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++/* Before removing this assert, give rk3588_rng_read an upper bound of 32 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static_assert(RK_RNG_MAX_BYTE <= (TRNG_V1_RAND7 + 4 - TRNG_V1_RAND0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	      "You raised RK_RNG_MAX_BYTE and broke rk3588-rng, congrats."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ struct rk_rng { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct hwrng rng; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	void __iomem *base; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	int clk_num; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct clk_bulk_data *clk_bulks; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	const struct rk_rng_soc_data *soc_data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct device *dev; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++struct rk_rng_soc_data { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	int (*rk_rng_init)(struct hwrng *rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	void (*rk_rng_cleanup)(struct hwrng *rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	unsigned short quality; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	bool reset_optional; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ /* The mask in the upper 16 bits determines the bits that are updated */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ static void rk_rng_write_ctl(struct rk_rng *rng, u32 val, u32 mask) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	writel((mask << 16) | val, rng->base + TRNG_RNG_CTL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-static int rk_rng_init(struct hwrng *rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static inline void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	writel(val, rng->base + offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static inline u32 rk_rng_readl(struct rk_rng *rng, u32 offset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	return readl(rng->base + offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static int rk_rng_enable_clks(struct rk_rng *rk_rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	/* start clocks */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	if (ret < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -75,6 +140,18 @@ static int rk_rng_init(struct hwrng *rng 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 		return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static int rk3568_rng_init(struct hwrng *rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	ret = rk_rng_enable_clks(rk_rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (ret < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	/* set the sample period */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	writel(RK_RNG_SAMPLE_CNT, rk_rng->base + TRNG_RNG_SAMPLE_CNT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -87,7 +164,7 @@ static int rk_rng_init(struct hwrng *rng 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-static void rk_rng_cleanup(struct hwrng *rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static void rk3568_rng_cleanup(struct hwrng *rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -98,7 +175,7 @@ static void rk_rng_cleanup(struct hwrng 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static int rk3568_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -128,6 +205,114 @@ out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	return (ret < 0) ? ret : to_read; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static int rk3588_rng_init(struct hwrng *rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	u32 version, status, mask, istat; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	ret = rk_rng_enable_clks(rk_rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (ret < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	version = rk_rng_readl(rk_rng, TRNG_V1_VERSION); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (version != TRNG_v1_VERSION_CODE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		dev_err(rk_rng->dev, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++			"wrong trng version, expected = %08x, actual = %08x\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++			TRNG_V1_VERSION, version); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		ret = -EFAULT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		goto err_disable_clk; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	mask = TRNG_V1_STAT_SEEDED | TRNG_V1_STAT_GENERATING | 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	       TRNG_V1_STAT_RESEEDING; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (readl_poll_timeout(rk_rng->base + TRNG_V1_STAT, status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++			       (status & mask) == TRNG_V1_STAT_SEEDED, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++			       RK_RNG_POLL_PERIOD_US, RK_RNG_POLL_TIMEOUT_US) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		dev_err(rk_rng->dev, "timed out waiting for hwrng to reseed\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		ret = -ETIMEDOUT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		goto err_disable_clk; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	 * clear ISTAT flag, downstream advises to do this to avoid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	 * auto-reseeding "on power on" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	istat = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, istat, TRNG_V1_ISTAT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* auto reseed after RK_TRNG_V1_AUTO_RESEED_CNT bytes */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, RK_TRNG_V1_AUTO_RESEED_CNT / 16, TRNG_V1_AUTO_RQSTS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++err_disable_clk: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static void rk3588_rng_cleanup(struct hwrng *rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static int rk3588_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	int ret = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	u32 reg; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	ret = pm_runtime_resume_and_get(rk_rng->dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (ret < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* Clear ISTAT, even without interrupts enabled, this will be updated */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	reg = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* generate 256 bits of random data */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, TRNG_V1_MODE_256_BIT, TRNG_V1_MODE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, TRNG_V1_CTRL_RAND, TRNG_V1_CTRL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	ret = readl_poll_timeout_atomic(rk_rng->base + TRNG_V1_ISTAT, reg, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++					(reg & TRNG_V1_ISTAT_RAND_RDY), 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++					RK_RNG_POLL_TIMEOUT_US); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (ret < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* Read random data that's in registers TRNG_V1_RAND0 through RAND7 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	memcpy_fromio(buf, rk_rng->base + TRNG_V1_RAND0, to_read); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* Clear ISTAT */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	/* close the TRNG */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	pm_runtime_mark_last_busy(rk_rng->dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	pm_runtime_put_sync_autosuspend(rk_rng->dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	return (ret < 0) ? ret : to_read; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static const struct rk_rng_soc_data rk3568_soc_data = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.rk_rng_init = rk3568_rng_init, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.rk_rng_read = rk3568_rng_read, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.rk_rng_cleanup = rk3568_rng_cleanup, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.quality = 900, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.reset_optional = false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++static const struct rk_rng_soc_data rk3588_soc_data = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.rk_rng_init = rk3588_rng_init, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.rk_rng_read = rk3588_rng_read, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.rk_rng_cleanup = rk3588_rng_cleanup, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.quality = 999,		/* as determined by actual testing */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	.reset_optional = true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ static int rk_rng_probe(struct platform_device *pdev) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct device *dev = &pdev->dev; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -139,6 +324,7 @@ static int rk_rng_probe(struct platform_ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	if (!rk_rng) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 		return -ENOMEM; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng->soc_data = of_device_get_match_data(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	rk_rng->base = devm_platform_ioremap_resource(pdev, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	if (IS_ERR(rk_rng->base)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 		return PTR_ERR(rk_rng->base); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -148,24 +334,30 @@ static int rk_rng_probe(struct platform_ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 		return dev_err_probe(dev, rk_rng->clk_num, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 				     "Failed to get clks property\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	rst = devm_reset_control_array_get_exclusive(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	if (IS_ERR(rst)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-		return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	reset_control_assert(rst); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	udelay(2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	reset_control_deassert(rst); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (rk_rng->soc_data->reset_optional) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		rst = devm_reset_control_array_get_optional_exclusive(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		rst = devm_reset_control_array_get_exclusive(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	if (rst) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		if (IS_ERR(rst)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++			return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		reset_control_assert(rst); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		udelay(2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		reset_control_deassert(rst); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	platform_set_drvdata(pdev, rk_rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	rk_rng->rng.name = dev_driver_string(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	if (!IS_ENABLED(CONFIG_PM)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-		rk_rng->rng.init = rk_rng_init; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-		rk_rng->rng.cleanup = rk_rng_cleanup; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		rk_rng->rng.init = rk_rng->soc_data->rk_rng_init; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++		rk_rng->rng.cleanup = rk_rng->soc_data->rk_rng_cleanup; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	rk_rng->rng.read = rk_rng_read; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng->rng.read = rk_rng->soc_data->rk_rng_read; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	rk_rng->dev = dev; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	rk_rng->rng.quality = 900; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng->rng.quality = rk_rng->soc_data->quality; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	pm_runtime_set_autosuspend_delay(dev, RK_RNG_AUTOSUSPEND_DELAY); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	pm_runtime_use_autosuspend(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -184,7 +376,7 @@ static int __maybe_unused rk_rng_runtime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct rk_rng *rk_rng = dev_get_drvdata(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	rk_rng_cleanup(&rk_rng->rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	rk_rng->soc_data->rk_rng_cleanup(&rk_rng->rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -193,7 +385,7 @@ static int __maybe_unused rk_rng_runtime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	struct rk_rng *rk_rng = dev_get_drvdata(dev); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	return rk_rng_init(&rk_rng->rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	return rk_rng->soc_data->rk_rng_init(&rk_rng->rng); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ static const struct dev_pm_ops rk_rng_pm_ops = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -204,7 +396,8 @@ static const struct dev_pm_ops rk_rng_pm 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ static const struct of_device_id rk_rng_dt_match[] = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-	{ .compatible = "rockchip,rk3568-rng", }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	{ .compatible = "rockchip,rk3568-rng", .data = (void *)&rk3568_soc_data }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++	{ .compatible = "rockchip,rk3588-rng", .data = (void *)&rk3588_soc_data }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 	{ /* sentinel */ }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@@ -221,8 +414,9 @@ static struct platform_driver rk_rng_dri 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ module_platform_driver(rk_rng_driver); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-MODULE_DESCRIPTION("Rockchip RK3568 True Random Number Generator driver"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++MODULE_DESCRIPTION("Rockchip True Random Number Generator driver"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ MODULE_AUTHOR("Lin Jinhan <[email protected]>"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ MODULE_AUTHOR("Aurelien Jarno <[email protected]>"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ MODULE_AUTHOR("Daniel Golle <[email protected]>"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++MODULE_AUTHOR("Nicolas Frattaroli <[email protected]>"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ MODULE_LICENSE("GPL"); 
			 |