From 72029445230ebb4183014bd9cdfe623fddc00da8 Mon Sep 17 00:00:00 2001 From: Lin Jinhan Date: Mon, 25 Oct 2021 17:40:18 +0800 Subject: [PATCH] hwrng: rockchip: add trng v1 support Use "rockchip,trngv1" as compatible, first supported in RK3588. Signed-off-by: Lin Jinhan Change-Id: Ifa9637a64ceae1e7fd5478832b768838e43d460b --- drivers/char/hw_random/rockchip-rng.c | 159 +++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/drivers/char/hw_random/rockchip-rng.c b/drivers/char/hw_random/rockchip-rng.c index 87fb770014d4..f5075ae518c1 100644 --- a/drivers/char/hw_random/rockchip-rng.c +++ b/drivers/char/hw_random/rockchip-rng.c @@ -21,7 +21,7 @@ #define ROCKCHIP_AUTOSUSPEND_DELAY 100 #define ROCKCHIP_POLL_PERIOD_US 100 -#define ROCKCHIP_POLL_TIMEOUT_US 10000 +#define ROCKCHIP_POLL_TIMEOUT_US 50000 #define RK_MAX_RNG_BYTE (32) /* start of CRYPTO V1 register define */ @@ -53,8 +53,44 @@ #define CRYPTO_V2_RNG_DOUT_0 0x0010 /* end of CRYPTO V2 register define */ +/* start of TRNG_V1 register define */ +/* TRNG is no longer subordinate to the Crypto module */ +#define TRNG_V1_CTRL 0x0000 +#define TRNG_V1_CTRL_NOP _SBF(0, 0x00) +#define TRNG_V1_CTRL_RAND _SBF(0, 0x01) +#define TRNG_V1_CTRL_SEED _SBF(0, 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 _SBF(3, 0x00) +#define TRNG_V1_MODE_256_BIT _SBF(3, 0x01) + +#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 + +#define TRNG_V1_AUTO_RQSTS 0x0060 + +#define TRNG_V1_VERSION 0x00F0 +#define TRNG_v1_VERSION_CODE 0x46bc +/* end of TRNG_V1 register define */ + struct rk_rng_soc_data { u32 default_offset; + + int (*rk_rng_init)(struct hwrng *rng); int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait); }; @@ -104,6 +140,7 @@ static void rk_rng_cleanup(struct hwrng *rng) static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { int ret; + int read_len = 0; struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); if (!rk_rng->soc_data->rk_rng_read) @@ -115,7 +152,16 @@ static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) return ret; } - ret = rk_rng->soc_data->rk_rng_read(rng, buf, max, wait); + ret = 0; + while (max > ret) { + read_len = rk_rng->soc_data->rk_rng_read(rng, buf + ret, + max - ret, wait); + if (read_len < 0) { + ret = read_len; + break; + } + ret += read_len; + } pm_runtime_mark_last_busy(rk_rng->dev); pm_runtime_put_sync_autosuspend(rk_rng->dev); @@ -200,16 +246,117 @@ out: return ret; } +static int rk_trng_v1_init(struct hwrng *rng) +{ + int ret; + uint32_t auto_reseed_cnt = 1000; + uint32_t reg_ctrl, status, version; + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + + ret = pm_runtime_get_sync(rk_rng->dev); + if (ret < 0) { + pm_runtime_put_noidle(rk_rng->dev); + 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 exit; + } + + status = rk_rng_readl(rk_rng, TRNG_V1_STAT); + + /* TRNG should wait RAND_RDY triggered if it is busy or not seeded */ + if (!(status & TRNG_V1_STAT_SEEDED) || + (status & TRNG_V1_STAT_GENERATING) || + (status & TRNG_V1_STAT_RESEEDING)) { + readl_poll_timeout(rk_rng->mem + TRNG_V1_ISTAT, reg_ctrl, + (reg_ctrl & TRNG_V1_ISTAT_RAND_RDY), + ROCKCHIP_POLL_PERIOD_US, + ROCKCHIP_POLL_TIMEOUT_US); + } + + /* clear ISTAT flag because trng may auto reseeding when power on */ + reg_ctrl = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + rk_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); + + /* auto reseed after (auto_reseed_cnt * 16) byte rand generate */ + rk_rng_writel(rk_rng, auto_reseed_cnt, TRNG_V1_AUTO_RQSTS); + + ret = 0; +exit: + pm_runtime_mark_last_busy(rk_rng->dev); + pm_runtime_put_sync_autosuspend(rk_rng->dev); + + return ret; +} + +static int rk_trng_v1_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + int ret = 0; + u32 reg_ctrl = 0; + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + + /* clear ISTAT anyway */ + reg_ctrl = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + rk_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); + + /* generate 256bit random */ + 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); + + /* + * Generate2 56 bit random data will cost 1024 clock cycles. + * Estimated at 150M RNG module frequency, it takes 6.7 microseconds. + */ + udelay(10); + reg_ctrl = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + if (!(reg_ctrl & TRNG_V1_ISTAT_RAND_RDY)) { + /* wait RAND_RDY triggered */ + ret = readl_poll_timeout(rk_rng->mem + TRNG_V1_ISTAT, reg_ctrl, + (reg_ctrl & TRNG_V1_ISTAT_RAND_RDY), + ROCKCHIP_POLL_PERIOD_US, + ROCKCHIP_POLL_TIMEOUT_US); + if (ret < 0) + goto out; + } + + ret = min_t(size_t, max, RK_MAX_RNG_BYTE); + + rk_rng_read_regs(rk_rng, TRNG_V1_RAND0, buf, ret); + + /* clear all status flag */ + rk_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); +out: + /* close TRNG */ + rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); + + return ret; +} + static const struct rk_rng_soc_data rk_crypto_v1_soc_data = { .default_offset = 0, + .rk_rng_read = rk_crypto_v1_read, }; static const struct rk_rng_soc_data rk_crypto_v2_soc_data = { .default_offset = CRYPTO_V2_RNG_DEFAULT_OFFSET, + .rk_rng_read = rk_crypto_v2_read, }; +static const struct rk_rng_soc_data rk_trng_v1_soc_data = { + .default_offset = 0, + + .rk_rng_init = rk_trng_v1_init, + .rk_rng_read = rk_trng_v1_read, +}; + static const struct of_device_id rk_rng_dt_match[] = { { .compatible = "rockchip,cryptov1-rng", @@ -219,6 +366,10 @@ static const struct of_device_id rk_rng_dt_match[] = { .compatible = "rockchip,cryptov2-rng", .data = (void *)&rk_crypto_v2_soc_data, }, + { + .compatible = "rockchip,trngv1", + .data = (void *)&rk_trng_v1_soc_data, + }, { }, }; @@ -286,6 +437,10 @@ static int rk_rng_probe(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } + /* for some platform need hardware operation when probe */ + if (rk_rng->soc_data->rk_rng_init) + ret = rk_rng->soc_data->rk_rng_init(&rk_rng->rng); + return ret; }