ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/soc/asr/geu/asr-rng.c b/marvell/linux/drivers/soc/asr/geu/asr-rng.c
new file mode 100644
index 0000000..bc4e192
--- /dev/null
+++ b/marvell/linux/drivers/soc/asr/geu/asr-rng.c
@@ -0,0 +1,179 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/cputype.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+
+#include "asr-geu.h"
+
+#define SAMPLE_UDELAY    (6)
+
+static inline u32 rng_readl(struct asr_geu_rng *hwrng, u32 offset)
+{
+	struct asr_geu_dev *geu_dd = dev_get_drvdata(hwrng->dev);
+
+	return readl(geu_dd->io_base + offset);
+}
+
+static inline void rng_writel(struct asr_geu_rng *hwrng, u32 val,
+				u32 offset)
+{
+	struct asr_geu_dev *geu_dd = dev_get_drvdata(hwrng->dev);
+
+	writel(val, geu_dd->io_base + offset);
+}
+
+#if defined(CONFIG_CPU_ASR1901)
+static void asr_rng_clr_fifo(struct asr_geu_rng *hwrng)
+{
+	u32 val;
+	val = rng_readl(hwrng, GEU_SQU_RNG_CTRL);
+	val |= RNG_FIFO_CLR;
+	rng_writel(hwrng, val, GEU_SQU_RNG_CTRL);
+}
+#endif
+
+static int asr_rng_init(struct hwrng *rng)
+{
+	u32 val;
+	struct asr_geu_rng *hwrng = (struct asr_geu_rng *)rng->priv;
+
+	val = rng_readl(hwrng, GEU_RNG_CTRL);
+	if ((val & GEU_RNG_EN) == GEU_RNG_EN)
+		return 0;
+
+	#if !defined(CONFIG_CPU_ASR1901)
+		#if defined (CONFIG_CPU_ASR1903)
+		if (!cpu_is_asr1903_z1()) {
+			writel(hwrng->rn_saved, hwrng->seed_base + 0x0);
+			writel(jiffies & 0xFFFFFFFF, hwrng->seed_base + 0x4);
+		}
+		#else
+		rng_writel(hwrng, jiffies & 0xFFFFFFFF, GEU_RNG_SEED_HI);
+		rng_writel(hwrng, hwrng->rn_saved, GEU_RNG_SEED_LO);
+		#endif
+	#endif
+
+	val |= GEU_RNG_EN; /* enable hardware random generator */
+	rng_writel(hwrng, val, GEU_RNG_CTRL);
+	usleep_range(70, 80);
+
+	return 0;
+}
+
+static int asr_rng_disable(struct asr_geu_rng *hwrng)
+{
+	u32 val;
+
+	val = rng_readl(hwrng, GEU_RNG_CTRL);
+	val &= ~GEU_RNG_EN;
+	rng_writel(hwrng, val, GEU_RNG_CTRL);
+
+	return 0;
+}
+
+static int asr_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+	int timeout = max / 4 + 1;
+	unsigned int val, random = 0;
+	u32 *data = (u32 *)buf;
+	size_t read = 0;
+
+	struct asr_geu_rng *hwrng = (struct asr_geu_rng *)rng->priv;
+	struct asr_geu_dev *geu_dd = dev_get_drvdata(hwrng->dev);
+	struct asr_geu_ops *geu_ops = geu_dd->geu_ops;
+
+	geu_ops->dev_get(geu_dd);
+
+	asr_rng_init(rng);
+
+	random = hwrng->rn_saved;
+	while (read < max) {
+		val = rng_readl(hwrng, GEU_RNG_GEN);
+		#if defined(CONFIG_CPU_ASR1901)
+		asr_rng_clr_fifo(hwrng);
+		#endif
+		if (val == random || val == 0) {
+			if (wait) {
+				udelay(SAMPLE_UDELAY);
+				if (timeout-- == 0) {
+					geu_ops->dev_put(geu_dd);
+					return read;
+				}
+			} else {
+				geu_ops->dev_put(geu_dd);
+				return 0;
+			}
+		} else {
+			*data = random = val;
+			data++;
+			read += 4;
+		}
+	}
+
+	asr_rng_disable(hwrng);
+
+	hwrng->rn_saved = random;
+	geu_ops->dev_put(geu_dd);
+
+	return read;
+}
+
+static struct hwrng asr_rng = {
+	.name		= "asr",
+	.init		= asr_rng_init,
+	.read		= asr_rng_read,
+	.quality = 1000,
+};
+
+int asr_geu_rng_register(struct asr_geu_dev *geu_dd)
+{
+	int err = 0;
+	struct asr_geu_rng *prng;
+
+	prng = &geu_dd->asr_rng;
+	prng->rn_saved = 0xdeadbeef;
+	prng->hwrng = &asr_rng;
+	prng->io_base = geu_dd->io_base;
+	prng->dev = geu_dd->dev;
+#ifdef CONFIG_CPU_ASR1903
+	prng->seed_base = ioremap_nocache(ASR1903_RNG_SEED, 0x10);
+#endif
+
+	asr_rng.priv = (unsigned long)prng;
+
+	err = hwrng_register(&asr_rng);
+	if (err) {
+		dev_err(prng->dev, "failed to register asr_rng!\n");
+		return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(asr_geu_rng_register);
+
+int asr_geu_rng_unregister(struct asr_geu_dev *geu_dd)
+{
+	struct asr_geu_rng *prng = &geu_dd->asr_rng;
+
+#ifdef CONFIG_CPU_ASR1903
+	iounmap(prng->seed_base);
+#endif
+
+	hwrng_unregister(prng->hwrng);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(asr_geu_rng_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yu Zhang <yuzhang@asrmicro.com>");
+MODULE_DESCRIPTION("ASR H/W RNG driver");