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