mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	Adds to support JH7110 TRNG driver which is based on linux kernel's jh7110-trng.c. This can support to generate 256-bit random numbers and 128-bit but this makes 256-bit default for convenience. Signed-off-by: Chanho Park <chanho61.park@samsung.com> Reviewed-by: Leo Yu-Chi Liang <ycliang@andestech.com>
		
			
				
	
	
		
			275 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * TRNG driver for the StarFive JH7110 SoC
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include <clk.h>
 | 
						|
#include <dm.h>
 | 
						|
#include <reset.h>
 | 
						|
#include <rng.h>
 | 
						|
#include <asm/io.h>
 | 
						|
#include <linux/iopoll.h>
 | 
						|
 | 
						|
/* 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,
 | 
						|
};
 |