/*
 * Copyright (c) 2025, Texas Instruments
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT ti_k2g_sci_clk

#include <zephyr/device.h>
#include <zephyr/drivers/firmware/tisci/tisci.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/tisci_clock_control.h>
#include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(ti_k2g_sci_clk, CONFIG_CLOCK_CONTROL_LOG_LEVEL);

static const struct device *dmsc = DEVICE_DT_GET(DT_NODELABEL(dmsc));

static int tisci_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate)
{
	struct tisci_clock_config *req = (struct tisci_clock_config *)sys;
	uint64_t temp_rate;
	int ret;

	ret = tisci_cmd_clk_get_freq(dmsc, req->dev_id, req->clk_id, &temp_rate);
	if (ret) {
		LOG_ERR("Failed to get clock freq: dev_id=%u clk_id=%u err=%d", req->dev_id,
			req->clk_id, ret);
		return ret;
	}

	*rate = (uint32_t)temp_rate;

	return 0;
}

static int tisci_set_rate(const struct device *dev, void *sys, void *rate)
{
	struct tisci_clock_config *req = (struct tisci_clock_config *)sys;
	uint64_t freq = *((uint64_t *)rate);
	int ret;

	ret = tisci_cmd_clk_set_freq(dmsc, req->dev_id, req->clk_id, freq, freq, freq);
	if (ret) {
		LOG_ERR("Failed to set clock freq: dev_id=%u clk_id=%u freq=%llu err=%d",
			req->dev_id, req->clk_id, freq, ret);
	}

	return ret;
}

static enum clock_control_status tisci_get_status(const struct device *dev,
						  clock_control_subsys_t sys)
{
	struct tisci_clock_config *req = (struct tisci_clock_config *)sys;
	bool req_state = true;
	bool curr_state = true;
	int ret;

	ret = tisci_cmd_clk_is_on(dmsc, req->clk_id, req->dev_id, &req_state, &curr_state);
	if (ret) {
		LOG_ERR("Failed to get clock ON status: dev_id=%u clk_id=%u err=%d", req->dev_id,
			req->clk_id, ret);
		return CLOCK_CONTROL_STATUS_UNKNOWN;
	}
	if (curr_state) {
		return CLOCK_CONTROL_STATUS_ON;
	}
	if (req_state && !curr_state) {
		return CLOCK_CONTROL_STATUS_STARTING;
	}

	curr_state = true;

	ret = tisci_cmd_clk_is_off(dmsc, req->clk_id, req->dev_id, NULL, &curr_state);
	if (ret) {
		LOG_ERR("Failed to get clock OFF status: dev_id=%u clk_id=%u err=%d", req->dev_id,
			req->clk_id, ret);
		return CLOCK_CONTROL_STATUS_UNKNOWN;
	}

	if (curr_state) {
		return CLOCK_CONTROL_STATUS_OFF;
	}

	return CLOCK_CONTROL_STATUS_UNKNOWN;
}

static DEVICE_API(clock_control, tisci_clock_driver_api) = {
	.get_rate = tisci_get_rate,
	.set_rate = tisci_set_rate,
	.get_status = tisci_get_status
};

#define TI_K2G_SCI_CLK_INIT(_n)                                                                    \
	DEVICE_DT_INST_DEFINE(_n, NULL, NULL, NULL, NULL, PRE_KERNEL_1,                            \
			      CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &tisci_clock_driver_api);

DT_INST_FOREACH_STATUS_OKAY(TI_K2G_SCI_CLK_INIT)
