blob: 31706960157c904360650c7642dfc9ee36283e55 [file] [log] [blame]
// 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,
};