ingenic_rng.c revision 1.1
11.1Smacallan/*	$NetBSD: ingenic_rng.c,v 1.1 2015/08/07 17:39:58 macallan Exp $ */
21.1Smacallan
31.1Smacallan/*-
41.1Smacallan * Copyright (c) 2015 Michael McConville
51.1Smacallan * All rights reserved.
61.1Smacallan *
71.1Smacallan * Redistribution and use in source and binary forms, with or without
81.1Smacallan * modification, are permitted provided that the following conditions
91.1Smacallan * are met:
101.1Smacallan * 1. Redistributions of source code must retain the above copyright
111.1Smacallan *    notice, this list of conditions and the following disclaimer.
121.1Smacallan * 2. Redistributions in binary form must reproduce the above copyright
131.1Smacallan *    notice, this list of conditions and the following disclaimer in the
141.1Smacallan *    documentation and/or other materials provided with the distribution.
151.1Smacallan *
161.1Smacallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
171.1Smacallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
181.1Smacallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
191.1Smacallan * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
201.1Smacallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
211.1Smacallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
221.1Smacallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
231.1Smacallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
241.1Smacallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
251.1Smacallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
261.1Smacallan * POSSIBILITY OF SUCH DAMAGE.
271.1Smacallan */
281.1Smacallan
291.1Smacallan#include <sys/cdefs.h>
301.1Smacallan__KERNEL_RCSID(0, "$NetBSD: ingenic_rng.c,v 1.1 2015/08/07 17:39:58 macallan Exp $");
311.1Smacallan
321.1Smacallan#include <sys/param.h>
331.1Smacallan#include <sys/systm.h>
341.1Smacallan#include <sys/device.h>
351.1Smacallan#include <sys/mutex.h>
361.1Smacallan#include <sys/kernel.h>
371.1Smacallan#include <sys/mutex.h>
381.1Smacallan#include <sys/callout.h>
391.1Smacallan#include <sys/bus.h>
401.1Smacallan#include <sys/workqueue.h>
411.1Smacallan#include <sys/rndpool.h>
421.1Smacallan#include <sys/rndsource.h>
431.1Smacallan
441.1Smacallan#include <mips/ingenic/ingenic_var.h>
451.1Smacallan#include <mips/ingenic/ingenic_regs.h>
461.1Smacallan
471.1Smacallan#include "opt_ingenic.h"
481.1Smacallan
491.1Smacallanstruct ingenic_rng_softc;
501.1Smacallan
511.1Smacallanstatic int	ingenic_rng_match(device_t, cfdata_t, void *);
521.1Smacallanstatic void	ingenic_rng_attach(device_t, device_t, void *);
531.1Smacallan
541.1Smacallanstatic void	ingenic_rng_get(struct ingenic_rng_softc *);
551.1Smacallanstatic void	ingenic_rng_get_intr(void *);
561.1Smacallanstatic void	ingenic_rng_get_cb(size_t, void *);
571.1Smacallan
581.1Smacallanstruct ingenic_rng_softc {
591.1Smacallan	device_t		sc_dev;
601.1Smacallan	bus_space_tag_t		sc_bst;
611.1Smacallan	bus_space_handle_t	sc_bsh;
621.1Smacallan
631.1Smacallan	void *			sc_sih;
641.1Smacallan
651.1Smacallan	krndsource_t		sc_rndsource;
661.1Smacallan	size_t			sc_bytes_wanted;
671.1Smacallan
681.1Smacallan	kmutex_t		sc_intr_lock;
691.1Smacallan	kmutex_t		sc_rnd_lock;
701.1Smacallan};
711.1Smacallan
721.1SmacallanCFATTACH_DECL_NEW(ingenic_rng, sizeof(struct ingenic_rng_softc),
731.1Smacallan	ingenic_rng_match, ingenic_rng_attach, NULL, NULL);
741.1Smacallan
751.1Smacallanstatic int
761.1Smacallaningenic_rng_match(device_t parent, cfdata_t cf, void *aux)
771.1Smacallan{
781.1Smacallan	struct apbus_attach_args *aa = aux;
791.1Smacallan
801.1Smacallan	if (strcmp(aa->aa_name, "jzrng") == 0) {
811.1Smacallan		return 1;
821.1Smacallan	} else {
831.1Smacallan		return 0;
841.1Smacallan	}
851.1Smacallan}
861.1Smacallan
871.1Smacallanstatic void
881.1Smacallaningenic_rng_attach(device_t parent, device_t self, void *aux)
891.1Smacallan{
901.1Smacallan	struct ingenic_rng_softc * const sc = device_private(self);
911.1Smacallan	struct apbus_attach_args * const aa = aux;
921.1Smacallan	int error;
931.1Smacallan
941.1Smacallan	sc->sc_dev = self;
951.1Smacallan	sc->sc_bst = aa->aa_bst;
961.1Smacallan	if (aa->aa_addr == 0) {
971.1Smacallan		aa->aa_addr = JZ_RNG;
981.1Smacallan	}
991.1Smacallan
1001.1Smacallan	error = bus_space_map(aa->aa_bst, aa->aa_addr, 4, 0, &sc->sc_bsh);
1011.1Smacallan	if (error) {
1021.1Smacallan		aprint_error_dev(self,
1031.1Smacallan			"can't map registers for %s: %d\n", aa->aa_name, error);
1041.1Smacallan		return;
1051.1Smacallan	}
1061.1Smacallan
1071.1Smacallan	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SERIAL);
1081.1Smacallan	mutex_init(&sc->sc_rnd_lock, MUTEX_DEFAULT, IPL_SERIAL);
1091.1Smacallan
1101.1Smacallan	aprint_naive(": Ingenic random number generator\n");
1111.1Smacallan	aprint_normal(": Ingenic random number generator\n");
1121.1Smacallan
1131.1Smacallan	sc->sc_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE,
1141.1Smacallan	    ingenic_rng_get_intr, sc);
1151.1Smacallan	if (sc->sc_sih == NULL) {
1161.1Smacallan		aprint_error_dev(self, "couldn't establish softint\n");
1171.1Smacallan		return;
1181.1Smacallan	}
1191.1Smacallan
1201.1Smacallan	rndsource_setcb(&sc->sc_rndsource, ingenic_rng_get_cb, sc);
1211.1Smacallan	rnd_attach_source(&sc->sc_rndsource, device_xname(self), RND_TYPE_RNG,
1221.1Smacallan	    RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB);
1231.1Smacallan
1241.1Smacallan	ingenic_rng_get_cb(RND_POOLBITS / NBBY, sc);
1251.1Smacallan}
1261.1Smacallan
1271.1Smacallanstatic void
1281.1Smacallaningenic_rng_get(struct ingenic_rng_softc *sc)
1291.1Smacallan{
1301.1Smacallan	uint32_t data;
1311.1Smacallan
1321.1Smacallan	mutex_spin_enter(&sc->sc_intr_lock);
1331.1Smacallan	while (sc->sc_bytes_wanted) {
1341.1Smacallan		bus_space_read_region_4(sc->sc_bst, sc->sc_bsh, 0, &data, 1);
1351.1Smacallan#if 0
1361.1Smacallan		device_printf(sc->sc_dev, "random output: %x\n", data);
1371.1Smacallan#endif
1381.1Smacallan		mutex_spin_exit(&sc->sc_intr_lock);
1391.1Smacallan		mutex_spin_enter(&sc->sc_rnd_lock);
1401.1Smacallan		rnd_add_data(&sc->sc_rndsource, &data, sizeof(data),
1411.1Smacallan		    sizeof(data) * NBBY);
1421.1Smacallan		mutex_spin_exit(&sc->sc_rnd_lock);
1431.1Smacallan		mutex_spin_enter(&sc->sc_intr_lock);
1441.1Smacallan		sc->sc_bytes_wanted -= MIN(sc->sc_bytes_wanted, sizeof(data));
1451.1Smacallan	}
1461.1Smacallan	explicit_memset(&data, 0, sizeof(data));
1471.1Smacallan	mutex_spin_exit(&sc->sc_intr_lock);
1481.1Smacallan}
1491.1Smacallan
1501.1Smacallanstatic void
1511.1Smacallaningenic_rng_get_cb(size_t bytes_wanted, void *priv)
1521.1Smacallan{
1531.1Smacallan	struct ingenic_rng_softc * const sc = priv;
1541.1Smacallan
1551.1Smacallan	mutex_spin_enter(&sc->sc_intr_lock);
1561.1Smacallan	if (sc->sc_bytes_wanted == 0) {
1571.1Smacallan		softint_schedule(sc->sc_sih);
1581.1Smacallan	}
1591.1Smacallan	if (bytes_wanted > (UINT_MAX - sc->sc_bytes_wanted)) {
1601.1Smacallan		sc->sc_bytes_wanted = UINT_MAX;
1611.1Smacallan	} else {
1621.1Smacallan		sc->sc_bytes_wanted += bytes_wanted;
1631.1Smacallan	}
1641.1Smacallan	mutex_spin_exit(&sc->sc_intr_lock);
1651.1Smacallan}
1661.1Smacallan
1671.1Smacallanstatic void
1681.1Smacallaningenic_rng_get_intr(void *priv)
1691.1Smacallan{
1701.1Smacallan	struct ingenic_rng_softc * const sc = priv;
1711.1Smacallan
1721.1Smacallan	ingenic_rng_get(sc);
1731.1Smacallan}
174