/*
 * Copyright (c) 2023-2024 Analog Devices, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <string.h>
#include <zephyr/sys/util.h>

#define DT_DRV_COMPAT adi_max32_trng

#include <zephyr/device.h>
#include <zephyr/drivers/entropy.h>
#include <zephyr/drivers/clock_control/adi_max32_clock_control.h>

#include <wrap_max32_trng.h>

struct max32_trng_config {
	const struct device *clock;
	struct max32_perclk perclk;
};

static int api_get_entropy(const struct device *dev, uint8_t *buf, uint16_t len)
{
	return MXC_TRNG_Random(buf, len);
}

static int api_get_entropy_isr(const struct device *dev, uint8_t *buf, uint16_t len, uint32_t flags)
{
	int ret = 0;

	if ((flags & ENTROPY_BUSYWAIT) == 0) {
		uint32_t temp;
		uint32_t copy_len;
		uint32_t count = 0;

		while (len) {
			ret = Wrap_MXC_TRNG_RandomInt_NonBlocking(&temp);
			if (ret != 0) {
				break; /* Data not ready do not wait */
			}

			copy_len = MIN(len, 4);
			memcpy(buf, (uint8_t *)&temp, copy_len);

			len -= copy_len;
			buf += copy_len;
			count += copy_len;
		}

		/* User would like to read len bytes but in non-blocking mode
		 *  the function might read less, in that case return value will be
		 *  number of bytes read, if its 0 that means no data reads function
		 *  will return -ENODATA
		 */
		ret = count ? count : -ENODATA;
	} else {
		/* Allowed to busy-wait */
		ret = api_get_entropy(dev, buf, len);
		if (ret == 0) {
			ret = len; /* Data retrieved successfully. */
		}
	}

	return ret;
}

static DEVICE_API(entropy, entropy_max32_api) = {.get_entropy = api_get_entropy,
							    .get_entropy_isr = api_get_entropy_isr};

static int entropy_max32_init(const struct device *dev)
{
	int ret;
	const struct max32_trng_config *cfg = dev->config;

	/* Enable clock */
	ret = clock_control_on(cfg->clock, (clock_control_subsys_t)&cfg->perclk);

	return ret;
}

static const struct max32_trng_config max32_trng_cfg = {
	.clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)),
	.perclk.bus = DT_INST_CLOCKS_CELL(0, offset),
	.perclk.bit = DT_INST_CLOCKS_CELL(0, bit),
};

DEVICE_DT_INST_DEFINE(0, entropy_max32_init, NULL, NULL, &max32_trng_cfg, PRE_KERNEL_1,
		      CONFIG_ENTROPY_INIT_PRIORITY, &entropy_max32_api);
