// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright (c) 2019, Broadcom
 */

#include <drivers/gic.h>
#include <drivers/sp805_wdt.h>
#include <io.h>
#include <kernel/interrupt.h>
#include <kernel/misc.h>
#include <kernel/pseudo_ta.h>
#include <trace.h>

#define SEC_WD_SERVICE_UUID \
		{ 0x6272636D, 0x2019, 0x0801,  \
		{ 0x42, 0x43, 0x4D, 0x5F, 0x57, 0x44, 0x54, 0x30 } }

#define PTA_BCM_SEC_WD_CMD_CONFIG	0
#define PTA_BCM_SEC_WD_CMD_START	1
#define PTA_BCM_SEC_WD_CMD_PING		2
#define PTA_BCM_SEC_WD_CMD_STOP		3
#define PTA_BCM_SEC_WD_CMD_SET_TIMEOUT	4

#define SEC_WD_TA_NAME		"pta_bcm_sec_wd.ta"

static struct sp805_wdt_data wd_pd;

static void wd_isr_handler(struct wdt_chip *chip __unused)
{
	/* Do nothing */
	DMSG("Watchdog ISR handler !!!");
}

static TEE_Result pta_wd_config(uint32_t param_types,
				TEE_Param params[TEE_NUM_PARAMS])
{
	uint32_t timeout = 0;
	uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE);

	if (exp_param_types != param_types) {
		EMSG("Invalid Param types");
		return TEE_ERROR_BAD_PARAMETERS;
	}

	timeout = params[0].value.a;

	sp805_wdt_init(&wd_pd, SEC_WDT_BASE, SEC_WDT_CLK_HZ, timeout);

	sp805_register_itr_handler(&wd_pd, GIC_SPI_TO_ITNUM(SEC_WDT_INTR),
				   ITRF_TRIGGER_LEVEL, wd_isr_handler);
	wd_pd.chip.ops->start(&wd_pd.chip);

	return TEE_SUCCESS;
}

static TEE_Result pta_wd_start(uint32_t param_types,
			       TEE_Param params[TEE_NUM_PARAMS] __unused)
{
	uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE);

	if (exp_param_types != param_types) {
		EMSG("Invalid Param types");
		return TEE_ERROR_BAD_PARAMETERS;
	}

	wd_pd.chip.ops->start(&wd_pd.chip);

	return TEE_SUCCESS;
}

static TEE_Result pta_wd_ping(uint32_t param_types,
			      TEE_Param params[TEE_NUM_PARAMS] __unused)
{
	uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE);

	if (exp_param_types != param_types) {
		EMSG("Invalid Param types");
		return TEE_ERROR_BAD_PARAMETERS;
	}

	wd_pd.chip.ops->ping(&wd_pd.chip);

	return TEE_SUCCESS;
}

static TEE_Result pta_wd_stop(uint32_t param_types,
			      TEE_Param params[TEE_NUM_PARAMS] __unused)
{
	uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE);

	if (exp_param_types != param_types) {
		EMSG("Invalid Param types");
		return TEE_ERROR_BAD_PARAMETERS;
	}

	wd_pd.chip.ops->stop(&wd_pd.chip);

	return TEE_SUCCESS;
}

static TEE_Result pta_wd_set_timeout(uint32_t param_types,
				     TEE_Param params[TEE_NUM_PARAMS])
{
	uint32_t timeout = 0;
	uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE,
						   TEE_PARAM_TYPE_NONE);

	if (exp_param_types != param_types) {
		EMSG("Invalid Param types");
		return TEE_ERROR_BAD_PARAMETERS;
	}

	timeout = params[0].value.a;

	wd_pd.chip.ops->set_timeout(&wd_pd.chip, timeout);

	return TEE_SUCCESS;
}

static TEE_Result invoke_command(void *session_context __unused,
				 uint32_t cmd_id,
				 uint32_t param_types,
				 TEE_Param params[TEE_NUM_PARAMS])
{
	TEE_Result res = TEE_SUCCESS;

	DMSG("command entry point[%d] for \"%s\"", cmd_id, SEC_WD_TA_NAME);

	switch (cmd_id) {
	case PTA_BCM_SEC_WD_CMD_CONFIG:
		res = pta_wd_config(param_types, params);
		break;
	case PTA_BCM_SEC_WD_CMD_START:
		res = pta_wd_start(param_types, params);
		break;
	case PTA_BCM_SEC_WD_CMD_PING:
		res = pta_wd_ping(param_types, params);
		break;
	case PTA_BCM_SEC_WD_CMD_STOP:
		res = pta_wd_stop(param_types, params);
		break;
	case PTA_BCM_SEC_WD_CMD_SET_TIMEOUT:
		res = pta_wd_set_timeout(param_types, params);
		break;
	default:
		EMSG("cmd: %d Not supported %s", cmd_id, SEC_WD_TA_NAME);
		res = TEE_ERROR_NOT_SUPPORTED;
		break;
	}

	return res;
}

pseudo_ta_register(.uuid = SEC_WD_SERVICE_UUID,
		   .name = SEC_WD_TA_NAME,
		   .flags = PTA_DEFAULT_FLAGS,
		   .invoke_command_entry_point = invoke_command);
