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");