1 /* $OpenBSD: mvrng.c,v 1.4 2021/10/24 17:52:26 mpi Exp $ */
2 /*
3 * Copyright (c) 2018 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/fdt.h>
28
29 /* Registers */
30 #define RNG_OUTPUT0 0x0000
31 #define RNG_OUTPUT1 0x0004
32 #define RNG_OUTPUT2 0x0008
33 #define RNG_OUTPUT3 0x000c
34 #define RNG_STATUS 0x0010
35 #define RNG_STATUS_READY (1 << 0)
36 #define RNG_STATUS_SHUTDOWN (1 << 1)
37 #define RNG_CONTROL 0x0014
38 #define RNG_CONTROL_TRNG_EN (1 << 10)
39 #define RNG_CONFIG 0x0018
40 #define RNG_CONFIG_MIN_CYCLES_SHIFT 0
41 #define RNG_CONFIG_MAX_CYCLES_SHIFT 16
42 #define RNG_FROENABLE 0x0020
43 #define RNG_FROENABLE_MASK 0xffffff
44 #define RNG_FRODETUNE 0x0024
45 #define RNG_ALARMMASK 0x0028
46 #define RNG_ALARMSTOP 0x002c
47
48 #define HREAD4(sc, reg) \
49 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
50 #define HWRITE4(sc, reg, val) \
51 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
52 #define HSET4(sc, reg, bits) \
53 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
54 #define HCLR4(sc, reg, bits) \
55 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
56
57 struct mvrng_softc {
58 struct device sc_dev;
59 bus_space_tag_t sc_iot;
60 bus_space_handle_t sc_ioh;
61
62 struct timeout sc_to;
63 };
64
65 int mvrng_match(struct device *, void *, void *);
66 void mvrng_attach(struct device *, struct device *, void *);
67
68 const struct cfattach mvrng_ca = {
69 sizeof (struct mvrng_softc), mvrng_match, mvrng_attach
70 };
71
72 struct cfdriver mvrng_cd = {
73 NULL, "mvrng", DV_DULL
74 };
75
76 void mvrng_rnd(void *);
77
78 int
mvrng_match(struct device * parent,void * match,void * aux)79 mvrng_match(struct device *parent, void *match, void *aux)
80 {
81 struct fdt_attach_args *faa = aux;
82
83 return OF_is_compatible(faa->fa_node, "marvell,armada-8k-rng");
84 }
85
86 void
mvrng_attach(struct device * parent,struct device * self,void * aux)87 mvrng_attach(struct device *parent, struct device *self, void *aux)
88 {
89 struct mvrng_softc *sc = (struct mvrng_softc *)self;
90 struct fdt_attach_args *faa = aux;
91
92 if (faa->fa_nreg < 1) {
93 printf(": no registers\n");
94 return;
95 }
96
97 sc->sc_iot = faa->fa_iot;
98 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
99 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
100 printf(": can't map registers\n");
101 return;
102 }
103
104 printf("\n");
105
106 /* Configure and enable the RNG. */
107 HWRITE4(sc, RNG_CONFIG, 0x5 << RNG_CONFIG_MIN_CYCLES_SHIFT |
108 0x22 << RNG_CONFIG_MAX_CYCLES_SHIFT);
109 HWRITE4(sc, RNG_FRODETUNE, 0);
110 HWRITE4(sc, RNG_FROENABLE, RNG_FROENABLE_MASK);
111 HSET4(sc, RNG_CONTROL, RNG_CONTROL_TRNG_EN);
112
113 timeout_set(&sc->sc_to, mvrng_rnd, sc);
114 mvrng_rnd(sc);
115 }
116
117 void
mvrng_rnd(void * arg)118 mvrng_rnd(void *arg)
119 {
120 struct mvrng_softc *sc = arg;
121 uint32_t status, detune;
122
123 status = HREAD4(sc, RNG_STATUS);
124 if (status & RNG_STATUS_SHUTDOWN) {
125 /* Clear alarms. */
126 HWRITE4(sc, RNG_ALARMMASK, 0);
127 HWRITE4(sc, RNG_ALARMSTOP, 0);
128
129 /* Detune FROs that are shutdown. */
130 detune = ~HREAD4(sc, RNG_FROENABLE) & RNG_FROENABLE_MASK;
131 HSET4(sc, RNG_FRODETUNE, detune);
132
133 /* Re-enable them. */
134 HWRITE4(sc, RNG_FROENABLE, RNG_FROENABLE_MASK);
135 HWRITE4(sc, RNG_STATUS, RNG_STATUS_SHUTDOWN);
136 }
137 if (status & RNG_STATUS_READY) {
138 enqueue_randomness(HREAD4(sc, RNG_OUTPUT0));
139 enqueue_randomness(HREAD4(sc, RNG_OUTPUT1));
140 enqueue_randomness(HREAD4(sc, RNG_OUTPUT2));
141 enqueue_randomness(HREAD4(sc, RNG_OUTPUT3));
142 HWRITE4(sc, RNG_STATUS, RNG_STATUS_READY);
143 }
144
145 timeout_add_sec(&sc->sc_to, 1);
146 }
147