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
64struct rkrng_v;
65
66struct 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
76struct 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
84int	rkrng_match(struct device *, void *, void *);
85void	rkrng_attach(struct device *, struct device *, void *);
86
87const struct cfattach rkrng_ca = {
88	sizeof (struct rkrng_softc), rkrng_match, rkrng_attach
89};
90
91struct cfdriver rkrng_cd = {
92	NULL, "rkrng", DV_DULL
93};
94
95void	rkrng_rnd(void *);
96
97void	rkrng_v1_start(struct rkrng_softc *);
98int	rkrng_v1_starting(struct rkrng_softc *);
99void	rkrng_v1_stop(struct rkrng_softc *);
100
101static 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
109void	rkrng_v2_start(struct rkrng_softc *);
110int	rkrng_v2_starting(struct rkrng_softc *);
111void	rkrng_v2_stop(struct rkrng_softc *);
112
113static 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
121int
122rkrng_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
133void
134rkrng_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
172void
173rkrng_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
180int
181rkrng_v1_starting(struct rkrng_softc *sc)
182{
183	return (HREAD4(sc, RNG_CTRL) & RNG_CTRL_START);
184}
185
186void
187rkrng_v1_stop(struct rkrng_softc *sc)
188{
189	HWRITE4(sc, RNG_CTRL, (RNG_CTRL_START << 16) | 0);
190}
191
192void
193rkrng_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
204int
205rkrng_v2_starting(struct rkrng_softc *sc)
206{
207	return (HREAD4(sc, TRNG_CTL) & TRNG_CTL_RNG_START);
208}
209
210void
211rkrng_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
218void
219rkrng_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