bcm2835_rng.c revision 331889
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 331889 2018-04-02 21:48:29Z 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 __inline void
113bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc)
114{
115
116	atomic_add_long(&sc->sc_underrun, 1);
117}
118
119static __inline uint32_t
120bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off)
121{
122
123	return bus_read_4(sc->sc_mem_res, off);
124}
125
126static __inline void
127bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off,
128    uint32_t *datap, bus_size_t count)
129{
130
131	bus_read_multi_4(sc->sc_mem_res, off, datap, count);
132}
133
134static __inline void
135bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val)
136{
137
138	bus_write_4(sc->sc_mem_res, off, val);
139}
140
141static void
142bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp)
143{
144	uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val;
145	int i;
146
147	/* Display RNG control register contents */
148	val = bcm2835_rng_read4(sc, RNG_CTRL);
149	sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);
150
151	comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT;
152	sbuf_printf(sbp, "  RNG_COMBLK2_OSC (%02x)\n", comblk2_osc);
153	for (i = 0; i < RNG_NUM_OSCILLATORS; i++)
154		if ((comblk2_osc & (1 << i)) == 0)
155			sbuf_printf(sbp, "    Oscillator %d enabled\n", i + 1);
156
157	comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT;
158	sbuf_printf(sbp, "  RNG_COMBLK1_OSC (%02x)\n", comblk1_osc);
159	for (i = 0; i < RNG_NUM_OSCILLATORS; i++)
160		if ((comblk1_osc & (1 << i)) == 0)
161			sbuf_printf(sbp, "    Oscillator %d enabled\n", i + 1);
162
163	jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >>
164	    RNG_JCLK_BYP_DIV_CNT_SHIFT;
165	sbuf_printf(sbp,
166	    "  RNG_JCLK_BYP_DIV_CNT (%02x)\n    APB clock frequency / %d\n",
167	    jclk_byp_div, 2 * (jclk_byp_div + 1));
168
169	sbuf_printf(sbp, "  RNG_JCLK_BYP_SRC:\n    %s\n",
170	    (val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" :
171	    "Use RNG clock (APB clock)");
172
173	sbuf_printf(sbp, "  RNG_JCLK_BYP_SEL:\n    %s\n",
174	    (val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" :
175	    "Use internal jitter clock");
176
177	if ((val & RNG_RBG2X) != 0)
178		sbuf_cat(sbp, "  RNG_RBG2X: RNG 2X SPEED enabled\n");
179
180	if ((val & RNG_RBGEN_BIT) != 0)
181		sbuf_cat(sbp, "  RNG_RBGEN_BIT: RBG enabled\n");
182
183	/* Display RNG status register contents */
184	val = bcm2835_rng_read4(sc, RNG_STATUS);
185	sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);
186	sbuf_printf(sbp, "  RND_VAL: %02x\n",
187	    (val >> RND_VAL_SHIFT) & RND_VAL_MASK);
188	sbuf_printf(sbp, "  RND_WARM_CNT: %05x\n", val & RND_WARM_CNT);
189
190	/* Display FIFO threshold register contents */
191	val = bcm2835_rng_read4(sc, RNG_FF_THRES);
192	sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK);
193
194	/* Display interrupt mask register contents */
195	val = bcm2835_rng_read4(sc, RNG_INT_MASK);
196	sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n",
197	     ((val & RNG_INT_OFF_BIT) != 0) ? "disabled" : "enabled");
198}
199
200static void
201bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc)
202{
203	uint32_t mask;
204
205	/* Set the interrupt off bit in the interrupt mask register */
206	mask = bcm2835_rng_read4(sc, RNG_INT_MASK);
207	mask |= RNG_INT_OFF_BIT;
208        bcm2835_rng_write4(sc, RNG_INT_MASK, mask);
209}
210
211#if defined(BCM2835_RNG_USE_INTERRUPT)
212static void
213bcm2835_rng_enable_intr(struct bcm2835_rng_softc *sc)
214{
215	uint32_t mask;
216
217	/* Clear the interrupt off bit in the interrupt mask register */
218	mask = bcm2835_rng_read4(sc, RNG_INT_MASK);
219	mask &= ~RNG_INT_OFF_BIT;
220        bcm2835_rng_write4(sc, RNG_INT_MASK, mask);
221}
222#endif
223
224static void
225bcm2835_rng_start(struct bcm2835_rng_softc *sc)
226{
227	uint32_t ctrl;
228
229	/* Disable the interrupt */
230	bcm2835_rng_disable_intr(sc);
231
232	/* Set the warmup count */
233	bcm2835_rng_write4(sc, RNG_STATUS, RND_VAL_WARM_CNT);
234
235	/* Enable the RNG */
236	ctrl = bcm2835_rng_read4(sc, RNG_CTRL);
237	ctrl |= RNG_RBGEN_BIT;
238	if (sc->sc_rbg2x)
239		ctrl |= RNG_RBG2X;
240	bcm2835_rng_write4(sc, RNG_CTRL, ctrl);
241
242#if defined(BCM2835_RNG_USE_INTERRUPT)
243	/* Enable the interrupt */
244	bcm2835_rng_enable_intr(sc);
245#endif
246}
247
248static void
249bcm2835_rng_stop(struct bcm2835_rng_softc *sc)
250{
251	uint32_t ctrl;
252
253	/* Disable the RNG */
254	ctrl = bcm2835_rng_read4(sc, RNG_CTRL);
255	ctrl &= ~RNG_RBGEN_BIT;
256	bcm2835_rng_write4(sc, RNG_CTRL, ctrl);
257}
258
259static void
260bcm2835_rng_harvest(struct bcm2835_rng_softc *sc)
261{
262	uint32_t *dest;
263	uint32_t status;
264	u_int cnt, nread, num_avail, num_words;
265	int seen_underrun, num_stalls;
266
267	dest = sc->sc_buf;
268	nread = num_words = 0;
269	seen_underrun = num_stalls = 0;
270	for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0;
271	    cnt -= num_words) {
272		/* Read status register to find out how many words available */
273		status = bcm2835_rng_read4(sc, RNG_STATUS);
274		num_avail = (status >> RND_VAL_SHIFT) & RND_VAL_MASK;
275
276		/* If we have none... */
277		if (num_avail == 0) {
278			bcm2835_rng_stat_inc_underrun(sc);
279			if (++seen_underrun >= sc->sc_stall_count) {
280				if (num_stalls++ > 0) {
281					device_printf(sc->sc_dev,
282					    "RNG stalled, disabling device\n");
283					bcm2835_rng_stop(sc);
284					break;
285				} else {
286					device_printf(sc->sc_dev,
287					    "Too many underruns, resetting\n");
288					bcm2835_rng_stop(sc);
289					bcm2835_rng_start(sc);
290					seen_underrun = 0;
291				}
292			}
293			/* Try again */
294			continue;
295		}
296
297		CTR2(KTR_DEV, "%s: %d words available in RNG FIFO",
298		    device_get_nameunit(sc->sc_dev), num_avail);
299
300		/* Pull MIN(num_avail, cnt) words from the FIFO */
301		num_words = (num_avail > cnt) ? cnt : num_avail;
302		bcm2835_rng_read_multi4(sc, RNG_DATA, dest,
303		    num_words);
304		dest += num_words;
305		nread += num_words;
306	}
307
308	cnt = nread * sizeof(uint32_t);
309	if (cnt > 0)
310		random_harvest_queue(sc->sc_buf, cnt, cnt * NBBY / 2,
311		    RANDOM_PURE_BROADCOM);
312
313#if defined(BCM2835_RNG_USE_CALLOUT)
314	callout_reset(&sc->sc_rngto, sc->sc_rnghz, bcm2835_rng_harvest, sc);
315#endif
316}
317
318static int
319sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS)
320{
321	struct bcm2835_rng_softc *sc = arg1;
322	int error, rbg2x;
323
324	rbg2x = sc->sc_rbg2x;
325	error = sysctl_handle_int(oidp, &rbg2x, 0, req);
326	if (error)
327		return (error);
328	if (req->newptr == NULL)
329		return (error);
330	if (rbg2x == sc->sc_rbg2x)
331		return (0);
332
333	/* Reset the RNG */
334	bcm2835_rng_stop(sc);
335	sc->sc_rbg2x = rbg2x;
336	bcm2835_rng_start(sc);
337
338	return (0);
339}
340
341#ifdef BCM2835_RNG_DEBUG_REGISTERS
342static int
343sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS)
344{
345	struct sbuf sb;
346	struct bcm2835_rng_softc *sc = arg1;
347	int error;
348
349	error = sysctl_wire_old_buffer(req, 0);
350	if (error != 0)
351		return (error);
352	sbuf_new_for_sysctl(&sb, NULL, 128, req);
353	bcm2835_rng_dump_registers(sc, &sb);
354        error = sbuf_finish(&sb);
355        sbuf_delete(&sb);
356        return (error);
357}
358#endif
359
360static int
361bcm2835_rng_probe(device_t dev)
362{
363
364	if (!ofw_bus_status_okay(dev))
365		return (ENXIO);
366
367	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-rng"))
368		return (ENXIO);
369
370	device_set_desc(dev, "Broadcom BCM2835 RNG");
371
372	return (BUS_PROBE_DEFAULT);
373}
374
375static int
376bcm2835_rng_attach(device_t dev)
377{
378	struct bcm2835_rng_softc *sc;
379	struct sysctl_ctx_list *sysctl_ctx;
380	struct sysctl_oid *sysctl_tree;
381	int error, rid;
382
383	error = 0;
384	sc = device_get_softc(dev);
385	sc->sc_dev = dev;
386	sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT;
387#ifdef BCM2835_RNG_USE_CALLOUT
388	/* Initialize callout */
389	callout_init(&sc->sc_rngto, CALLOUT_MPSAFE);
390#endif
391	TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x);
392	TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count);
393
394	/* Allocate memory resources */
395	rid = 0;
396	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
397	    RF_ACTIVE);
398	if (sc->sc_mem_res == NULL) {
399		bcm2835_rng_detach(dev);
400		return (ENXIO);
401	}
402
403#if defined(BCM2835_RNG_USE_INTERRUPT)
404	/* Allocate interrupt resource */
405	rid = 0;
406	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
407	    RF_SHAREABLE | RF_ACTIVE);
408	if (sc->sc_irq_res == NULL) {
409		bcm2835_rng_detach(dev);
410		return (ENXIO);
411	}
412
413	/* Set up the interrupt handler */
414	error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
415	    NULL, (driver_intr_t *)bcm2835_rng_harvest, sc, &sc->sc_intr_hdl);
416	if (error) {
417		device_printf(dev, "Failed to set up IRQ\n");
418		sc->sc_intr_hdl = NULL;
419		bcm2835_rng_detach(dev);
420		return (error);
421	}
422#endif
423
424	/* Start the RNG */
425	bcm2835_rng_start(sc);
426
427	/* Dump the registers if booting verbose */
428	if (bootverbose) {
429		struct sbuf sb;
430
431		(void) sbuf_new(&sb, NULL, 256,
432		    SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
433		bcm2835_rng_dump_registers(sc, &sb);
434		sbuf_trim(&sb);
435		error = sbuf_finish(&sb);
436		if (error == 0)
437			device_printf(dev, "%s", sbuf_data(&sb));
438		sbuf_delete(&sb);
439	}
440
441	sysctl_ctx = device_get_sysctl_ctx(dev);
442	sysctl_tree = device_get_sysctl_tree(dev);
443	SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
444	    "underrun", CTLFLAG_RD, &sc->sc_underrun,
445	    "Number of FIFO underruns");
446	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
447	    "2xspeed", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
448	    sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED");
449	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
450	    "stall_count", CTLFLAG_RW, &sc->sc_stall_count,
451	    RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall");
452#ifdef BCM2835_RNG_DEBUG_REGISTERS
453	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
454	    "dumpregs", CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
455	    sysctl_bcm2835_rng_dump, "S", "Dump RNG registers");
456#endif
457
458#if defined(BCM2835_RNG_USE_CALLOUT)
459	/* Reset callout */
460	if (hz >= 100)
461		sc->sc_rnghz = hz / 100;
462	else
463		sc->sc_rnghz = 1;
464	callout_reset(&sc->sc_rngto, sc->sc_rnghz, bcm2835_rng_harvest, sc);
465#endif
466
467	return (0);
468}
469
470static int
471bcm2835_rng_detach(device_t dev)
472{
473	struct bcm2835_rng_softc *sc;
474#if defined(BCM2835_RNG_USE_INTERRUPT)
475	int error;
476#endif
477
478	sc = device_get_softc(dev);
479
480	/* Stop the RNG */
481	bcm2835_rng_stop(sc);
482
483	/* Drain the callout it */
484#if defined(BCM2835_RNG_USE_CALLOUT)
485	callout_drain(&sc->sc_rngto);
486#endif
487
488#if defined(BCM2835_RNG_USE_INTERRUPT)
489	/* Tear down the interrupt */
490	if (sc->sc_irq_res && sc->sc_intr_hdl) {
491		error = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
492		if (error != 0) {
493			device_printf(dev, "could not tear down IRQ\n");
494			return (error);
495		}
496		sc->sc_intr_hdl = NULL;
497	}
498
499	/* Release interrupt resource */
500	if (sc->sc_irq_res) {
501		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
502		sc->sc_irq_res = NULL;
503	}
504#endif
505
506	/* Release memory resource */
507	if (sc->sc_mem_res != NULL)
508		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
509
510	return (0);
511}
512
513static device_method_t bcm2835_rng_methods[] = {
514	/* Device interface */
515	DEVMETHOD(device_probe,		bcm2835_rng_probe),
516	DEVMETHOD(device_attach,	bcm2835_rng_attach),
517	DEVMETHOD(device_detach,	bcm2835_rng_detach),
518
519	DEVMETHOD_END
520};
521
522static driver_t bcm2835_rng_driver = {
523	"bcmrng",
524	bcm2835_rng_methods,
525	sizeof(struct bcm2835_rng_softc)
526};
527static devclass_t bcm2835_rng_devclass;
528
529DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver,
530    bcm2835_rng_devclass, 0, 0);
531DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, bcm2835_rng_devclass, 0,
532    0);
533MODULE_VERSION(bcm2835_rng, 1);
534MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1);
535