Changeset View
Standalone View
sys/arm/broadcom/bcm2835/bcm2835_rng.c
- This file was added.
/* | |||||
* Copyright (c) 2015, 2016, Stephen J. Kiernan | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | |||||
#include <sys/random.h> | |||||
#include <sys/sbuf.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/selinfo.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/rman.h> | |||||
#include <machine/bus.h> | |||||
#include <machine/resource.h> | |||||
#include <dev/ofw/openfirm.h> | |||||
#include <dev/ofw/ofw_bus.h> | |||||
#include <dev/ofw/ofw_bus_subr.h> | |||||
#include <dev/random/randomdev.h> | |||||
#include <dev/random/random_harvestq.h> | |||||
#if !defined(BCM2835_RNG_USE_CALLOUT) | |||||
#define BCM2835_RNG_USE_INTERRUPT | |||||
markm: This mode looks like it may be a liability? Should the callout/interrupt-using method not be… | |||||
Not Done Inline ActionsYes, this bled in from my testing. stevek: Yes, this bled in from my testing. | |||||
#endif | |||||
static device_attach_t bcm2835_rng_attach; | |||||
static device_detach_t bcm2835_rng_detach; | |||||
static device_probe_t bcm2835_rng_probe; | |||||
#define RNG_CTRL 0x00 /* RNG Control Register */ | |||||
#define RNG_COMBLK1_OSC 0x003f0000 /* Combiner Blk 1 Oscillator */ | |||||
#define RNG_COMBLK1_OSC_SHIFT 16 | |||||
#define RNG_COMBLK2_OSC 0x0fc00000 /* Combiner Blk 2 Oscillator */ | |||||
#define RNG_COMBLK2_OSC_SHIFT 22 | |||||
#define RNG_JCLK_BYP_DIV_CNT 0x0000ff00 /* Jitter clk bypass divider | |||||
count */ | |||||
#define RNG_JCLK_BYP_DIV_CNT_SHIFT 8 | |||||
#define RNG_JCLK_BYP_SRC 0x00000020 /* Jitter clk bypass source */ | |||||
#define RNG_JCLK_BYP_SEL 0x00000010 /* Jitter clk bypass select */ | |||||
#define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */ | |||||
#define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */ | |||||
Not Done Inline ActionsThere is a mix of tabs and spaces in these defines giving a ragged indentation. Please check style(9) and use only one form for uniformity. markm: There is a mix of tabs and spaces in these defines giving a ragged indentation. Please check… | |||||
#define RNG_STATUS 0x04 /* RNG status register */ | |||||
#define RND_VAL_SHIFT 24 /* Shift for valid words */ | |||||
#define RND_VAL_MASK 0x000000ff /* Number valid words mask */ | |||||
#define RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */ | |||||
#define RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */ | |||||
#define RNG_DATA 0x08 /* RNG Data Register */ | |||||
#define RNG_FF_THRES 0x0c | |||||
#define RNG_FF_THRES_MASK 0x0000001f | |||||
#define RNG_INT_MASK 0x10 | |||||
#define RNG_INT_OFF_BIT 0x00000001 | |||||
#define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */ | |||||
#define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t)) | |||||
#define RNG_NUM_OSCILLATORS 6 | |||||
#define RNG_STALL_COUNT_DEFAULT 10 | |||||
struct bcm2835_rng_softc { | |||||
device_t sc_dev; | |||||
struct resource * sc_mem_res; | |||||
struct resource * sc_irq_res; | |||||
void * sc_intr_hdl; | |||||
#if defined(BCM2835_RNG_USE_CALLOUT) || defined(BCM2835_RNG_USE_INTERRUPT) | |||||
uint32_t sc_buf[RNG_FIFO_WORDS]; | |||||
#endif | |||||
#if defined(BCM2835_RNG_USE_CALLOUT) | |||||
struct callout sc_rngto; | |||||
int sc_rnghz; | |||||
#endif | |||||
int sc_stall_count; | |||||
int sc_rbg2x; | |||||
long sc_underrun; | |||||
}; | |||||
static __inline void | |||||
bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc) | |||||
{ | |||||
Not Done Inline ActionsThis define looks a little spare here. markm: This define looks a little spare here. | |||||
atomic_add_long(&sc->sc_underrun, 1); | |||||
} | |||||
static __inline uint32_t | |||||
bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off) | |||||
{ | |||||
return bus_read_4(sc->sc_mem_res, off); | |||||
} | |||||
static __inline void | |||||
bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off, | |||||
uint32_t *datap, bus_size_t count) | |||||
Not Done Inline ActionsAn inline function gives slightly better type-safety and better readability, not to mention consistency with the functions below. markm: An inline function gives slightly better type-safety and better readability, not to mention… | |||||
{ | |||||
bus_read_multi_4(sc->sc_mem_res, off, datap, count); | |||||
} | |||||
static __inline void | |||||
bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val) | |||||
{ | |||||
bus_write_4(sc->sc_mem_res, off, val); | |||||
} | |||||
static void | |||||
bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp) | |||||
{ | |||||
uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val; | |||||
int i; | |||||
/* Display RNG control register contents */ | |||||
val = bcm2835_rng_read4(sc, RNG_CTRL); | |||||
sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); | |||||
comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT; | |||||
sbuf_printf(sbp, " RNG_COMBLK2_OSC (%02x)\n", comblk2_osc); | |||||
for (i = 0; i < RNG_NUM_OSCILLATORS; i++) | |||||
if ((comblk2_osc & (1 << i)) == 0) | |||||
sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1); | |||||
comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT; | |||||
sbuf_printf(sbp, " RNG_COMBLK1_OSC (%02x)\n", comblk1_osc); | |||||
for (i = 0; i < RNG_NUM_OSCILLATORS; i++) | |||||
if ((comblk1_osc & (1 << i)) == 0) | |||||
sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1); | |||||
jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >> | |||||
RNG_JCLK_BYP_DIV_CNT_SHIFT; | |||||
sbuf_printf(sbp, | |||||
" RNG_JCLK_BYP_DIV_CNT (%02x)\n APB clock frequency / %d\n", | |||||
Not Done Inline ActionsIs this right? The general pattern suggests the constant should be 0x1. Also, can the whole block not be done with some arithmetic, rather than having a bunch of nearly identical constant strings? markm: Is this right? The general pattern suggests the constant should be 0x1.
Also, can the whole… | |||||
jclk_byp_div, 2 * (jclk_byp_div + 1)); | |||||
sbuf_printf(sbp, " RNG_JCLK_BYP_SRC:\n %s\n", | |||||
(val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" : | |||||
"Use RNG clock (APB clock)"); | |||||
sbuf_printf(sbp, " RNG_JCLK_BYP_SEL:\n %s\n", | |||||
(val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" : | |||||
"Use internal jitter clock"); | |||||
if ((val & RNG_RBG2X) != 0) | |||||
sbuf_cat(sbp, " RNG_RBG2X: RNG 2X SPEED enabled\n"); | |||||
if ((val & RNG_RBGEN_BIT) != 0) | |||||
sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n"); | |||||
/* Display RNG status register contents */ | |||||
val = bcm2835_rng_read4(sc, RNG_STATUS); | |||||
sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); | |||||
sbuf_printf(sbp, " RND_VAL: %02x\n", | |||||
(val >> RND_VAL_SHIFT) & RND_VAL_MASK); | |||||
sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & RND_WARM_CNT); | |||||
/* Display FIFO threshold register contents */ | |||||
val = bcm2835_rng_read4(sc, RNG_FF_THRES); | |||||
sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK); | |||||
/* Display interrupt mask register contents */ | |||||
val = bcm2835_rng_read4(sc, RNG_INT_MASK); | |||||
sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n", | |||||
((val & RNG_INT_OFF_BIT) != 0) ? "disabled" : "enabled"); | |||||
} | |||||
static void | |||||
bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc) | |||||
{ | |||||
uint32_t mask; | |||||
/* Set the interrupt off bit in the interrupt mask register */ | |||||
mask = bcm2835_rng_read4(sc, RNG_INT_MASK); | |||||
mask |= RNG_INT_OFF_BIT; | |||||
bcm2835_rng_write4(sc, RNG_INT_MASK, mask); | |||||
} | |||||
#if defined(BCM2835_RNG_USE_INTERRUPT) | |||||
static void | |||||
bcm2835_rng_enable_intr(struct bcm2835_rng_softc *sc) | |||||
{ | |||||
uint32_t mask; | |||||
/* Clear the interrupt off bit in the interrupt mask register */ | |||||
mask = bcm2835_rng_read4(sc, RNG_INT_MASK); | |||||
mask &= ~RNG_INT_OFF_BIT; | |||||
bcm2835_rng_write4(sc, RNG_INT_MASK, mask); | |||||
} | |||||
#endif | |||||
static void | |||||
bcm2835_rng_start(struct bcm2835_rng_softc *sc) | |||||
{ | |||||
uint32_t ctrl; | |||||
/* Disable the interrupt */ | |||||
bcm2835_rng_disable_intr(sc); | |||||
/* Set the warmup count */ | |||||
bcm2835_rng_write4(sc, RNG_STATUS, RND_VAL_WARM_CNT); | |||||
/* Enable the RNG */ | |||||
ctrl = bcm2835_rng_read4(sc, RNG_CTRL); | |||||
ctrl |= RNG_RBGEN_BIT; | |||||
if (sc->sc_rbg2x) | |||||
ctrl |= RNG_RBG2X; | |||||
bcm2835_rng_write4(sc, RNG_CTRL, ctrl); | |||||
#if defined(BCM2835_RNG_USE_INTERRUPT) | |||||
/* Enable the interrupt */ | |||||
bcm2835_rng_enable_intr(sc); | |||||
#endif | |||||
} | |||||
static void | |||||
Not Done Inline ActionsIt might be a good idea to make this tweekable, like a sysctl, instead of a hard constant. A panic is quite harsh, so I agree with the #ifdef defaulting to "off". markm: It might be a good idea to make this tweekable, like a sysctl, instead of a hard constant.
A… | |||||
bcm2835_rng_stop(struct bcm2835_rng_softc *sc) | |||||
{ | |||||
uint32_t ctrl; | |||||
/* Disable the RNG */ | |||||
ctrl = bcm2835_rng_read4(sc, RNG_CTRL); | |||||
ctrl &= ~RNG_RBGEN_BIT; | |||||
bcm2835_rng_write4(sc, RNG_CTRL, ctrl); | |||||
} | |||||
Not Done Inline ActionsYuk :-) A delay here s a no-no. The interrupt/callout method needs to be the default/only method, and this delay needs to go. markm: Yuk :-) A delay here s a no-no. The interrupt/callout method needs to be the default/only… | |||||
Not Done Inline ActionsOkay, I'll just remove the source registration pieces. Makes sense to just axe them since the RBG is not able to keep up with requests as a pure entropy source. stevek: Okay, I'll just remove the source registration pieces. Makes sense to just axe them since the… | |||||
static void | |||||
bcm2835_rng_harvest(struct bcm2835_rng_softc *sc) | |||||
{ | |||||
uint32_t *dest; | |||||
uint32_t status; | |||||
u_int cnt, nread, num_avail, num_words; | |||||
int seen_underrun, num_stalls; | |||||
dest = sc->sc_buf; | |||||
nread = num_words = 0; | |||||
seen_underrun = num_stalls = 0; | |||||
for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0; | |||||
cnt -= num_words) { | |||||
/* Read status register to find out how many words available */ | |||||
status = bcm2835_rng_read4(sc, RNG_STATUS); | |||||
num_avail = (status >> RND_VAL_SHIFT) & RND_VAL_MASK; | |||||
/* If we have none... */ | |||||
if (num_avail == 0) { | |||||
bcm2835_rng_stat_inc_underrun(sc); | |||||
if (++seen_underrun >= sc->sc_stall_count) { | |||||
if (num_stalls++ > 0) { | |||||
device_printf(sc->sc_dev, | |||||
"RNG stalled, disabling device\n"); | |||||
bcm2835_rng_stop(sc); | |||||
break; | |||||
} else { | |||||
device_printf(sc->sc_dev, | |||||
"Too many underruns, resetting\n"); | |||||
bcm2835_rng_stop(sc); | |||||
bcm2835_rng_start(sc); | |||||
seen_underrun = 0; | |||||
} | |||||
} | |||||
/* Try again */ | |||||
continue; | |||||
} | |||||
CTR2(KTR_DEV, "%s: %d words available in RNG FIFO", | |||||
device_get_nameunit(sc->sc_dev), num_avail); | |||||
/* Pull MIN(num_avail, cnt) words from the FIFO */ | |||||
num_words = (num_avail > cnt) ? cnt : num_avail; | |||||
bcm2835_rng_read_multi4(sc, RNG_DATA, dest, | |||||
num_words); | |||||
dest += num_words; | |||||
nread += num_words; | |||||
} | |||||
cnt = nread * sizeof(uint32_t); | |||||
if (cnt > 0) | |||||
random_harvest_queue(sc->sc_buf, cnt, cnt * NBBY / 2, | |||||
RANDOM_PURE_BROADCOM); | |||||
#if defined(BCM2835_RNG_USE_CALLOUT) | |||||
callout_reset(&sc->sc_rngto, sc->sc_rnghz, bcm2835_rng_harvest, sc); | |||||
#endif | |||||
} | |||||
static int | |||||
sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct bcm2835_rng_softc *sc = arg1; | |||||
int error, rbg2x; | |||||
rbg2x = sc->sc_rbg2x; | |||||
error = sysctl_handle_int(oidp, &rbg2x, 0, req); | |||||
if (error) | |||||
return (error); | |||||
if (req->newptr == NULL) | |||||
return (error); | |||||
if (rbg2x == sc->sc_rbg2x) | |||||
return (0); | |||||
/* Reset the RNG */ | |||||
bcm2835_rng_stop(sc); | |||||
sc->sc_rbg2x = rbg2x; | |||||
bcm2835_rng_start(sc); | |||||
return (0); | |||||
} | |||||
#ifdef BCM2835_RNG_DEBUG_REGISTERS | |||||
static int | |||||
sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct sbuf sb; | |||||
struct bcm2835_rng_softc *sc = arg1; | |||||
int error; | |||||
error = sysctl_wire_old_buffer(req, 0); | |||||
if (error != 0) | |||||
return (error); | |||||
sbuf_new_for_sysctl(&sb, NULL, 128, req); | |||||
bcm2835_rng_dump_registers(sc, &sb); | |||||
error = sbuf_finish(&sb); | |||||
sbuf_delete(&sb); | |||||
return (error); | |||||
} | |||||
#endif | |||||
static int | |||||
bcm2835_rng_probe(device_t dev) | |||||
{ | |||||
if (!ofw_bus_status_okay(dev)) | |||||
return (ENXIO); | |||||
if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-rng")) | |||||
return (ENXIO); | |||||
device_set_desc(dev, "Broadcom BCM2835 RNG"); | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | |||||
static int | |||||
bcm2835_rng_attach(device_t dev) | |||||
{ | |||||
struct bcm2835_rng_softc *sc; | |||||
struct sysctl_ctx_list *sysctl_ctx; | |||||
struct sysctl_oid *sysctl_tree; | |||||
int error, rid; | |||||
error = 0; | |||||
sc = device_get_softc(dev); | |||||
sc->sc_dev = dev; | |||||
sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT; | |||||
#ifdef BCM2835_RNG_USE_CALLOUT | |||||
/* Initialize callout */ | |||||
callout_init(&sc->sc_rngto, CALLOUT_MPSAFE); | |||||
#endif | |||||
TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x); | |||||
TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count); | |||||
/* Allocate memory resources */ | |||||
rid = 0; | |||||
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, | |||||
RF_ACTIVE); | |||||
if (sc->sc_mem_res == NULL) { | |||||
error = ENXIO; | |||||
goto out; | |||||
} | |||||
#if defined(BCM2835_RNG_USE_INTERRUPT) | |||||
/* Allocate interrupt resource */ | |||||
rid = 0; | |||||
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | |||||
RF_SHAREABLE | RF_ACTIVE); | |||||
if (sc->sc_irq_res == NULL) { | |||||
error = ENXIO; | |||||
goto out; | |||||
} | |||||
/* Set up the interrupt handler */ | |||||
error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, | |||||
NULL, (driver_intr_t *)bcm2835_rng_harvest, sc, &sc->sc_intr_hdl); | |||||
if (error) { | |||||
device_printf(dev, "Failed to set up IRQ\n"); | |||||
sc->sc_intr_hdl = NULL; | |||||
goto out; | |||||
} | |||||
#endif | |||||
/* Start the RNG */ | |||||
bcm2835_rng_start(sc); | |||||
/* Dump the registers if booting verbose */ | |||||
if (bootverbose) { | |||||
Not Done Inline ActionsCan this also be a sysctl? markm: Can this also be a sysctl? | |||||
Not Done Inline ActionsThere is a per-device sysctl. See farther below in this function. stevek: There is a per-device sysctl. See farther below in this function. | |||||
struct sbuf sb; | |||||
(void) sbuf_new(&sb, NULL, 256, | |||||
SBUF_AUTOEXTEND | SBUF_INCLUDENUL); | |||||
bcm2835_rng_dump_registers(sc, &sb); | |||||
sbuf_trim(&sb); | |||||
error = sbuf_finish(&sb); | |||||
if (error == 0) | |||||
device_printf(dev, "%s", sbuf_data(&sb)); | |||||
sbuf_delete(&sb); | |||||
Not Done Inline ActionsYuk. I know there is precedent for goto's in the BSD kernel, but I'd prefer not to see any new ones. (Preference not insistence). markm: Yuk. I know there is precedent for goto's in the BSD kernel, but I'd prefer not to see any new… | |||||
} | |||||
sysctl_ctx = device_get_sysctl_ctx(dev); | |||||
sysctl_tree = device_get_sysctl_tree(dev); | |||||
SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, | |||||
"underrun", CTLFLAG_RD, &sc->sc_underrun, | |||||
"Number of FIFO underruns"); | |||||
SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, | |||||
"2xspeed", CTLTYPE_INT | CTLFLAG_RW, sc, 0, | |||||
sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED"); | |||||
SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, | |||||
"stall_count", CTLFLAG_RW, &sc->sc_stall_count, | |||||
RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall"); | |||||
#ifdef BCM2835_RNG_DEBUG_REGISTERS | |||||
SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, | |||||
"dumpregs", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, | |||||
sysctl_bcm2835_rng_dump, "S", "Dump RNG registers"); | |||||
#endif | |||||
#if defined(BCM2835_RNG_USE_CALLOUT) | |||||
/* Reset callout */ | |||||
if (hz >= 100) | |||||
sc->sc_rnghz = hz / 100; | |||||
else | |||||
sc->sc_rnghz = 1; | |||||
callout_reset(&sc->sc_rngto, sc->sc_rnghz, bcm2835_rng_harvest, sc); | |||||
#endif | |||||
out: | |||||
if (error) | |||||
bcm2835_rng_detach(dev); | |||||
return (error); | |||||
} | |||||
static int | |||||
bcm2835_rng_detach(device_t dev) | |||||
{ | |||||
struct bcm2835_rng_softc *sc; | |||||
#if defined(BCM2835_RNG_USE_INTERRUPT) | |||||
int error; | |||||
#endif | |||||
sc = device_get_softc(dev); | |||||
/* Stop the RNG */ | |||||
bcm2835_rng_stop(sc); | |||||
/* Drain the callout it */ | |||||
#if defined(BCM2835_RNG_USE_CALLOUT) | |||||
callout_drain(&sc->sc_rngto); | |||||
#endif | |||||
#if defined(BCM2835_RNG_USE_INTERRUPT) | |||||
/* Tear down the interrupt */ | |||||
if (sc->sc_irq_res && sc->sc_intr_hdl) { | |||||
error = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); | |||||
if (error != 0) { | |||||
device_printf(dev, "could not tear down IRQ\n"); | |||||
return (error); | |||||
} | |||||
sc->sc_intr_hdl = NULL; | |||||
} | |||||
/* Release interrupt resource */ | |||||
if (sc->sc_irq_res) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); | |||||
sc->sc_irq_res = NULL; | |||||
} | |||||
#endif | |||||
/* Release memory resource */ | |||||
if (sc->sc_mem_res != NULL) | |||||
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); | |||||
return (0); | |||||
} | |||||
static device_method_t bcm2835_rng_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_probe, bcm2835_rng_probe), | |||||
DEVMETHOD(device_attach, bcm2835_rng_attach), | |||||
DEVMETHOD(device_detach, bcm2835_rng_detach), | |||||
DEVMETHOD_END | |||||
}; | |||||
static driver_t bcm2835_rng_driver = { | |||||
"bcmrng", | |||||
bcm2835_rng_methods, | |||||
sizeof(struct bcm2835_rng_softc) | |||||
}; | |||||
static devclass_t bcm2835_rng_devclass; | |||||
DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver, | |||||
bcm2835_rng_devclass, 0, 0); | |||||
DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, bcm2835_rng_devclass, 0, | |||||
0); | |||||
MODULE_VERSION(bcm2835_rng, 1); | |||||
MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1); |
This mode looks like it may be a liability? Should the callout/interrupt-using method not be the default?