drivers: crypto: ace_sha: add implementation of hardware based lib rand

This patch adds implementation of rand library based on hardware random
number generator of security subsystem in Exynos SOC.

This library includes:
- srand()  - used for seed hardware block
- rand()   - returns random number
- rand_r() - the same as above with given seed

which depends on CONFIG_EXYNOS_ACE_SHA and CONFIG_LIB_HW_RAND.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
cc: Akshay Saraswat <akshay.s@samsung.com>
cc: ARUN MANKUZHI <arun.m@samsung.com>
cc: Minkyu Kang <mk7.kang@samsung.com>
Cc: Michael Walle <michael@walle.cc>
Cc: Tom Rini <trini@ti.com>
Cc: Masahiro Yamada <yamada.m@jp.panasonic.com>
diff --git a/drivers/crypto/ace_sha.c b/drivers/crypto/ace_sha.c
index acbafde..ed4f541 100644
--- a/drivers/crypto/ace_sha.c
+++ b/drivers/crypto/ace_sha.c
@@ -5,10 +5,12 @@
  * SPDX-License-Identifier:	GPL-2.0+
  */
 #include <common.h>
+#include "ace_sha.h"
+
+#ifdef CONFIG_SHA_HW_ACCEL
 #include <sha256.h>
 #include <sha1.h>
 #include <asm/errno.h>
-#include "ace_sha.h"
 
 /* SHA1 value for the message of zero length */
 static const unsigned char sha1_digest_emptymsg[SHA1_SUM_LEN] = {
@@ -111,3 +113,72 @@
 	if (ace_sha_hash_digest(pbuf, buf_len, pout, ACE_SHA_TYPE_SHA1))
 		debug("ACE was not setup properly or it is faulty\n");
 }
+#endif /* CONFIG_SHA_HW_ACCEL */
+
+#ifdef CONFIG_LIB_HW_RAND
+static unsigned int seed_done;
+
+void srand(unsigned int seed)
+{
+	struct exynos_ace_sfr *reg =
+		(struct exynos_ace_sfr *)samsung_get_base_ace_sfr();
+	int i, status;
+
+	/* Seed data */
+	for (i = 0; i < ACE_HASH_PRNG_REG_NUM; i++)
+		writel(seed << i, &reg->hash_seed[i]);
+
+	/* Wait for seed setup done */
+	while (1) {
+		status = readl(&reg->hash_status);
+		if ((status & ACE_HASH_SEEDSETTING_MASK) ||
+		    (status & ACE_HASH_PRNGERROR_MASK))
+			break;
+	}
+
+	seed_done = 1;
+}
+
+unsigned int rand(void)
+{
+	struct exynos_ace_sfr *reg =
+		(struct exynos_ace_sfr *)samsung_get_base_ace_sfr();
+	int i, status;
+	unsigned int seed = (unsigned int)&status;
+	unsigned int ret = 0;
+
+	if (!seed_done)
+		srand(seed);
+
+	/* Start PRNG */
+	writel(ACE_HASH_ENGSEL_PRNG | ACE_HASH_STARTBIT_ON, &reg->hash_control);
+
+	/* Wait for PRNG done */
+	while (1) {
+		status = readl(&reg->hash_status);
+		if (status & ACE_HASH_PRNGDONE_MASK)
+			break;
+		if (status & ACE_HASH_PRNGERROR_MASK) {
+			seed_done = 0;
+			return 0;
+		}
+	}
+
+	/* Clear Done IRQ */
+	writel(ACE_HASH_PRNGDONE_MASK, &reg->hash_status);
+
+	/* Read a PRNG result */
+	for (i = 0; i < ACE_HASH_PRNG_REG_NUM; i++)
+		ret += readl(&reg->hash_prng[i]);
+
+	seed_done = 0;
+	return ret;
+}
+
+unsigned int rand_r(unsigned int *seedp)
+{
+	srand(*seedp);
+
+	return rand();
+}
+#endif /* CONFIG_LIB_HW_RAND */