blob: 4c49aa9e4446278f571ae5127f11d244448b2edb [file] [log] [blame]
Matthias Brugger18988ec2020-12-15 10:49:23 +01001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright 2020, Matthias Brugger <mbrugger@suse.com>
4 *
5 * Driver for Raspberry Pi hardware random number generator
6 */
7
Matthias Brugger18988ec2020-12-15 10:49:23 +01008#include <dm.h>
Matthias Brugger18988ec2020-12-15 10:49:23 +01009#include <rng.h>
10#include <asm/io.h>
Heinrich Schuchardt10a45cc2024-02-13 00:44:47 +010011#include <linux/delay.h>
Matthias Brugger18988ec2020-12-15 10:49:23 +010012
13#define usleep_range(a, b) udelay((b))
14
15#define RNG_CTRL_OFFSET 0x00
16#define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF
17#define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001
18#define RNG_CTRL_RNG_RBGEN_DISABLE 0x00000000
19
20#define RNG_SOFT_RESET_OFFSET 0x04
21#define RNG_SOFT_RESET 0x00000001
22
23#define RBG_SOFT_RESET_OFFSET 0x08
24#define RBG_SOFT_RESET 0x00000001
25
26#define RNG_INT_STATUS_OFFSET 0x18
27#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000
28#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020
29
30#define RNG_FIFO_DATA_OFFSET 0x20
31
32#define RNG_FIFO_COUNT_OFFSET 0x24
33#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF
34
Peter Robinsonb2ce4212021-02-18 20:06:37 +000035struct iproc_rng200_plat {
Matthias Bruggerd7abe4c2021-02-26 11:48:00 +010036 void __iomem *base;
Matthias Brugger18988ec2020-12-15 10:49:23 +010037};
38
Peter Robinsonb2ce4212021-02-18 20:06:37 +000039static void iproc_rng200_enable(struct iproc_rng200_plat *pdata, bool enable)
Matthias Brugger18988ec2020-12-15 10:49:23 +010040{
Matthias Bruggerd7abe4c2021-02-26 11:48:00 +010041 void __iomem *rng_base = pdata->base;
Matthias Brugger18988ec2020-12-15 10:49:23 +010042 u32 val;
43
44 val = readl(rng_base + RNG_CTRL_OFFSET);
45 val &= ~RNG_CTRL_RNG_RBGEN_MASK;
46 if (enable)
47 val |= RNG_CTRL_RNG_RBGEN_ENABLE;
48 else
49 val &= ~RNG_CTRL_RNG_RBGEN_ENABLE;
50
51 writel(val, rng_base + RNG_CTRL_OFFSET);
Matthias Brugger18988ec2020-12-15 10:49:23 +010052}
53
Peter Robinsonb2ce4212021-02-18 20:06:37 +000054static void iproc_rng200_restart(struct iproc_rng200_plat *pdata)
Matthias Brugger18988ec2020-12-15 10:49:23 +010055{
Matthias Bruggerd7abe4c2021-02-26 11:48:00 +010056 void __iomem *rng_base = pdata->base;
Matthias Brugger18988ec2020-12-15 10:49:23 +010057 u32 val;
58
59 iproc_rng200_enable(pdata, false);
60
61 /* Clear all interrupt status */
62 writel(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET);
63
64 /* Reset RNG and RBG */
65 val = readl(rng_base + RBG_SOFT_RESET_OFFSET);
66 val |= RBG_SOFT_RESET;
67 writel(val, rng_base + RBG_SOFT_RESET_OFFSET);
68
69 val = readl(rng_base + RNG_SOFT_RESET_OFFSET);
70 val |= RNG_SOFT_RESET;
71 writel(val, rng_base + RNG_SOFT_RESET_OFFSET);
72
73 val = readl(rng_base + RNG_SOFT_RESET_OFFSET);
74 val &= ~RNG_SOFT_RESET;
75 writel(val, rng_base + RNG_SOFT_RESET_OFFSET);
76
77 val = readl(rng_base + RBG_SOFT_RESET_OFFSET);
78 val &= ~RBG_SOFT_RESET;
79 writel(val, rng_base + RBG_SOFT_RESET_OFFSET);
80
81 iproc_rng200_enable(pdata, true);
82}
83
84static int iproc_rng200_read(struct udevice *dev, void *data, size_t len)
85{
Peter Robinsonb2ce4212021-02-18 20:06:37 +000086 struct iproc_rng200_plat *priv = dev_get_plat(dev);
Matthias Brugger18988ec2020-12-15 10:49:23 +010087 char *buf = (char *)data;
88 u32 num_remaining = len;
89 u32 status;
90
91 #define MAX_RESETS_PER_READ 1
92 u32 num_resets = 0;
93
94 while (num_remaining > 0) {
95
96 /* Is RNG sane? If not, reset it. */
97 status = readl(priv->base + RNG_INT_STATUS_OFFSET);
98 if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK |
99 RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) {
100
101 if (num_resets >= MAX_RESETS_PER_READ)
102 return len - num_remaining;
103
104 iproc_rng200_restart(priv);
105 num_resets++;
106 }
107
108 /* Are there any random numbers available? */
109 if ((readl(priv->base + RNG_FIFO_COUNT_OFFSET) &
110 RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) {
111
112 if (num_remaining >= sizeof(u32)) {
113 /* Buffer has room to store entire word */
114 *(u32 *)buf = readl(priv->base +
115 RNG_FIFO_DATA_OFFSET);
116 buf += sizeof(u32);
117 num_remaining -= sizeof(u32);
118 } else {
119 /* Buffer can only store partial word */
120 u32 rnd_number = readl(priv->base +
121 RNG_FIFO_DATA_OFFSET);
122 memcpy(buf, &rnd_number, num_remaining);
123 buf += num_remaining;
124 num_remaining = 0;
125 }
126
127 } else {
128 /* Can wait, give others chance to run */
129 usleep_range(min(num_remaining * 10, 500U), 500);
130 }
131 }
132
133 return 0;
134}
135
136static int iproc_rng200_probe(struct udevice *dev)
137{
Peter Robinsonb2ce4212021-02-18 20:06:37 +0000138 struct iproc_rng200_plat *priv = dev_get_plat(dev);
Matthias Brugger18988ec2020-12-15 10:49:23 +0100139
140 iproc_rng200_enable(priv, true);
141
142 return 0;
143}
144
145static int iproc_rng200_remove(struct udevice *dev)
146{
Peter Robinsonb2ce4212021-02-18 20:06:37 +0000147 struct iproc_rng200_plat *priv = dev_get_plat(dev);
Matthias Brugger18988ec2020-12-15 10:49:23 +0100148
149 iproc_rng200_enable(priv, false);
150
151 return 0;
152}
153
Peter Robinsonb2ce4212021-02-18 20:06:37 +0000154static int iproc_rng200_of_to_plat(struct udevice *dev)
Matthias Brugger18988ec2020-12-15 10:49:23 +0100155{
Peter Robinsonb2ce4212021-02-18 20:06:37 +0000156 struct iproc_rng200_plat *pdata = dev_get_plat(dev);
Matthias Brugger18988ec2020-12-15 10:49:23 +0100157
Matthias Bruggerd7abe4c2021-02-26 11:48:00 +0100158 pdata->base = devfdt_map_physmem(dev, sizeof(void *));
Matthias Brugger18988ec2020-12-15 10:49:23 +0100159 if (!pdata->base)
160 return -ENODEV;
161
162 return 0;
163}
164
165static const struct dm_rng_ops iproc_rng200_ops = {
166 .read = iproc_rng200_read,
167};
168
169static const struct udevice_id iproc_rng200_rng_match[] = {
170 { .compatible = "brcm,bcm2711-rng200", },
171 { .compatible = "brcm,iproc-rng200", },
172 {},
173};
174
175U_BOOT_DRIVER(iproc_rng200_rng) = {
176 .name = "iproc_rng200-rng",
177 .id = UCLASS_RNG,
178 .of_match = iproc_rng200_rng_match,
179 .ops = &iproc_rng200_ops,
180 .probe = iproc_rng200_probe,
181 .remove = iproc_rng200_remove,
Peter Robinsonb2ce4212021-02-18 20:06:37 +0000182 .priv_auto = sizeof(struct iproc_rng200_plat),
183 .of_to_plat = iproc_rng200_of_to_plat,
Matthias Brugger18988ec2020-12-15 10:49:23 +0100184};