diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig index 4f6e367169..a89c899568 100644 --- a/drivers/rng/Kconfig +++ b/drivers/rng/Kconfig @@ -99,4 +99,10 @@ config TPM_RNG functionality. Enable random number generator on TPM devices. +config RNG_JH7110 + bool "StarFive JH7110 Random Number Generator support" + depends on DM_RNG && STARFIVE_JH7110 + help + Enable True Random Number Generator in StarFive JH7110 SoCs. + endif diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile index a5d3ca4130..7e64c4cdfc 100644 --- a/drivers/rng/Makefile +++ b/drivers/rng/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_RNG_IPROC200) += iproc_rng200.o obj-$(CONFIG_RNG_SMCCC_TRNG) += smccc_trng.o obj-$(CONFIG_RNG_ARM_RNDR) += arm_rndr.o obj-$(CONFIG_TPM_RNG) += tpm_rng.o +obj-$(CONFIG_RNG_JH7110) += jh7110_rng.o diff --git a/drivers/rng/jh7110_rng.c b/drivers/rng/jh7110_rng.c new file mode 100644 index 0000000000..eb21afe4e7 --- /dev/null +++ b/drivers/rng/jh7110_rng.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TRNG driver for the StarFive JH7110 SoC + * + */ + +#include +#include +#include +#include +#include +#include + +/* trng register offset */ +#define STARFIVE_CTRL 0x00 +#define STARFIVE_STAT 0x04 +#define STARFIVE_MODE 0x08 +#define STARFIVE_SMODE 0x0C +#define STARFIVE_IE 0x10 +#define STARFIVE_ISTAT 0x14 +#define STARFIVE_RAND0 0x20 +#define STARFIVE_RAND1 0x24 +#define STARFIVE_RAND2 0x28 +#define STARFIVE_RAND3 0x2C +#define STARFIVE_RAND4 0x30 +#define STARFIVE_RAND5 0x34 +#define STARFIVE_RAND6 0x38 +#define STARFIVE_RAND7 0x3C +#define STARFIVE_AUTO_RQSTS 0x60 +#define STARFIVE_AUTO_AGE 0x64 + +/* CTRL CMD */ +#define STARFIVE_CTRL_EXEC_NOP 0x0 +#define STARFIVE_CTRL_GENE_RANDNUM 0x1 +#define STARFIVE_CTRL_EXEC_RANDRESEED 0x2 + +/* STAT */ +#define STARFIVE_STAT_NONCE_MODE BIT(2) +#define STARFIVE_STAT_R256 BIT(3) +#define STARFIVE_STAT_MISSION_MODE BIT(8) +#define STARFIVE_STAT_SEEDED BIT(9) +#define STARFIVE_STAT_LAST_RESEED(x) ((x) << 16) +#define STARFIVE_STAT_SRVC_RQST BIT(27) +#define STARFIVE_STAT_RAND_GENERATING BIT(30) +#define STARFIVE_STAT_RAND_SEEDING BIT(31) +#define STARFIVE_STAT_RUNNING (STARFIVE_STAT_RAND_GENERATING | \ + STARFIVE_STAT_RAND_SEEDING) + +/* MODE */ +#define STARFIVE_MODE_R256 BIT(3) + +/* SMODE */ +#define STARFIVE_SMODE_NONCE_MODE BIT(2) +#define STARFIVE_SMODE_MISSION_MODE BIT(8) +#define STARFIVE_SMODE_MAX_REJECTS(x) ((x) << 16) + +/* IE */ +#define STARFIVE_IE_RAND_RDY_EN BIT(0) +#define STARFIVE_IE_SEED_DONE_EN BIT(1) +#define STARFIVE_IE_LFSR_LOCKUP_EN BIT(4) +#define STARFIVE_IE_GLBL_EN BIT(31) + +#define STARFIVE_IE_ALL (STARFIVE_IE_GLBL_EN | \ + STARFIVE_IE_RAND_RDY_EN | \ + STARFIVE_IE_SEED_DONE_EN | \ + STARFIVE_IE_LFSR_LOCKUP_EN) + +/* ISTAT */ +#define STARFIVE_ISTAT_RAND_RDY BIT(0) +#define STARFIVE_ISTAT_SEED_DONE BIT(1) +#define STARFIVE_ISTAT_LFSR_LOCKUP BIT(4) + +#define STARFIVE_RAND_LEN sizeof(u32) + +enum mode { + PRNG_128BIT, + PRNG_256BIT, +}; + +struct starfive_trng_plat { + void *base; + struct clk *hclk; + struct clk *ahb; + struct reset_ctl *rst; + u32 mode; +}; + +static inline int starfive_trng_wait_idle(struct starfive_trng_plat *trng) +{ + u32 stat; + + return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat, + !(stat & STARFIVE_STAT_RUNNING), + 100000); +} + +static inline void starfive_trng_irq_mask_clear(struct starfive_trng_plat *trng) +{ + /* clear register: ISTAT */ + u32 data = readl(trng->base + STARFIVE_ISTAT); + + writel(data, trng->base + STARFIVE_ISTAT); +} + +static int starfive_trng_cmd(struct starfive_trng_plat *trng, u32 cmd) +{ + u32 stat, flg; + int ret; + + switch (cmd) { + case STARFIVE_CTRL_GENE_RANDNUM: + writel(cmd, trng->base + STARFIVE_CTRL); + flg = STARFIVE_ISTAT_RAND_RDY; + break; + case STARFIVE_CTRL_EXEC_RANDRESEED: + writel(cmd, trng->base + STARFIVE_CTRL); + flg = STARFIVE_ISTAT_SEED_DONE; + break; + default: + return -EINVAL; + } + + ret = readl_relaxed_poll_timeout(trng->base + STARFIVE_ISTAT, stat, + (stat & flg), 1000); + writel(flg, trng->base + STARFIVE_ISTAT); + + return ret; +} + +static int starfive_trng_read(struct udevice *dev, void *data, size_t len) +{ + struct starfive_trng_plat *trng = dev_get_plat(dev); + u8 *buffer = data; + int iter_mask; + + if (trng->mode == PRNG_256BIT) + iter_mask = 7; + else + iter_mask = 3; + + for (int i = 0; len; ++i, i &= iter_mask) { + u32 val; + size_t step; + int ret; + + if (!i) { + ret = starfive_trng_cmd(trng, + STARFIVE_CTRL_GENE_RANDNUM); + if (ret) + return ret; + } + + val = readl(trng->base + STARFIVE_RAND0 + + (i * STARFIVE_RAND_LEN)); + step = min_t(size_t, len, STARFIVE_RAND_LEN); + memcpy(buffer, &val, step); + buffer += step; + len -= step; + } + + return 0; +} + +static int starfive_trng_init(struct starfive_trng_plat *trng) +{ + u32 mode, intr = 0; + + /* setup Auto Request/Age register */ + writel(0, trng->base + STARFIVE_AUTO_AGE); + writel(0, trng->base + STARFIVE_AUTO_RQSTS); + + /* clear register: ISTAT */ + starfive_trng_irq_mask_clear(trng); + + intr |= STARFIVE_IE_ALL; + writel(intr, trng->base + STARFIVE_IE); + + mode = readl(trng->base + STARFIVE_MODE); + + switch (trng->mode) { + case PRNG_128BIT: + mode &= ~STARFIVE_MODE_R256; + break; + case PRNG_256BIT: + mode |= STARFIVE_MODE_R256; + break; + default: + mode |= STARFIVE_MODE_R256; + break; + } + + writel(mode, trng->base + STARFIVE_MODE); + + return starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED); +} + +static int starfive_trng_probe(struct udevice *dev) +{ + struct starfive_trng_plat *pdata = dev_get_plat(dev); + int err; + + err = clk_enable(pdata->hclk); + if (err) + return err; + + err = clk_enable(pdata->ahb); + if (err) + goto err_ahb; + + err = reset_deassert(pdata->rst); + if (err) + goto err_reset; + + pdata->mode = PRNG_256BIT; + + err = starfive_trng_init(pdata); + if (err) + goto err_trng_init; + + return 0; + +err_trng_init: + reset_assert(pdata->rst); +err_reset: + clk_disable(pdata->ahb); +err_ahb: + clk_disable(pdata->hclk); + + return err; +} + +static int starfive_trng_of_to_plat(struct udevice *dev) +{ + struct starfive_trng_plat *pdata = dev_get_plat(dev); + + pdata->base = (void *)dev_read_addr(dev); + if (!pdata->base) + return -ENODEV; + + pdata->hclk = devm_clk_get(dev, "hclk"); + if (IS_ERR(pdata->hclk)) + return -ENODEV; + + pdata->ahb = devm_clk_get(dev, "ahb"); + if (IS_ERR(pdata->ahb)) + return -ENODEV; + + pdata->rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(pdata->rst)) + return -ENODEV; + + return 0; +} + +static const struct dm_rng_ops starfive_trng_ops = { + .read = starfive_trng_read, +}; + +static const struct udevice_id starfive_trng_match[] = { + { + .compatible = "starfive,jh7110-trng", + }, + {}, +}; + +U_BOOT_DRIVER(starfive_trng) = { + .name = "jh7110-trng", + .id = UCLASS_RNG, + .of_match = starfive_trng_match, + .probe = starfive_trng_probe, + .ops = &starfive_trng_ops, + .plat_auto = sizeof(struct starfive_trng_plat), + .of_to_plat = starfive_trng_of_to_plat, +};