1 /* $OpenBSD: rkrng.c,v 1.6 2024/02/17 13:29:25 kettenis Exp $ */
2 /*
3 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/timeout.h>
22
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_clock.h>
28 #include <dev/ofw/fdt.h>
29
30 /* Registers */
31
32 /* V1 */
33 #define RNG_CTRL 0x0008
34 #define RNG_CTRL_START (1 << 8)
35 #define RNG_TRNG_CTRL 0x0200
36 #define RNG_TRNG_CTRL_OSC_ENABLE (1 << 16)
37 #define RNG_TRNG_CTRL_SAMPLE_PERIOD(x) (x)
38 #define RNG_DATA0 0x0204
39
40 /* True Random Number Generator (TRNG) */
41 #define TRNG_RST_CTL 0x0004
42 #define TRNG_RST_CTL_SW_RNG_RESET (0x1U << 1)
43 #define TRNG_CTL 0x0400
44 #define TRNG_CTL_RNG_START (0x1U << 0)
45 #define TRNG_CTL_RNG_ENABLE (0x1U << 1)
46 #define TRNG_CTL_RING_SEL_MASK (0x3U << 2)
47 #define TRNG_CTL_RING_SEL_SLOWEST (0x0U << 2)
48 #define TRNG_CTL_RING_SEL_SLOW (0x1U << 2)
49 #define TRNG_CTL_RING_SEL_FAST (0x2U << 2)
50 #define TRNG_CTL_RING_SEL_FASTEST (0x3U << 2)
51 #define TRNG_CTL_RNG_LEN_MASK (0x3U << 4)
52 #define TRNG_CTL_RNG_LEN_64BIT (0x0U << 4)
53 #define TRNG_CTL_RNG_LEN_128BIT (0x1U << 4)
54 #define TRNG_CTL_RNG_LEN_192BIT (0x2U << 4)
55 #define TRNG_CTL_RNG_LEN_256BIT (0x3U << 4)
56 #define TRNG_SAMPLE_CNT 0x0404
57 #define TRNG_DOUT_BASE 0x0410
58
59 #define HREAD4(sc, reg) \
60 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
61 #define HWRITE4(sc, reg, val) \
62 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
63
64 struct rkrng_v;
65
66 struct rkrng_softc {
67 struct device sc_dev;
68 const struct rkrng_v *sc_v;
69 bus_space_tag_t sc_iot;
70 bus_space_handle_t sc_ioh;
71
72 struct timeout sc_to;
73 int sc_started;
74 };
75
76 struct rkrng_v {
77 unsigned int version;
78 void (*start)(struct rkrng_softc *sc);
79 int (*starting)(struct rkrng_softc *sc);
80 void (*stop)(struct rkrng_softc *sc);
81 bus_size_t dout;
82 };
83
84 int rkrng_match(struct device *, void *, void *);
85 void rkrng_attach(struct device *, struct device *, void *);
86
87 const struct cfattach rkrng_ca = {
88 sizeof (struct rkrng_softc), rkrng_match, rkrng_attach
89 };
90
91 struct cfdriver rkrng_cd = {
92 NULL, "rkrng", DV_DULL
93 };
94
95 void rkrng_rnd(void *);
96
97 void rkrng_v1_start(struct rkrng_softc *);
98 int rkrng_v1_starting(struct rkrng_softc *);
99 void rkrng_v1_stop(struct rkrng_softc *);
100
101 static const struct rkrng_v rkrnv_v1 = {
102 .version = 1,
103 .start = rkrng_v1_start,
104 .starting = rkrng_v1_starting,
105 .stop = rkrng_v1_stop,
106 .dout = RNG_DATA0,
107 };
108
109 void rkrng_v2_start(struct rkrng_softc *);
110 int rkrng_v2_starting(struct rkrng_softc *);
111 void rkrng_v2_stop(struct rkrng_softc *);
112
113 static const struct rkrng_v rkrnv_v2 = {
114 .version = 2,
115 .start = rkrng_v2_start,
116 .starting = rkrng_v2_starting,
117 .stop = rkrng_v2_stop,
118 .dout = TRNG_DOUT_BASE,
119 };
120
121 int
rkrng_match(struct device * parent,void * match,void * aux)122 rkrng_match(struct device *parent, void *match, void *aux)
123 {
124 struct fdt_attach_args *faa = aux;
125
126 return OF_is_compatible(faa->fa_node, "rockchip,cryptov1-rng") ||
127 OF_is_compatible(faa->fa_node, "rockchip,rk3288-crypto") ||
128 OF_is_compatible(faa->fa_node, "rockchip,rk3328-crypto") ||
129 OF_is_compatible(faa->fa_node, "rockchip,rk3399-crypto") ||
130 OF_is_compatible(faa->fa_node, "rockchip,cryptov2-rng");
131 }
132
133 void
rkrng_attach(struct device * parent,struct device * self,void * aux)134 rkrng_attach(struct device *parent, struct device *self, void *aux)
135 {
136 struct rkrng_softc *sc = (struct rkrng_softc *)self;
137 struct fdt_attach_args *faa = aux;
138
139 if (OF_is_compatible(faa->fa_node, "rockchip,cryptov1-rng") ||
140 OF_is_compatible(faa->fa_node, "rockchip,rk3288-crypto") ||
141 OF_is_compatible(faa->fa_node, "rockchip,rk3328-crypto") ||
142 OF_is_compatible(faa->fa_node, "rockchip,rk3399-crypto"))
143 sc->sc_v = &rkrnv_v1;
144 else if (OF_is_compatible(faa->fa_node, "rockchip,cryptov2-rng"))
145 sc->sc_v = &rkrnv_v2;
146 else {
147 printf(": unhandled version\n");
148 return;
149 }
150
151 if (faa->fa_nreg < 1) {
152 printf(": no registers\n");
153 return;
154 }
155
156 sc->sc_iot = faa->fa_iot;
157 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
158 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
159 printf(": can't map registers\n");
160 return;
161 }
162
163 printf(": ver %u\n", sc->sc_v->version);
164
165 clock_set_assigned(faa->fa_node);
166 clock_enable_all(faa->fa_node);
167
168 timeout_set(&sc->sc_to, rkrng_rnd, sc);
169 rkrng_rnd(sc);
170 }
171
172 void
rkrng_v1_start(struct rkrng_softc * sc)173 rkrng_v1_start(struct rkrng_softc *sc)
174 {
175 HWRITE4(sc, RNG_TRNG_CTRL, RNG_TRNG_CTRL_OSC_ENABLE |
176 RNG_TRNG_CTRL_SAMPLE_PERIOD(100));
177 HWRITE4(sc, RNG_CTRL, (RNG_CTRL_START << 16) | RNG_CTRL_START);
178 }
179
180 int
rkrng_v1_starting(struct rkrng_softc * sc)181 rkrng_v1_starting(struct rkrng_softc *sc)
182 {
183 return (HREAD4(sc, RNG_CTRL) & RNG_CTRL_START);
184 }
185
186 void
rkrng_v1_stop(struct rkrng_softc * sc)187 rkrng_v1_stop(struct rkrng_softc *sc)
188 {
189 HWRITE4(sc, RNG_CTRL, (RNG_CTRL_START << 16) | 0);
190 }
191
192 void
rkrng_v2_start(struct rkrng_softc * sc)193 rkrng_v2_start(struct rkrng_softc *sc)
194 {
195 uint32_t ctl_m = TRNG_CTL_RNG_START | TRNG_CTL_RNG_ENABLE |
196 TRNG_CTL_RING_SEL_MASK | TRNG_CTL_RNG_LEN_MASK;
197 uint32_t ctl_v = TRNG_CTL_RNG_START | TRNG_CTL_RNG_ENABLE |
198 TRNG_CTL_RING_SEL_SLOW | TRNG_CTL_RNG_LEN_256BIT;
199
200 HWRITE4(sc, TRNG_SAMPLE_CNT, 100);
201 HWRITE4(sc, TRNG_CTL, (ctl_m << 16) | ctl_v);
202 }
203
204 int
rkrng_v2_starting(struct rkrng_softc * sc)205 rkrng_v2_starting(struct rkrng_softc *sc)
206 {
207 return (HREAD4(sc, TRNG_CTL) & TRNG_CTL_RNG_START);
208 }
209
210 void
rkrng_v2_stop(struct rkrng_softc * sc)211 rkrng_v2_stop(struct rkrng_softc *sc)
212 {
213 uint32_t ctl_m = TRNG_CTL_RNG_START | TRNG_CTL_RNG_ENABLE;
214
215 HWRITE4(sc, TRNG_CTL, (ctl_m << 16) | 0);
216 }
217
218 void
rkrng_rnd(void * arg)219 rkrng_rnd(void *arg)
220 {
221 struct rkrng_softc *sc = arg;
222 bus_size_t off;
223
224 if (!sc->sc_started) {
225 sc->sc_v->start(sc);
226 sc->sc_started = 1;
227 timeout_add_usec(&sc->sc_to, 100);
228 return;
229 }
230
231 if (sc->sc_v->starting(sc)) {
232 timeout_add_usec(&sc->sc_to, 100);
233 return;
234 }
235
236 for (off = 0; off < 32; off += 4)
237 enqueue_randomness(HREAD4(sc, sc->sc_v->dout + off));
238
239 sc->sc_v->stop(sc);
240 sc->sc_started = 0;
241
242 timeout_add_sec(&sc->sc_to, 1);
243 }
244