blob: 6b19c171fcdae4bf607ef81b4c2d8728c79cfaec [file] [log] [blame]
Kshitiz Varshney8428a492022-12-22 09:50:27 +01001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * RNG driver for Freescale RNGC
4 *
5 * Copyright 2022 NXP
6 *
7 * Based on RNGC driver in drivers/char/hw_random/imx-rngc.c in Linux
8 */
9
Kshitiz Varshney8428a492022-12-22 09:50:27 +010010#include <cpu_func.h>
11#include <dm.h>
12#include <rng.h>
13#include <asm/cache.h>
14#include <asm/io.h>
15#include <dm/root.h>
16#include <linux/delay.h>
17#include <linux/kernel.h>
18
19#define DCP_RNG_MAX_FIFO_STORE_SIZE 4
20#define RNGC_VER_ID 0x0
21#define RNGC_COMMAND 0x4
22#define RNGC_CONTROL 0x8
23#define RNGC_STATUS 0xC
24#define RNGC_ERROR 0x10
25#define RNGC_FIFO 0x14
26
27/* the fields in the ver id register */
28#define RNGC_TYPE_SHIFT 28
29
30/* the rng_type field */
31#define RNGC_TYPE_RNGB 0x1
32#define RNGC_TYPE_RNGC 0x2
33
34#define RNGC_CMD_CLR_ERR 0x20
35#define RNGC_CMD_SEED 0x2
36
37#define RNGC_CTRL_AUTO_SEED 0x10
38
39#define RNGC_STATUS_ERROR 0x10000
40#define RNGC_STATUS_FIFO_LEVEL_MASK 0xf00
41#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
42#define RNGC_STATUS_SEED_DONE 0x20
43#define RNGC_STATUS_ST_DONE 0x10
44
45#define RNGC_ERROR_STATUS_STAT_ERR 0x8
46
47#define RNGC_TIMEOUT 3000000U /* 3 sec */
48
49struct imx_rngc_priv {
50 unsigned long base;
51};
52
53static int rngc_read(struct udevice *dev, void *data, size_t len)
54{
55 struct imx_rngc_priv *priv = dev_get_priv(dev);
56 u8 buffer[DCP_RNG_MAX_FIFO_STORE_SIZE];
57 u32 status, level;
58 size_t size;
59
60 while (len) {
61 status = readl(priv->base + RNGC_STATUS);
62
63 /* is there some error while reading this random number? */
64 if (status & RNGC_STATUS_ERROR)
65 break;
66 /* how many random numbers are in FIFO? [0-16] */
67 level = (status & RNGC_STATUS_FIFO_LEVEL_MASK) >>
68 RNGC_STATUS_FIFO_LEVEL_SHIFT;
69
70 if (level) {
71 /* retrieve a random number from FIFO */
72 *(u32 *)buffer = readl(priv->base + RNGC_FIFO);
73 size = min(len, sizeof(u32));
74 memcpy(data, buffer, size);
75 data += size;
76 len -= size;
77 }
78 }
79
80 return len ? -EIO : 0;
81}
82
83static int rngc_init(struct imx_rngc_priv *priv)
84{
85 u32 cmd, ctrl, status, err_reg = 0;
86 unsigned long long timeval = 0;
87 unsigned long long timeout = RNGC_TIMEOUT;
88
89 /* clear error */
90 cmd = readl(priv->base + RNGC_COMMAND);
91 writel(cmd | RNGC_CMD_CLR_ERR, priv->base + RNGC_COMMAND);
92
93 /* create seed, repeat while there is some statistical error */
94 do {
95 /* seed creation */
96 cmd = readl(priv->base + RNGC_COMMAND);
97 writel(cmd | RNGC_CMD_SEED, priv->base + RNGC_COMMAND);
98
99 udelay(1);
100 timeval += 1;
101
102 status = readl(priv->base + RNGC_STATUS);
103 err_reg = readl(priv->base + RNGC_ERROR);
104
105 if (status & (RNGC_STATUS_SEED_DONE | RNGC_STATUS_ST_DONE))
106 break;
107
108 if (timeval > timeout) {
109 debug("rngc timed out\n");
110 return -ETIMEDOUT;
111 }
112 } while (err_reg == RNGC_ERROR_STATUS_STAT_ERR);
113
114 if (err_reg)
115 return -EIO;
116
117 /*
118 * enable automatic seeding, the rngc creates a new seed automatically
119 * after serving 2^20 random 160-bit words
120 */
121 ctrl = readl(priv->base + RNGC_CONTROL);
122 ctrl |= RNGC_CTRL_AUTO_SEED;
123 writel(ctrl, priv->base + RNGC_CONTROL);
124 return 0;
125}
126
127static int rngc_probe(struct udevice *dev)
128{
129 struct imx_rngc_priv *priv = dev_get_priv(dev);
130 fdt_addr_t addr;
131 u32 ver_id;
132 u8 rng_type;
133 int ret;
134
135 addr = dev_read_addr(dev);
136 if (addr == FDT_ADDR_T_NONE) {
137 ret = -EINVAL;
138 goto err;
139 }
140
141 priv->base = addr;
142 ver_id = readl(priv->base + RNGC_VER_ID);
143 rng_type = ver_id >> RNGC_TYPE_SHIFT;
144 /*
145 * This driver supports only RNGC and RNGB. (There's a different
146 * driver for RNGA.)
147 */
148 if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) {
149 ret = -ENODEV;
150 goto err;
151 }
152
153 ret = rngc_init(priv);
154 if (ret)
155 goto err;
156
157 return 0;
158
159err:
160 printf("%s error = %d\n", __func__, ret);
161 return ret;
162}
163
164static const struct dm_rng_ops rngc_ops = {
165 .read = rngc_read,
166};
167
168static const struct udevice_id rngc_dt_ids[] = {
169 { .compatible = "fsl,imx25-rngb" },
170 { }
171};
172
173U_BOOT_DRIVER(dcp_rng) = {
174 .name = "dcp_rng",
175 .id = UCLASS_RNG,
176 .of_match = rngc_dt_ids,
177 .ops = &rngc_ops,
178 .probe = rngc_probe,
179 .priv_auto = sizeof(struct imx_rngc_priv),
180 .flags = DM_FLAG_ALLOC_PRIV_DMA,
181};