diff --git a/core/arch/arm/plat-synquacer/main.c b/core/arch/arm/plat-synquacer/main.c
index 714becd..c91ba9b 100644
--- a/core/arch/arm/plat-synquacer/main.c
+++ b/core/arch/arm/plat-synquacer/main.c
@@ -18,6 +18,7 @@
 #include <sm/optee_smc.h>
 #include <tee/entry_fast.h>
 #include <tee/entry_std.h>
+#include <rng_pta.h>
 #include <timer_fiq.h>
 
 static void main_fiq(void);
@@ -39,6 +40,7 @@ static struct pl011_data console_data;
 
 register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, CORE_MMU_DEVICE_SIZE);
 register_phys_mem(MEM_AREA_IO_SEC, GIC_BASE, CORE_MMU_DEVICE_SIZE);
+register_phys_mem(MEM_AREA_IO_SEC, THERMAL_SENSOR_BASE, CORE_MMU_DEVICE_SIZE);
 
 const struct thread_handlers *generic_boot_get_handlers(void)
 {
@@ -78,6 +80,9 @@ static enum itr_return timer_itr_cb(struct itr_handler *h __unused)
 	/* Reset timer for next FIQ */
 	generic_timer_handler();
 
+	/* Collect entropy on each timer FIQ */
+	rng_collect_entropy();
+
 	return ITRR_HANDLED;
 }
 
@@ -92,6 +97,10 @@ static TEE_Result init_timer_itr(void)
 	itr_add(&timer_itr);
 	itr_enable(IT_SEC_TIMER);
 
+	/* Enable timer FIQ to fetch entropy required during boot */
+	generic_timer_start();
+	timer_fiq_running = true;
+
 	return TEE_SUCCESS;
 }
 driver_init(init_timer_itr);
diff --git a/core/arch/arm/plat-synquacer/platform_config.h b/core/arch/arm/plat-synquacer/platform_config.h
index f9b1b40..8a91ddb 100644
--- a/core/arch/arm/plat-synquacer/platform_config.h
+++ b/core/arch/arm/plat-synquacer/platform_config.h
@@ -19,6 +19,7 @@
 #define CONSOLE_UART_CLK_IN_HZ		62500000
 #define CONSOLE_BAUDRATE		115200
 
+#define THERMAL_SENSOR_BASE		0x54190000
 #define IT_SEC_TIMER			29
 
 #define DRAM0_BASE			0x80000000
diff --git a/core/arch/arm/plat-synquacer/rng_pta.c b/core/arch/arm/plat-synquacer/rng_pta.c
new file mode 100644
index 0000000..f9a40aa
--- /dev/null
+++ b/core/arch/arm/plat-synquacer/rng_pta.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2018, Linaro Limited
+ */
+
+/*
+ * Developerbox doesn't provide a hardware based true random number
+ * generator. So this pseudo TA is an effort to provide good source of
+ * entropy using noise from 7 thermal sensors. Its suitable for entropy
+ * required during boot, seeding kernel entropy pool, cryptographic use
+ * etc.
+ *
+ * Assumption
+ * ==========
+ *
+ * Here we have assumed a conservative entropy estimate as 4 bits per 7
+ * sensor readings. This entropy estimate is based on our simple minimal
+ * entropy estimates done on 2.1G bytes of raw samples collected from
+ * thermal sensors.
+ *
+ * Theory of operation
+ * ===================
+ *
+ * This routine uses secure timer interrupt to sample raw thermal sensor
+ * readings. As thermal sensor refresh rate is every 2ms, so interrupt
+ * fires every 2ms. It implements continuous health test counting rising
+ * and falling edges to report if sensors fail to provide entropy.
+ *
+ * It uses vetted conditioner as SHA512/256 (approved hash algorithm)
+ * to condense entropy. As per NIST.SP.800-90B spec, to get full entropy
+ * from vetted conditioner, we need to supply double of input entropy.
+ * According to assumption above and requirement for vetted conditioner,
+ * we need to supply 28 raw sensor readings to get 1 byte of full
+ * entropy as output. So for 32 bytes of conditioner output, we need to
+ * supply 896 bytes of raw sensor readings.
+ *
+ * Interfaces -> Input
+ * -------------------
+ *
+ * void rng_collect_entropy(void);
+ *
+ * Called as part of secure timer interrupt handler to sample raw
+ * thermal sensor readings and add entropy to the pool.
+ *
+ * Interfaces -> Output
+ * --------------------
+ *
+ * TEE_Result rng_get_entropy(uint32_t types,
+ *                            TEE_Param params[TEE_NUM_PARAMS]);
+ *
+ * Invoke command to expose an entropy interface to normal world.
+ *
+ * Testing
+ * =======
+ *
+ * Passes FIPS 140-2 rngtest.
+ *
+ * Limitation
+ * ==========
+ *
+ * Output rate is limited to approx. 128 bytes per second.
+ *
+ */
+
+#include <crypto/crypto.h>
+#include <kernel/delay.h>
+#include <kernel/pseudo_ta.h>
+#include <kernel/spinlock.h>
+#include <mm/core_memprot.h>
+#include <io.h>
+#include <string.h>
+#include <rng_pta.h>
+#include <rng_pta_client.h>
+#include <timer_fiq.h>
+
+#define PTA_NAME "rng.pta"
+
+#define THERMAL_SENSOR_BASE0		0x54190800
+#define THERMAL_SENSOR_OFFSET		0x80
+#define NUM_OF_SENSORS			7
+
+#define TEMP_DATA_REG_OFFSET		0x34
+
+#define ENTROPY_POOL_SIZE		4096
+
+#define SENSOR_DATA_SIZE		128
+
+/*
+ * As per NIST.SP.800-90B, to get full entropy from vetted conditioner, we need
+ * to supply double of input entropy. So with full entropy (8 bits per byte) we
+ * will get yield as one byte of output data for every 28 sensor readings.
+ * For 32 bytes of SHA512/256 output data, we need to supply 896 bytes of raw
+ * input data.
+ */
+#define CONDITIONER_PAYLOAD		(SENSOR_DATA_SIZE * NUM_OF_SENSORS)
+
+/*
+ * Used in heatlh test to check if count of bit flips 1-0 or 0-1 lies in 12.5%
+ * to 50.0% of 128 bytes raw data from particular sensor reading. In ideal
+ * scenario either of bit flips should be around 25%.
+ */
+#define MAX_BIT_FLIP_EDGE_COUNT		64
+#define MIN_BIT_FLIP_EDGE_COUNT		16
+
+static uint8_t entropy_pool[ENTROPY_POOL_SIZE] = {0};
+static uint32_t entropy_size;
+
+/* Current sensor data */
+static uint8_t sensor_data[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0};
+static uint8_t sensor_idx;
+
+/* Sensor data that passed health test */
+static uint8_t sensor_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0};
+static uint8_t num_sensors_pass;
+
+static uint8_t rest_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0};
+
+static uint32_t health_test_fail_cnt;
+static uint32_t health_test_pass_cnt;
+
+static unsigned int entropy_lock = SPINLOCK_UNLOCK;
+
+static void pool_add_entropy(uint8_t *entropy, uint32_t size)
+{
+	uint32_t copy_size;
+
+	if (entropy_size >= ENTROPY_POOL_SIZE)
+		return;
+
+	if ((ENTROPY_POOL_SIZE - entropy_size) >= size)
+		copy_size = size;
+	else
+		copy_size = ENTROPY_POOL_SIZE - entropy_size;
+
+	memcpy((entropy_pool + entropy_size), entropy, copy_size);
+
+	entropy_size += copy_size;
+}
+
+static void pool_get_entropy(uint8_t *buf, uint32_t size)
+{
+	uint32_t off;
+
+	if (size > entropy_size)
+		return;
+
+	off = entropy_size - size;
+
+	memcpy(buf, &entropy_pool[off], size);
+	entropy_size -= size;
+}
+
+static bool health_test(uint8_t sensor_id)
+{
+	bool result = true;
+	uint8_t bit_flip_falling = 0, bit_flip_rising = 0;
+	uint8_t i;
+
+	for (i = 0; i < (SENSOR_DATA_SIZE - 1); i++) {
+		if ((sensor_data[sensor_id][i] ^
+		     sensor_data[sensor_id][i + 1]) & 0x1) {
+			bit_flip_falling += sensor_data[sensor_id][i] & 0x1;
+			bit_flip_rising += sensor_data[sensor_id][i + 1] & 0x1;
+		}
+	}
+
+	if (bit_flip_falling > bit_flip_rising) {
+		if (bit_flip_rising < MIN_BIT_FLIP_EDGE_COUNT)
+			result = false;
+		if (bit_flip_falling > MAX_BIT_FLIP_EDGE_COUNT)
+			result = false;
+	} else {
+		if (bit_flip_falling < MIN_BIT_FLIP_EDGE_COUNT)
+			result = false;
+		if (bit_flip_rising > MAX_BIT_FLIP_EDGE_COUNT)
+			result = false;
+	}
+
+	return result;
+}
+
+static void pool_check_add_entropy(void)
+{
+	uint32_t i;
+	uint8_t entropy_sha512_256[TEE_SHA256_HASH_SIZE];
+	uint8_t rest_sensors_pass = 0;
+	TEE_Result res;
+
+	for (i = 0; i < NUM_OF_SENSORS; i++) {
+		/* Check if particular sensor data passes health test */
+		if (health_test(i) == true) {
+			health_test_pass_cnt++;
+			if (num_sensors_pass < NUM_OF_SENSORS) {
+				memcpy(sensor_data_pass[num_sensors_pass],
+				       sensor_data[i], SENSOR_DATA_SIZE);
+				num_sensors_pass++;
+			} else {
+				memcpy(rest_data_pass[rest_sensors_pass],
+				       sensor_data[i], SENSOR_DATA_SIZE);
+				rest_sensors_pass++;
+			}
+		} else {
+			health_test_fail_cnt++;
+		}
+	}
+
+	/* Check if sensor_data_pass is full */
+	if (num_sensors_pass == NUM_OF_SENSORS) {
+		/*
+		 * Use vetted conditioner SHA512/256 as per
+		 * NIST.SP.800-90B to condition raw data from entropy
+		 * source.
+		 */
+		res = hash_sha512_256_compute(entropy_sha512_256,
+					      (uint8_t *)sensor_data_pass,
+					      CONDITIONER_PAYLOAD);
+		if (res == TEE_SUCCESS)
+			pool_add_entropy(entropy_sha512_256,
+					 TEE_SHA256_HASH_SIZE);
+	}
+
+	if (rest_sensors_pass)
+		memcpy((uint8_t *)sensor_data_pass, (uint8_t *)rest_data_pass,
+		       (rest_sensors_pass * SENSOR_DATA_SIZE));
+
+	num_sensors_pass = rest_sensors_pass;
+}
+
+void rng_collect_entropy(void)
+{
+	uint8_t i;
+	void *vaddr;
+	uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL);
+
+	cpu_spin_lock(&entropy_lock);
+
+	for (i = 0; i < NUM_OF_SENSORS; i++) {
+		vaddr = phys_to_virt_io(THERMAL_SENSOR_BASE0 +
+					(THERMAL_SENSOR_OFFSET * i) +
+					TEMP_DATA_REG_OFFSET);
+		sensor_data[i][sensor_idx] = (uint8_t)read32((vaddr_t)vaddr);
+	}
+
+	sensor_idx++;
+
+	if (sensor_idx >= SENSOR_DATA_SIZE) {
+		pool_check_add_entropy();
+		sensor_idx = 0;
+	}
+
+	if (entropy_size >= ENTROPY_POOL_SIZE) {
+		generic_timer_stop();
+		timer_fiq_running = false;
+	}
+
+	cpu_spin_unlock(&entropy_lock);
+	thread_set_exceptions(exceptions);
+}
+
+static TEE_Result rng_get_entropy(uint32_t types,
+				  TEE_Param params[TEE_NUM_PARAMS])
+{
+	uint8_t *e = NULL;
+	uint32_t pool_size = 0, rq_size = 0;
+	uint32_t exceptions;
+	TEE_Result res = TEE_SUCCESS;
+
+	if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
+				     TEE_PARAM_TYPE_NONE,
+				     TEE_PARAM_TYPE_NONE,
+				     TEE_PARAM_TYPE_NONE)) {
+		EMSG("bad parameters types: 0x%" PRIx32, types);
+		return TEE_ERROR_BAD_PARAMETERS;
+	}
+
+	rq_size = params[0].memref.size;
+
+	if ((rq_size == 0) || (rq_size > ENTROPY_POOL_SIZE))
+		return TEE_ERROR_NOT_SUPPORTED;
+
+	e = (uint8_t *)params[0].memref.buffer;
+	if (!e)
+		return TEE_ERROR_BAD_PARAMETERS;
+
+	exceptions = thread_mask_exceptions(THREAD_EXCP_ALL);
+	cpu_spin_lock(&entropy_lock);
+
+	/*
+	 * Report health test failure to normal world in case fail count
+	 * exceeds 1% of pass count.
+	 */
+	if (health_test_pass_cnt > 100) {
+		if (health_test_fail_cnt > (health_test_pass_cnt / 100)) {
+			res = TEE_ERROR_HEALTH_TEST_FAIL;
+			params[0].memref.size = 0;
+			health_test_pass_cnt = 0;
+			health_test_fail_cnt = 0;
+			goto exit;
+		}
+	} else {
+		if (health_test_fail_cnt > 1) {
+			res = TEE_ERROR_HEALTH_TEST_FAIL;
+			params[0].memref.size = 0;
+			health_test_pass_cnt = 0;
+			health_test_fail_cnt = 0;
+			goto exit;
+		}
+	}
+
+	pool_size = entropy_size;
+
+	if (pool_size < rq_size) {
+		params[0].memref.size = pool_size;
+		pool_get_entropy(e, pool_size);
+	} else {
+		params[0].memref.size = rq_size;
+		pool_get_entropy(e, rq_size);
+	}
+
+exit:
+	if (timer_fiq_running == false) {
+		/* Enable timer FIQ to fetch entropy */
+		generic_timer_start();
+		timer_fiq_running = true;
+	}
+
+	cpu_spin_unlock(&entropy_lock);
+	thread_set_exceptions(exceptions);
+
+	return res;
+}
+
+/*
+ * Trusted Application Entry Points
+ */
+static TEE_Result open_session(uint32_t param_types __unused,
+			       TEE_Param params[TEE_NUM_PARAMS] __unused,
+			       void **session_context __unused)
+{
+	DMSG("open entry point for pseudo-TA \"%s\"", PTA_NAME);
+	return TEE_SUCCESS;
+}
+
+static TEE_Result invoke_command(void *pSessionContext __unused,
+				 uint32_t nCommandID, uint32_t nParamTypes,
+				 TEE_Param pParams[TEE_NUM_PARAMS])
+{
+	FMSG("command entry point for pseudo-TA \"%s\"", PTA_NAME);
+
+	switch (nCommandID) {
+	case PTA_CMD_GET_ENTROPY:
+		return rng_get_entropy(nParamTypes, pParams);
+	default:
+		break;
+	}
+
+	return TEE_ERROR_NOT_IMPLEMENTED;
+}
+
+pseudo_ta_register(.uuid = PTA_RNG_UUID, .name = PTA_NAME,
+		   .flags = PTA_DEFAULT_FLAGS,
+		   .open_session_entry_point = open_session,
+		   .invoke_command_entry_point = invoke_command);
diff --git a/core/arch/arm/plat-synquacer/rng_pta.h b/core/arch/arm/plat-synquacer/rng_pta.h
new file mode 100644
index 0000000..8ce2afa
--- /dev/null
+++ b/core/arch/arm/plat-synquacer/rng_pta.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2018, Linaro Limited
+ */
+
+#ifndef __RNG_PTA_H
+#define __RNG_PTA_H
+
+void rng_collect_entropy(void);
+
+#endif /* __RNG_PTA_H */
diff --git a/core/arch/arm/plat-synquacer/rng_pta_client.h b/core/arch/arm/plat-synquacer/rng_pta_client.h
new file mode 100644
index 0000000..b7986d3
--- /dev/null
+++ b/core/arch/arm/plat-synquacer/rng_pta_client.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2018, Linaro Limited
+ */
+
+#ifndef __RNG_PTA_CLIENT_H
+#define __RNG_PTA_CLIENT_H
+
+#define PTA_RNG_UUID { 0xab7a617c, 0xb8e7, 0x4d8f, \
+		{ 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64 } }
+
+#define TEE_ERROR_HEALTH_TEST_FAIL	0x00000001
+
+/*
+ * PTA_CMD_GET_ENTROPY - Get Entropy from RNG using Thermal Sensor
+ *
+ * param[0] (inout memref) - Entropy buffer memory reference
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
+ * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
+ */
+#define PTA_CMD_GET_ENTROPY		0x0
+
+#endif /* __RNG_PTA_CLIENT_H */
diff --git a/core/arch/arm/plat-synquacer/sub.mk b/core/arch/arm/plat-synquacer/sub.mk
index cfa1dc3..013e57d 100644
--- a/core/arch/arm/plat-synquacer/sub.mk
+++ b/core/arch/arm/plat-synquacer/sub.mk
@@ -1,3 +1,4 @@
 global-incdirs-y += .
 srcs-y += main.c
+srcs-y += rng_pta.c
 srcs-y += timer_fiq.c
