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 */
diff --git a/drivers/crypto/ace_sha.h b/drivers/crypto/ace_sha.h
index a426d52..f1097f7 100644
--- a/drivers/crypto/ace_sha.h
+++ b/drivers/crypto/ace_sha.h
@@ -72,9 +72,10 @@
 	unsigned char   res12[0x30];
 	unsigned int	hash_result[8];
 	unsigned char   res13[0x20];
-	unsigned int	hash_seed[8];
-	unsigned int	hash_prng[8];
-	unsigned char   res14[0x180];
+	unsigned int	hash_seed[5];
+	unsigned char	res14[12];
+	unsigned int	hash_prng[5];
+	unsigned char	res15[0x18c];
 
 	unsigned int	pka_sfr[5];		/* base + 0x700 */
 };
@@ -291,6 +292,7 @@
 #define ACE_HASH_PRNGERROR_MASK	(1 << 7)
 #define ACE_HASH_PRNGERROR_OFF		(0 << 7)
 #define ACE_HASH_PRNGERROR_ON		(1 << 7)
+#define ACE_HASH_PRNG_REG_NUM		5
 
 #define ACE_SHA_TYPE_SHA1		1
 #define ACE_SHA_TYPE_SHA256		2