mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 14:00:19 +00:00 
			
		
		
		
	This commit introduces Random number generator to uboot. It uses DCP
driver for number generation.
RNG driver can be invoked by using below command on uboot prompt:-
           rng <number of bytes>
Signed-off-by: Kshitiz Varshney <kshitiz.varshney@nxp.com>
Reviewed-by: Ye Li <ye.li@nxp.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
		
	
			
		
			
				
	
	
		
			183 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * RNG driver for Freescale RNGC
 | 
						|
 *
 | 
						|
 * Copyright 2022 NXP
 | 
						|
 *
 | 
						|
 * Based on RNGC driver in drivers/char/hw_random/imx-rngc.c in Linux
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <cpu_func.h>
 | 
						|
#include <dm.h>
 | 
						|
#include <rng.h>
 | 
						|
#include <asm/cache.h>
 | 
						|
#include <asm/io.h>
 | 
						|
#include <dm/root.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
 | 
						|
#define DCP_RNG_MAX_FIFO_STORE_SIZE	4
 | 
						|
#define RNGC_VER_ID			0x0
 | 
						|
#define RNGC_COMMAND			0x4
 | 
						|
#define RNGC_CONTROL			0x8
 | 
						|
#define RNGC_STATUS			0xC
 | 
						|
#define RNGC_ERROR			0x10
 | 
						|
#define RNGC_FIFO			0x14
 | 
						|
 | 
						|
/* the fields in the ver id register */
 | 
						|
#define RNGC_TYPE_SHIFT			28
 | 
						|
 | 
						|
/* the rng_type field */
 | 
						|
#define RNGC_TYPE_RNGB			0x1
 | 
						|
#define RNGC_TYPE_RNGC			0x2
 | 
						|
 | 
						|
#define RNGC_CMD_CLR_ERR		0x20
 | 
						|
#define RNGC_CMD_SEED			0x2
 | 
						|
 | 
						|
#define RNGC_CTRL_AUTO_SEED		0x10
 | 
						|
 | 
						|
#define RNGC_STATUS_ERROR		0x10000
 | 
						|
#define RNGC_STATUS_FIFO_LEVEL_MASK	0xf00
 | 
						|
#define RNGC_STATUS_FIFO_LEVEL_SHIFT	8
 | 
						|
#define RNGC_STATUS_SEED_DONE		0x20
 | 
						|
#define RNGC_STATUS_ST_DONE		0x10
 | 
						|
 | 
						|
#define RNGC_ERROR_STATUS_STAT_ERR	0x8
 | 
						|
 | 
						|
#define RNGC_TIMEOUT			3000000U /* 3 sec */
 | 
						|
 | 
						|
struct imx_rngc_priv {
 | 
						|
	unsigned long base;
 | 
						|
};
 | 
						|
 | 
						|
static int rngc_read(struct udevice *dev, void *data, size_t len)
 | 
						|
{
 | 
						|
	struct imx_rngc_priv *priv = dev_get_priv(dev);
 | 
						|
	u8 buffer[DCP_RNG_MAX_FIFO_STORE_SIZE];
 | 
						|
	u32 status, level;
 | 
						|
	size_t size;
 | 
						|
 | 
						|
	while (len) {
 | 
						|
		status = readl(priv->base + RNGC_STATUS);
 | 
						|
 | 
						|
		/* is there some error while reading this random number? */
 | 
						|
		if (status & RNGC_STATUS_ERROR)
 | 
						|
			break;
 | 
						|
		/* how many random numbers are in FIFO? [0-16] */
 | 
						|
		level = (status & RNGC_STATUS_FIFO_LEVEL_MASK) >>
 | 
						|
			RNGC_STATUS_FIFO_LEVEL_SHIFT;
 | 
						|
 | 
						|
		if (level) {
 | 
						|
			/* retrieve a random number from FIFO */
 | 
						|
			*(u32 *)buffer = readl(priv->base + RNGC_FIFO);
 | 
						|
			size = min(len, sizeof(u32));
 | 
						|
			memcpy(data, buffer, size);
 | 
						|
			data += size;
 | 
						|
			len -= size;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return len ? -EIO : 0;
 | 
						|
}
 | 
						|
 | 
						|
static int rngc_init(struct imx_rngc_priv *priv)
 | 
						|
{
 | 
						|
	u32 cmd, ctrl, status, err_reg = 0;
 | 
						|
	unsigned long long timeval = 0;
 | 
						|
	unsigned long long timeout = RNGC_TIMEOUT;
 | 
						|
 | 
						|
	/* clear error */
 | 
						|
	cmd = readl(priv->base + RNGC_COMMAND);
 | 
						|
	writel(cmd | RNGC_CMD_CLR_ERR, priv->base + RNGC_COMMAND);
 | 
						|
 | 
						|
	/* create seed, repeat while there is some statistical error */
 | 
						|
	do {
 | 
						|
		/* seed creation */
 | 
						|
		cmd = readl(priv->base + RNGC_COMMAND);
 | 
						|
		writel(cmd | RNGC_CMD_SEED, priv->base + RNGC_COMMAND);
 | 
						|
 | 
						|
		udelay(1);
 | 
						|
		timeval += 1;
 | 
						|
 | 
						|
		status = readl(priv->base + RNGC_STATUS);
 | 
						|
		err_reg = readl(priv->base + RNGC_ERROR);
 | 
						|
 | 
						|
		if (status & (RNGC_STATUS_SEED_DONE | RNGC_STATUS_ST_DONE))
 | 
						|
			break;
 | 
						|
 | 
						|
		if (timeval > timeout) {
 | 
						|
			debug("rngc timed out\n");
 | 
						|
			return -ETIMEDOUT;
 | 
						|
		}
 | 
						|
	} while (err_reg == RNGC_ERROR_STATUS_STAT_ERR);
 | 
						|
 | 
						|
	if (err_reg)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * enable automatic seeding, the rngc creates a new seed automatically
 | 
						|
	 * after serving 2^20 random 160-bit words
 | 
						|
	 */
 | 
						|
	ctrl = readl(priv->base + RNGC_CONTROL);
 | 
						|
	ctrl |= RNGC_CTRL_AUTO_SEED;
 | 
						|
	writel(ctrl, priv->base + RNGC_CONTROL);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int rngc_probe(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct imx_rngc_priv *priv = dev_get_priv(dev);
 | 
						|
	fdt_addr_t addr;
 | 
						|
	u32 ver_id;
 | 
						|
	u8  rng_type;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	addr = dev_read_addr(dev);
 | 
						|
	if (addr == FDT_ADDR_T_NONE) {
 | 
						|
		ret = -EINVAL;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	priv->base = addr;
 | 
						|
	ver_id = readl(priv->base + RNGC_VER_ID);
 | 
						|
	rng_type = ver_id >> RNGC_TYPE_SHIFT;
 | 
						|
	/*
 | 
						|
	 * This driver supports only RNGC and RNGB. (There's a different
 | 
						|
	 * driver for RNGA.)
 | 
						|
	 */
 | 
						|
	if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) {
 | 
						|
		ret = -ENODEV;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = rngc_init(priv);
 | 
						|
	if (ret)
 | 
						|
		goto err;
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
err:
 | 
						|
	printf("%s error = %d\n", __func__, ret);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dm_rng_ops rngc_ops = {
 | 
						|
	.read = rngc_read,
 | 
						|
};
 | 
						|
 | 
						|
static const struct udevice_id rngc_dt_ids[] = {
 | 
						|
	{ .compatible = "fsl,imx25-rngb" },
 | 
						|
	{ }
 | 
						|
};
 | 
						|
 | 
						|
U_BOOT_DRIVER(dcp_rng) = {
 | 
						|
	.name = "dcp_rng",
 | 
						|
	.id = UCLASS_RNG,
 | 
						|
	.of_match = rngc_dt_ids,
 | 
						|
	.ops = &rngc_ops,
 | 
						|
	.probe = rngc_probe,
 | 
						|
	.priv_auto  = sizeof(struct imx_rngc_priv),
 | 
						|
	.flags = DM_FLAG_ALLOC_PRIV_DMA,
 | 
						|
};
 |