/* $NetBSD: am335x_trng.c,v 1.2 2016/12/17 15:24:35 riastradh Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 __KERNEL_RCSID(0, "$NetBSD: am335x_trng.c,v 1.2 2016/12/17 15:24:35 riastradh Exp $"); #include "opt_omap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct omap_module rng_module = { AM335X_PRCM_CM_PER, CM_PER_RNG_CLKCTRL }; struct trng_softc { device_t sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; kmutex_t sc_lock; krndsource_t sc_rndsource; }; static int trng_match(device_t, cfdata_t, void *); static void trng_attach(device_t, device_t, void *); static void trng_callback(size_t, void *); CFATTACH_DECL_NEW(trng, sizeof(struct trng_softc), trng_match, trng_attach, NULL, NULL); #define TRNG_READ(sc, reg) \ bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) #define TRNG_WRITE(sc, reg, val) \ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) static int trng_match(device_t parent, cfdata_t match, void *aux) { struct obio_attach_args *obio = aux; if (obio->obio_addr == AM335X_TRNG_BASE && obio->obio_size == AM335X_TRNG_SIZE && obio->obio_intr == AM335X_INT_TRNG) return 1; return 0; } static void trng_attach(device_t parent, device_t self, void *aux) { struct trng_softc *sc = device_private(self); struct obio_attach_args *obio = aux; sc->sc_dev = self; sc->sc_iot = obio->obio_iot; if (bus_space_map(obio->obio_iot, obio->obio_addr, obio->obio_size, 0, &sc->sc_ioh) != 0) { aprint_error(": couldn't map address spcae\n"); return; } mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); prcm_module_enable(&rng_module); if ((TRNG_READ(sc, TRNG_CONTROL_REG) & TRNG_CONTROL_ENABLE) == 0) { TRNG_WRITE(sc, TRNG_CONFIG_REG, __SHIFTIN(0x21, TRNG_CONFIG_MIN_REFILL) | __SHIFTIN(0x22, TRNG_CONFIG_MAX_REFILL)); TRNG_WRITE(sc, TRNG_CONTROL_REG, __SHIFTIN(0x21, TRNG_CONTROL_STARTUP_CYCLES) | TRNG_CONTROL_ENABLE); } rndsource_setcb(&sc->sc_rndsource, trng_callback, sc); rnd_attach_source(&sc->sc_rndsource, device_xname(self), RND_TYPE_RNG, RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB); aprint_naive("\n"); aprint_normal("\n"); trng_callback(RND_POOLBITS / NBBY, sc); } static void trng_callback(size_t bytes_wanted, void *priv) { struct trng_softc * const sc = priv; uint32_t buf[2]; u_int retry; mutex_enter(&sc->sc_lock); while (bytes_wanted) { for (retry = 10; retry > 0; retry--) { if (TRNG_READ(sc, TRNG_STATUS_REG) & TRNG_STATUS_READY) break; delay(10); } if (retry == 0) break; buf[0] = TRNG_READ(sc, TRNG_OUTPUT_L_REG); buf[1] = TRNG_READ(sc, TRNG_OUTPUT_H_REG); TRNG_WRITE(sc, TRNG_INTACK_REG, TRNG_INTACK_READY); rnd_add_data_sync(&sc->sc_rndsource, buf, sizeof(buf), sizeof(buf) * NBBY); bytes_wanted -= MIN(bytes_wanted, sizeof(buf)); } explicit_memset(buf, 0, sizeof(buf)); mutex_exit(&sc->sc_lock); }