// 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, };