hwrng: rockchip: add trng v1 support

Use "rockchip,trngv1" as compatible, first supported in RK3588.

Signed-off-by: Lin Jinhan <troy.lin@rock-chips.com>
Change-Id: Ifa9637a64ceae1e7fd5478832b768838e43d460b
This commit is contained in:
Lin Jinhan
2021-10-25 17:40:18 +08:00
committed by Tao Huang
parent 60969b9366
commit 7202944523

View File

@@ -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;
}