1a1acc06fSMark Murray /*
2a1acc06fSMark Murray * Copyright (c) 2015, 2016, Stephen J. Kiernan
3a1acc06fSMark Murray * All rights reserved.
4a1acc06fSMark Murray *
5a1acc06fSMark Murray * Redistribution and use in source and binary forms, with or without
6a1acc06fSMark Murray * modification, are permitted provided that the following conditions
7a1acc06fSMark Murray * are met:
8a1acc06fSMark Murray * 1. Redistributions of source code must retain the above copyright
9a1acc06fSMark Murray * notice, this list of conditions and the following disclaimer.
10a1acc06fSMark Murray * 2. Redistributions in binary form must reproduce the above copyright
11a1acc06fSMark Murray * notice, this list of conditions and the following disclaimer in the
12a1acc06fSMark Murray * documentation and/or other materials provided with the distribution.
13a1acc06fSMark Murray *
14a1acc06fSMark Murray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a1acc06fSMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a1acc06fSMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a1acc06fSMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a1acc06fSMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a1acc06fSMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a1acc06fSMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a1acc06fSMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a1acc06fSMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a1acc06fSMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a1acc06fSMark Murray * SUCH DAMAGE.
25a1acc06fSMark Murray */
26a1acc06fSMark Murray
27a1acc06fSMark Murray
28a1acc06fSMark Murray #include <sys/param.h>
29a1acc06fSMark Murray #include <sys/kernel.h>
30e2e050c8SConrad Meyer #include <sys/ktr.h>
31a1acc06fSMark Murray #include <sys/lock.h>
32a1acc06fSMark Murray #include <sys/malloc.h>
33a1acc06fSMark Murray #include <sys/module.h>
34a1acc06fSMark Murray #include <sys/random.h>
35a1acc06fSMark Murray #include <sys/sbuf.h>
36a1acc06fSMark Murray #include <sys/sysctl.h>
37a1acc06fSMark Murray #include <sys/selinfo.h>
38a1acc06fSMark Murray #include <sys/systm.h>
39a1acc06fSMark Murray #include <sys/bus.h>
40a1acc06fSMark Murray #include <sys/rman.h>
41a1acc06fSMark Murray
42a1acc06fSMark Murray #include <machine/bus.h>
43a1acc06fSMark Murray #include <machine/resource.h>
44a1acc06fSMark Murray
45a1acc06fSMark Murray #include <dev/ofw/openfirm.h>
46a1acc06fSMark Murray #include <dev/ofw/ofw_bus.h>
47a1acc06fSMark Murray #include <dev/ofw/ofw_bus_subr.h>
48a1acc06fSMark Murray
49a1acc06fSMark Murray #include <dev/random/randomdev.h>
50a1acc06fSMark Murray #include <dev/random/random_harvestq.h>
51a1acc06fSMark Murray
52a1acc06fSMark Murray static device_attach_t bcm2835_rng_attach;
53a1acc06fSMark Murray static device_detach_t bcm2835_rng_detach;
54a1acc06fSMark Murray static device_probe_t bcm2835_rng_probe;
55a1acc06fSMark Murray
56a1acc06fSMark Murray #define RNG_CTRL 0x00 /* RNG Control Register */
57a1acc06fSMark Murray #define RNG_COMBLK1_OSC 0x003f0000 /* Combiner Blk 1 Oscillator */
58a1acc06fSMark Murray #define RNG_COMBLK1_OSC_SHIFT 16
59a1acc06fSMark Murray #define RNG_COMBLK2_OSC 0x0fc00000 /* Combiner Blk 2 Oscillator */
60a1acc06fSMark Murray #define RNG_COMBLK2_OSC_SHIFT 22
61a1acc06fSMark Murray #define RNG_JCLK_BYP_DIV_CNT 0x0000ff00 /* Jitter clk bypass divider
62a1acc06fSMark Murray count */
63a1acc06fSMark Murray #define RNG_JCLK_BYP_DIV_CNT_SHIFT 8
64a1acc06fSMark Murray #define RNG_JCLK_BYP_SRC 0x00000020 /* Jitter clk bypass source */
65a1acc06fSMark Murray #define RNG_JCLK_BYP_SEL 0x00000010 /* Jitter clk bypass select */
66a1acc06fSMark Murray #define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */
67a1acc06fSMark Murray #define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */
68a1acc06fSMark Murray
69e797dc58SGordon Bergling #define BCM2835_RNG_STATUS 0x04 /* BCM2835 RNG status register */
70e797dc58SGordon Bergling #define BCM2838_RNG_STATUS 0x18 /* BCM2838 RNG status register */
71a1acc06fSMark Murray
72e797dc58SGordon Bergling #define BCM2838_RNG_COUNT 0x24 /* How many values available */
73e797dc58SGordon Bergling #define BCM2838_COUNT_VAL_MASK 0x000000ff
74e797dc58SGordon Bergling
75e797dc58SGordon Bergling #define BCM2835_RND_VAL_SHIFT 24 /* Shift for valid words */
76e797dc58SGordon Bergling #define BCM2835_RND_VAL_MASK 0x000000ff /* Number valid words mask */
77e797dc58SGordon Bergling #define BCM2835_RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */
78e797dc58SGordon Bergling #define BCM2835_RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */
79e797dc58SGordon Bergling
80e797dc58SGordon Bergling #define BCM2835_RNG_DATA 0x08 /* RNG Data Register */
81e797dc58SGordon Bergling #define BCM2838_RNG_DATA 0x20
82a1acc06fSMark Murray #define RNG_FF_THRES 0x0c
83a1acc06fSMark Murray #define RNG_FF_THRES_MASK 0x0000001f
84a1acc06fSMark Murray
85e797dc58SGordon Bergling #define BCM2835_RNG_INT_MASK 0x10
86e797dc58SGordon Bergling #define BCM2835_RNG_INT_OFF_BIT 0x00000001
87a1acc06fSMark Murray
88a1acc06fSMark Murray #define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */
89a1acc06fSMark Murray #define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t))
90a1acc06fSMark Murray
91a1acc06fSMark Murray #define RNG_NUM_OSCILLATORS 6
92a1acc06fSMark Murray #define RNG_STALL_COUNT_DEFAULT 10
93a1acc06fSMark Murray
948e6a67d5SOleksandr Tymoshenko #define RNG_CALLOUT_TICKS (hz * 4)
958e6a67d5SOleksandr Tymoshenko
96e797dc58SGordon Bergling struct bcm_rng_conf {
97e797dc58SGordon Bergling bus_size_t control_reg;
98e797dc58SGordon Bergling bus_size_t status_reg;
99e797dc58SGordon Bergling bus_size_t count_reg;
100e797dc58SGordon Bergling bus_size_t data_reg;
101e797dc58SGordon Bergling bus_size_t intr_mask_reg;
102e797dc58SGordon Bergling uint32_t intr_disable_bit;
103e797dc58SGordon Bergling uint32_t count_value_shift;
104e797dc58SGordon Bergling uint32_t count_value_mask;
105e797dc58SGordon Bergling uint32_t warmup_count;
106e797dc58SGordon Bergling bool allow_2x_mode;
107e797dc58SGordon Bergling bool can_diagnose;
108e797dc58SGordon Bergling /* XXX diag regs */
109e797dc58SGordon Bergling };
110e797dc58SGordon Bergling
111e797dc58SGordon Bergling static const struct bcm_rng_conf bcm2835_rng_conf = {
112e797dc58SGordon Bergling .control_reg = RNG_CTRL,
113e797dc58SGordon Bergling .status_reg = BCM2835_RNG_STATUS,
114e797dc58SGordon Bergling .count_reg = BCM2835_RNG_STATUS, /* Same register */
115e797dc58SGordon Bergling .data_reg = BCM2835_RNG_DATA,
116e797dc58SGordon Bergling .intr_mask_reg = BCM2835_RNG_INT_MASK,
117e797dc58SGordon Bergling .intr_disable_bit = BCM2835_RNG_INT_OFF_BIT,
118e797dc58SGordon Bergling .count_value_shift = BCM2835_RND_VAL_SHIFT,
119e797dc58SGordon Bergling .count_value_mask = BCM2835_RND_VAL_MASK,
120e797dc58SGordon Bergling .warmup_count = BCM2835_RND_VAL_WARM_CNT,
121e797dc58SGordon Bergling .allow_2x_mode = true,
122e797dc58SGordon Bergling .can_diagnose = true
123e797dc58SGordon Bergling };
124e797dc58SGordon Bergling
125e797dc58SGordon Bergling static const struct bcm_rng_conf bcm2838_rng_conf = {
126e797dc58SGordon Bergling .control_reg = RNG_CTRL,
127e797dc58SGordon Bergling .status_reg = BCM2838_RNG_STATUS,
128e797dc58SGordon Bergling .count_reg = BCM2838_RNG_COUNT,
129e797dc58SGordon Bergling .data_reg = BCM2838_RNG_DATA,
130e797dc58SGordon Bergling .intr_mask_reg = 0,
131e797dc58SGordon Bergling .intr_disable_bit = 0,
132e797dc58SGordon Bergling .count_value_shift = 0,
133e797dc58SGordon Bergling .count_value_mask = BCM2838_COUNT_VAL_MASK,
134e797dc58SGordon Bergling .warmup_count = 0,
135e797dc58SGordon Bergling .allow_2x_mode = false,
136e797dc58SGordon Bergling .can_diagnose = false
137e797dc58SGordon Bergling };
138e797dc58SGordon Bergling
139a1acc06fSMark Murray struct bcm2835_rng_softc {
140a1acc06fSMark Murray device_t sc_dev;
141a1acc06fSMark Murray struct resource * sc_mem_res;
142a1acc06fSMark Murray struct resource * sc_irq_res;
143a1acc06fSMark Murray void * sc_intr_hdl;
144e797dc58SGordon Bergling struct bcm_rng_conf const* conf;
145a1acc06fSMark Murray uint32_t sc_buf[RNG_FIFO_WORDS];
146a1acc06fSMark Murray struct callout sc_rngto;
147a1acc06fSMark Murray int sc_stall_count;
148a1acc06fSMark Murray int sc_rbg2x;
149a1acc06fSMark Murray long sc_underrun;
150a1acc06fSMark Murray };
151a1acc06fSMark Murray
152c5765d84SIan Lepore static struct ofw_compat_data compat_data[] = {
153e797dc58SGordon Bergling {"broadcom,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf},
154e797dc58SGordon Bergling {"brcm,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf},
155e797dc58SGordon Bergling
156e797dc58SGordon Bergling {"brcm,bcm2711-rng200", (uintptr_t)&bcm2838_rng_conf},
157e797dc58SGordon Bergling {"brcm,bcm2838-rng", (uintptr_t)&bcm2838_rng_conf},
158e797dc58SGordon Bergling {"brcm,bcm2838-rng200", (uintptr_t)&bcm2838_rng_conf},
159e797dc58SGordon Bergling {"brcm,bcm7211-rng", (uintptr_t)&bcm2838_rng_conf},
160e797dc58SGordon Bergling {"brcm,bcm7278-rng", (uintptr_t)&bcm2838_rng_conf},
161e797dc58SGordon Bergling {"brcm,iproc-rng200", (uintptr_t)&bcm2838_rng_conf},
162c5765d84SIan Lepore {NULL, 0}
163c5765d84SIan Lepore };
164c5765d84SIan Lepore
165a1acc06fSMark Murray static __inline void
bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc * sc)166a1acc06fSMark Murray bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc)
167a1acc06fSMark Murray {
168a1acc06fSMark Murray
169a1acc06fSMark Murray atomic_add_long(&sc->sc_underrun, 1);
170a1acc06fSMark Murray }
171a1acc06fSMark Murray
172a1acc06fSMark Murray static __inline uint32_t
bcm2835_rng_read4(struct bcm2835_rng_softc * sc,bus_size_t off)173a1acc06fSMark Murray bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off)
174a1acc06fSMark Murray {
175a1acc06fSMark Murray
176a1acc06fSMark Murray return bus_read_4(sc->sc_mem_res, off);
177a1acc06fSMark Murray }
178a1acc06fSMark Murray
179a1acc06fSMark Murray static __inline void
bcm2835_rng_read_multi4(struct bcm2835_rng_softc * sc,bus_size_t off,uint32_t * datap,bus_size_t count)180a1acc06fSMark Murray bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off,
181a1acc06fSMark Murray uint32_t *datap, bus_size_t count)
182a1acc06fSMark Murray {
183a1acc06fSMark Murray
184a1acc06fSMark Murray bus_read_multi_4(sc->sc_mem_res, off, datap, count);
185a1acc06fSMark Murray }
186a1acc06fSMark Murray
187a1acc06fSMark Murray static __inline void
bcm2835_rng_write4(struct bcm2835_rng_softc * sc,bus_size_t off,uint32_t val)188a1acc06fSMark Murray bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val)
189a1acc06fSMark Murray {
190a1acc06fSMark Murray
191a1acc06fSMark Murray bus_write_4(sc->sc_mem_res, off, val);
192a1acc06fSMark Murray }
193a1acc06fSMark Murray
194a1acc06fSMark Murray static void
bcm2835_rng_dump_registers(struct bcm2835_rng_softc * sc,struct sbuf * sbp)195a1acc06fSMark Murray bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp)
196a1acc06fSMark Murray {
197a1acc06fSMark Murray uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val;
198a1acc06fSMark Murray int i;
199a1acc06fSMark Murray
200e797dc58SGordon Bergling if (!sc->conf->can_diagnose)
201e797dc58SGordon Bergling /* Not implemented. */
202e797dc58SGordon Bergling return;
203e797dc58SGordon Bergling
204a1acc06fSMark Murray /* Display RNG control register contents */
205e797dc58SGordon Bergling val = bcm2835_rng_read4(sc, sc->conf->control_reg);
206a1acc06fSMark Murray sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);
207a1acc06fSMark Murray
208a1acc06fSMark Murray comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT;
209a1acc06fSMark Murray sbuf_printf(sbp, " RNG_COMBLK2_OSC (%02x)\n", comblk2_osc);
210a1acc06fSMark Murray for (i = 0; i < RNG_NUM_OSCILLATORS; i++)
211a1acc06fSMark Murray if ((comblk2_osc & (1 << i)) == 0)
212a1acc06fSMark Murray sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1);
213a1acc06fSMark Murray
214a1acc06fSMark Murray comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT;
215a1acc06fSMark Murray sbuf_printf(sbp, " RNG_COMBLK1_OSC (%02x)\n", comblk1_osc);
216a1acc06fSMark Murray for (i = 0; i < RNG_NUM_OSCILLATORS; i++)
217a1acc06fSMark Murray if ((comblk1_osc & (1 << i)) == 0)
218a1acc06fSMark Murray sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1);
219a1acc06fSMark Murray
220a1acc06fSMark Murray jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >>
221a1acc06fSMark Murray RNG_JCLK_BYP_DIV_CNT_SHIFT;
222a1acc06fSMark Murray sbuf_printf(sbp,
223a1acc06fSMark Murray " RNG_JCLK_BYP_DIV_CNT (%02x)\n APB clock frequency / %d\n",
224a1acc06fSMark Murray jclk_byp_div, 2 * (jclk_byp_div + 1));
225a1acc06fSMark Murray
226a1acc06fSMark Murray sbuf_printf(sbp, " RNG_JCLK_BYP_SRC:\n %s\n",
227a1acc06fSMark Murray (val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" :
228a1acc06fSMark Murray "Use RNG clock (APB clock)");
229a1acc06fSMark Murray
230a1acc06fSMark Murray sbuf_printf(sbp, " RNG_JCLK_BYP_SEL:\n %s\n",
231a1acc06fSMark Murray (val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" :
232a1acc06fSMark Murray "Use internal jitter clock");
233a1acc06fSMark Murray
234a1acc06fSMark Murray if ((val & RNG_RBG2X) != 0)
235a1acc06fSMark Murray sbuf_cat(sbp, " RNG_RBG2X: RNG 2X SPEED enabled\n");
236a1acc06fSMark Murray
237a1acc06fSMark Murray if ((val & RNG_RBGEN_BIT) != 0)
238a1acc06fSMark Murray sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n");
239a1acc06fSMark Murray
240a1acc06fSMark Murray /* Display RNG status register contents */
241e797dc58SGordon Bergling val = bcm2835_rng_read4(sc, sc->conf->status_reg);
242a1acc06fSMark Murray sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);
243a1acc06fSMark Murray sbuf_printf(sbp, " RND_VAL: %02x\n",
244e797dc58SGordon Bergling (val >> sc->conf->count_value_shift) & sc->conf->count_value_mask);
245e797dc58SGordon Bergling sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & sc->conf->warmup_count);
246a1acc06fSMark Murray
247a1acc06fSMark Murray /* Display FIFO threshold register contents */
248a1acc06fSMark Murray val = bcm2835_rng_read4(sc, RNG_FF_THRES);
249a1acc06fSMark Murray sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK);
250a1acc06fSMark Murray
251a1acc06fSMark Murray /* Display interrupt mask register contents */
252e797dc58SGordon Bergling val = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg);
253a1acc06fSMark Murray sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n",
254e797dc58SGordon Bergling ((val & sc->conf->intr_disable_bit) != 0) ? "disabled" : "enabled");
255a1acc06fSMark Murray }
256a1acc06fSMark Murray
257a1acc06fSMark Murray static void
bcm2835_rng_disable_intr(struct bcm2835_rng_softc * sc)258a1acc06fSMark Murray bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc)
259a1acc06fSMark Murray {
260a1acc06fSMark Murray uint32_t mask;
261a1acc06fSMark Murray
262a1acc06fSMark Murray /* Set the interrupt off bit in the interrupt mask register */
263e797dc58SGordon Bergling mask = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg);
264e797dc58SGordon Bergling mask |= sc->conf->intr_disable_bit;
265e797dc58SGordon Bergling bcm2835_rng_write4(sc, sc->conf->intr_mask_reg, mask);
266a1acc06fSMark Murray }
267a1acc06fSMark Murray
268a1acc06fSMark Murray static void
bcm2835_rng_start(struct bcm2835_rng_softc * sc)269a1acc06fSMark Murray bcm2835_rng_start(struct bcm2835_rng_softc *sc)
270a1acc06fSMark Murray {
271a1acc06fSMark Murray uint32_t ctrl;
272a1acc06fSMark Murray
273a1acc06fSMark Murray /* Disable the interrupt */
274e797dc58SGordon Bergling if (sc->conf->intr_mask_reg)
275a1acc06fSMark Murray bcm2835_rng_disable_intr(sc);
276a1acc06fSMark Murray
277a1acc06fSMark Murray /* Set the warmup count */
278e797dc58SGordon Bergling if (sc->conf->warmup_count > 0)
279e797dc58SGordon Bergling bcm2835_rng_write4(sc, sc->conf->status_reg,
280e797dc58SGordon Bergling sc->conf->warmup_count);
281a1acc06fSMark Murray
282a1acc06fSMark Murray /* Enable the RNG */
283e797dc58SGordon Bergling ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg);
284a1acc06fSMark Murray ctrl |= RNG_RBGEN_BIT;
285e797dc58SGordon Bergling if (sc->sc_rbg2x && sc->conf->allow_2x_mode)
286a1acc06fSMark Murray ctrl |= RNG_RBG2X;
287e797dc58SGordon Bergling bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl);
288a1acc06fSMark Murray }
289a1acc06fSMark Murray
290a1acc06fSMark Murray static void
bcm2835_rng_stop(struct bcm2835_rng_softc * sc)291a1acc06fSMark Murray bcm2835_rng_stop(struct bcm2835_rng_softc *sc)
292a1acc06fSMark Murray {
293a1acc06fSMark Murray uint32_t ctrl;
294a1acc06fSMark Murray
295a1acc06fSMark Murray /* Disable the RNG */
296e797dc58SGordon Bergling ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg);
297a1acc06fSMark Murray ctrl &= ~RNG_RBGEN_BIT;
298e797dc58SGordon Bergling bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl);
299e797dc58SGordon Bergling }
300e797dc58SGordon Bergling
301e797dc58SGordon Bergling static void
bcm2835_rng_enqueue_harvest(struct bcm2835_rng_softc * sc,uint32_t nread)302e797dc58SGordon Bergling bcm2835_rng_enqueue_harvest(struct bcm2835_rng_softc *sc, uint32_t nread)
303e797dc58SGordon Bergling {
304e797dc58SGordon Bergling char *sc_buf_chunk;
305e797dc58SGordon Bergling uint32_t chunk_size;
306e797dc58SGordon Bergling uint32_t cnt;
307e797dc58SGordon Bergling
308e797dc58SGordon Bergling chunk_size = sizeof(((struct harvest_event *)0)->he_entropy);
309e797dc58SGordon Bergling cnt = nread * sizeof(uint32_t);
310e797dc58SGordon Bergling sc_buf_chunk = (void*)sc->sc_buf;
311e797dc58SGordon Bergling
312e797dc58SGordon Bergling while (cnt > 0) {
313e797dc58SGordon Bergling uint32_t size;
314e797dc58SGordon Bergling
315e797dc58SGordon Bergling size = MIN(cnt, chunk_size);
316e797dc58SGordon Bergling
317e797dc58SGordon Bergling random_harvest_queue(sc_buf_chunk, size, RANDOM_PURE_BROADCOM);
318e797dc58SGordon Bergling
319e797dc58SGordon Bergling sc_buf_chunk += size;
320e797dc58SGordon Bergling cnt -= size;
321e797dc58SGordon Bergling }
322a1acc06fSMark Murray }
323a1acc06fSMark Murray
324a1acc06fSMark Murray static void
bcm2835_rng_harvest(void * arg)3258e6a67d5SOleksandr Tymoshenko bcm2835_rng_harvest(void *arg)
326a1acc06fSMark Murray {
327a1acc06fSMark Murray uint32_t *dest;
328e797dc58SGordon Bergling uint32_t hwcount;
329a1acc06fSMark Murray u_int cnt, nread, num_avail, num_words;
330a1acc06fSMark Murray int seen_underrun, num_stalls;
3318e6a67d5SOleksandr Tymoshenko struct bcm2835_rng_softc *sc = arg;
332a1acc06fSMark Murray
333a1acc06fSMark Murray dest = sc->sc_buf;
334a1acc06fSMark Murray nread = num_words = 0;
335a1acc06fSMark Murray seen_underrun = num_stalls = 0;
336e797dc58SGordon Bergling
337a1acc06fSMark Murray for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0;
338a1acc06fSMark Murray cnt -= num_words) {
339e797dc58SGordon Bergling /* Read count register to find out how many words available */
340e797dc58SGordon Bergling hwcount = bcm2835_rng_read4(sc, sc->conf->count_reg);
341e797dc58SGordon Bergling num_avail = (hwcount >> sc->conf->count_value_shift) &
342e797dc58SGordon Bergling sc->conf->count_value_mask;
343a1acc06fSMark Murray
344a1acc06fSMark Murray /* If we have none... */
345a1acc06fSMark Murray if (num_avail == 0) {
346a1acc06fSMark Murray bcm2835_rng_stat_inc_underrun(sc);
347a1acc06fSMark Murray if (++seen_underrun >= sc->sc_stall_count) {
348a1acc06fSMark Murray if (num_stalls++ > 0) {
349a1acc06fSMark Murray device_printf(sc->sc_dev,
350a1acc06fSMark Murray "RNG stalled, disabling device\n");
351a1acc06fSMark Murray bcm2835_rng_stop(sc);
352a1acc06fSMark Murray break;
353a1acc06fSMark Murray } else {
354a1acc06fSMark Murray device_printf(sc->sc_dev,
355a1acc06fSMark Murray "Too many underruns, resetting\n");
356a1acc06fSMark Murray bcm2835_rng_stop(sc);
357a1acc06fSMark Murray bcm2835_rng_start(sc);
358a1acc06fSMark Murray seen_underrun = 0;
359a1acc06fSMark Murray }
360a1acc06fSMark Murray }
361a1acc06fSMark Murray /* Try again */
362a1acc06fSMark Murray continue;
363a1acc06fSMark Murray }
364a1acc06fSMark Murray
365a1acc06fSMark Murray CTR2(KTR_DEV, "%s: %d words available in RNG FIFO",
366a1acc06fSMark Murray device_get_nameunit(sc->sc_dev), num_avail);
367a1acc06fSMark Murray
368a1acc06fSMark Murray /* Pull MIN(num_avail, cnt) words from the FIFO */
369a1acc06fSMark Murray num_words = (num_avail > cnt) ? cnt : num_avail;
370e797dc58SGordon Bergling bcm2835_rng_read_multi4(sc, sc->conf->data_reg, dest,
371a1acc06fSMark Murray num_words);
372a1acc06fSMark Murray dest += num_words;
373a1acc06fSMark Murray nread += num_words;
374a1acc06fSMark Murray }
375a1acc06fSMark Murray
376e797dc58SGordon Bergling bcm2835_rng_enqueue_harvest(sc, nread);
377a1acc06fSMark Murray
3788e6a67d5SOleksandr Tymoshenko callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, bcm2835_rng_harvest, sc);
379a1acc06fSMark Murray }
380a1acc06fSMark Murray
381a1acc06fSMark Murray static int
sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS)382a1acc06fSMark Murray sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS)
383a1acc06fSMark Murray {
384a1acc06fSMark Murray struct bcm2835_rng_softc *sc = arg1;
385a1acc06fSMark Murray int error, rbg2x;
386a1acc06fSMark Murray
387a1acc06fSMark Murray rbg2x = sc->sc_rbg2x;
388a1acc06fSMark Murray error = sysctl_handle_int(oidp, &rbg2x, 0, req);
389a1acc06fSMark Murray if (error)
390a1acc06fSMark Murray return (error);
391a1acc06fSMark Murray if (req->newptr == NULL)
392a1acc06fSMark Murray return (error);
393a1acc06fSMark Murray if (rbg2x == sc->sc_rbg2x)
394a1acc06fSMark Murray return (0);
395a1acc06fSMark Murray
396a1acc06fSMark Murray /* Reset the RNG */
397a1acc06fSMark Murray bcm2835_rng_stop(sc);
398a1acc06fSMark Murray sc->sc_rbg2x = rbg2x;
399a1acc06fSMark Murray bcm2835_rng_start(sc);
400a1acc06fSMark Murray
401a1acc06fSMark Murray return (0);
402a1acc06fSMark Murray }
403a1acc06fSMark Murray
404a1acc06fSMark Murray #ifdef BCM2835_RNG_DEBUG_REGISTERS
405a1acc06fSMark Murray static int
sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS)406a1acc06fSMark Murray sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS)
407a1acc06fSMark Murray {
408a1acc06fSMark Murray struct sbuf sb;
409a1acc06fSMark Murray struct bcm2835_rng_softc *sc = arg1;
410a1acc06fSMark Murray int error;
411a1acc06fSMark Murray
412a1acc06fSMark Murray error = sysctl_wire_old_buffer(req, 0);
413a1acc06fSMark Murray if (error != 0)
414a1acc06fSMark Murray return (error);
415a1acc06fSMark Murray sbuf_new_for_sysctl(&sb, NULL, 128, req);
416a1acc06fSMark Murray bcm2835_rng_dump_registers(sc, &sb);
417a1acc06fSMark Murray error = sbuf_finish(&sb);
418a1acc06fSMark Murray sbuf_delete(&sb);
419a1acc06fSMark Murray return (error);
420a1acc06fSMark Murray }
421a1acc06fSMark Murray #endif
422a1acc06fSMark Murray
423a1acc06fSMark Murray static int
bcm2835_rng_probe(device_t dev)424a1acc06fSMark Murray bcm2835_rng_probe(device_t dev)
425a1acc06fSMark Murray {
426a1acc06fSMark Murray
427a1acc06fSMark Murray if (!ofw_bus_status_okay(dev))
428a1acc06fSMark Murray return (ENXIO);
429a1acc06fSMark Murray
430c5765d84SIan Lepore if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
431a1acc06fSMark Murray return (ENXIO);
432a1acc06fSMark Murray
433e797dc58SGordon Bergling device_set_desc(dev, "Broadcom BCM2835/BCM2838 RNG");
434a1acc06fSMark Murray
435a1acc06fSMark Murray return (BUS_PROBE_DEFAULT);
436a1acc06fSMark Murray }
437a1acc06fSMark Murray
438a1acc06fSMark Murray static int
bcm2835_rng_attach(device_t dev)439a1acc06fSMark Murray bcm2835_rng_attach(device_t dev)
440a1acc06fSMark Murray {
441a1acc06fSMark Murray struct bcm2835_rng_softc *sc;
442a1acc06fSMark Murray struct sysctl_ctx_list *sysctl_ctx;
443a1acc06fSMark Murray struct sysctl_oid *sysctl_tree;
444a1acc06fSMark Murray int error, rid;
445a1acc06fSMark Murray
446a1acc06fSMark Murray error = 0;
447a1acc06fSMark Murray sc = device_get_softc(dev);
448a1acc06fSMark Murray sc->sc_dev = dev;
449e797dc58SGordon Bergling
450e797dc58SGordon Bergling sc->conf = (void const*)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
451e797dc58SGordon Bergling KASSERT(sc->conf != NULL, ("bcm2835_rng_attach: sc->conf == NULL"));
452e797dc58SGordon Bergling
453a1acc06fSMark Murray sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT;
4548e6a67d5SOleksandr Tymoshenko
455a1acc06fSMark Murray /* Initialize callout */
456a1acc06fSMark Murray callout_init(&sc->sc_rngto, CALLOUT_MPSAFE);
4578e6a67d5SOleksandr Tymoshenko
458a1acc06fSMark Murray TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count);
459e797dc58SGordon Bergling if (sc->conf->allow_2x_mode)
460e797dc58SGordon Bergling TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x);
461a1acc06fSMark Murray
462a1acc06fSMark Murray /* Allocate memory resources */
463a1acc06fSMark Murray rid = 0;
464a1acc06fSMark Murray sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
465a1acc06fSMark Murray RF_ACTIVE);
466a1acc06fSMark Murray if (sc->sc_mem_res == NULL) {
467a1acc06fSMark Murray bcm2835_rng_detach(dev);
468a1acc06fSMark Murray return (ENXIO);
469a1acc06fSMark Murray }
470a1acc06fSMark Murray
471a1acc06fSMark Murray /* Start the RNG */
472a1acc06fSMark Murray bcm2835_rng_start(sc);
473a1acc06fSMark Murray
474a1acc06fSMark Murray /* Dump the registers if booting verbose */
475a1acc06fSMark Murray if (bootverbose) {
476a1acc06fSMark Murray struct sbuf sb;
477a1acc06fSMark Murray
478a1acc06fSMark Murray (void) sbuf_new(&sb, NULL, 256,
479a1acc06fSMark Murray SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
480a1acc06fSMark Murray bcm2835_rng_dump_registers(sc, &sb);
481a1acc06fSMark Murray sbuf_trim(&sb);
482a1acc06fSMark Murray error = sbuf_finish(&sb);
483a1acc06fSMark Murray if (error == 0)
484a1acc06fSMark Murray device_printf(dev, "%s", sbuf_data(&sb));
485a1acc06fSMark Murray sbuf_delete(&sb);
486a1acc06fSMark Murray }
487a1acc06fSMark Murray
488a1acc06fSMark Murray sysctl_ctx = device_get_sysctl_ctx(dev);
489a1acc06fSMark Murray sysctl_tree = device_get_sysctl_tree(dev);
490a1acc06fSMark Murray SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
491a1acc06fSMark Murray "underrun", CTLFLAG_RD, &sc->sc_underrun,
492a1acc06fSMark Murray "Number of FIFO underruns");
493e797dc58SGordon Bergling if (sc->conf->allow_2x_mode)
494a1acc06fSMark Murray SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
4957029da5cSPawel Biernacki "2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
496a1acc06fSMark Murray sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED");
497a1acc06fSMark Murray SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
498a1acc06fSMark Murray "stall_count", CTLFLAG_RW, &sc->sc_stall_count,
499a1acc06fSMark Murray RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall");
500a1acc06fSMark Murray #ifdef BCM2835_RNG_DEBUG_REGISTERS
501a1acc06fSMark Murray SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
5027029da5cSPawel Biernacki "dumpregs", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
503a1acc06fSMark Murray sysctl_bcm2835_rng_dump, "S", "Dump RNG registers");
504a1acc06fSMark Murray #endif
505a1acc06fSMark Murray
5068e6a67d5SOleksandr Tymoshenko /*
5078e6a67d5SOleksandr Tymoshenko * Schedule the initial harvesting one second from now, which should give the
5088e6a67d5SOleksandr Tymoshenko * hardware RNG plenty of time to generate the first random bytes.
5098e6a67d5SOleksandr Tymoshenko */
5108e6a67d5SOleksandr Tymoshenko callout_reset(&sc->sc_rngto, hz, bcm2835_rng_harvest, sc);
511a1acc06fSMark Murray
512a1acc06fSMark Murray return (0);
513a1acc06fSMark Murray }
514a1acc06fSMark Murray
515a1acc06fSMark Murray static int
bcm2835_rng_detach(device_t dev)516a1acc06fSMark Murray bcm2835_rng_detach(device_t dev)
517a1acc06fSMark Murray {
518a1acc06fSMark Murray struct bcm2835_rng_softc *sc;
519a1acc06fSMark Murray
520a1acc06fSMark Murray sc = device_get_softc(dev);
521a1acc06fSMark Murray
522a1acc06fSMark Murray /* Stop the RNG */
523a1acc06fSMark Murray bcm2835_rng_stop(sc);
524a1acc06fSMark Murray
525a1acc06fSMark Murray /* Drain the callout it */
526a1acc06fSMark Murray callout_drain(&sc->sc_rngto);
527a1acc06fSMark Murray
528a1acc06fSMark Murray /* Release memory resource */
529a1acc06fSMark Murray if (sc->sc_mem_res != NULL)
530a1acc06fSMark Murray bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
531a1acc06fSMark Murray
532a1acc06fSMark Murray return (0);
533a1acc06fSMark Murray }
534a1acc06fSMark Murray
535a1acc06fSMark Murray static device_method_t bcm2835_rng_methods[] = {
536a1acc06fSMark Murray /* Device interface */
537a1acc06fSMark Murray DEVMETHOD(device_probe, bcm2835_rng_probe),
538a1acc06fSMark Murray DEVMETHOD(device_attach, bcm2835_rng_attach),
539a1acc06fSMark Murray DEVMETHOD(device_detach, bcm2835_rng_detach),
540a1acc06fSMark Murray
541a1acc06fSMark Murray DEVMETHOD_END
542a1acc06fSMark Murray };
543a1acc06fSMark Murray
544a1acc06fSMark Murray static driver_t bcm2835_rng_driver = {
545a1acc06fSMark Murray "bcmrng",
546a1acc06fSMark Murray bcm2835_rng_methods,
547a1acc06fSMark Murray sizeof(struct bcm2835_rng_softc)
548a1acc06fSMark Murray };
549a1acc06fSMark Murray
550*82d4dc06SJohn Baldwin DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver, 0, 0);
551*82d4dc06SJohn Baldwin DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, 0, 0);
552a1acc06fSMark Murray MODULE_VERSION(bcm2835_rng, 1);
553a1acc06fSMark Murray MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1);
554