mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 11:50:43 +09:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user