/*
 * Copyright (c) 2022 ITE Corporation. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT ite_it8xxx2_peci

#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/peci.h>
#include <zephyr/kernel.h>
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/peci.h>
#include <soc.h>
#include <soc_dt.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <zephyr/irq.h>

LOG_MODULE_REGISTER(peci_ite_it8xxx2, CONFIG_PECI_LOG_LEVEL);

BUILD_ASSERT(IS_ENABLED(CONFIG_PECI_INTERRUPT_DRIVEN),
	     "Please enable the option CONFIG_PECI_INTERRUPT_DRIVEN");

/*
 * This driver is single-instance. If the devicetree contains multiple
 * instances, this will fail and the driver needs to be revisited.
 */
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
	     "Unsupported PECI Instance");

/* The following constants describes the bitrate of it8xxx2 PECI,
 * for the frequency are 2000KHz, 1000KHz, and 1600KHz. (Unit: KHz)
 */
#define	PECI_IT8XXX2_BITRATE_2MHZ		2000
#define	PECI_IT8XXX2_BITRATE_1MHZ		1000
#define	PECI_IT8XXX2_BITRATE_1P6MHZ		1600

/* The following masks are designed for the PECI bitrate settings,
 * for the bits[7:3] are not related to this features.
 */
#define	PECI_IT8XXX2_BITRATE_BITS_MASK		0x07
#define	PECI_IT8XXX2_BITRATE_2MHZ_BITS		0x00
#define	PECI_IT8XXX2_BITRATE_1MHZ_BITS		0x01
#define	PECI_IT8XXX2_BITRATE_1P6MHZ_BITS	0x04

/* The Transaction Timeout */
#define PECI_TIMEOUT_MS		30

/* PECI interface 0 */
#define PECI0			0

/* HOSTAR (F02C00h) */
#define HOBY			BIT(0)
#define FINISH			BIT(1)
#define RD_FCS_ERR		BIT(2)
#define WR_FCS_ERR		BIT(3)
#define EXTERR			BIT(5)
#define BUS_ER			BIT(6)
#define TEMPERR			BIT(7)
#define HOSTAR_RST_ANYBIT \
		(TEMPERR|BUS_ER|EXTERR|WR_FCS_ERR|RD_FCS_ERR|FINISH)

/* HOCTLR (F02C01h) */
#define START			BIT(0)
#define AWFCS_EN		BIT(1)
#define CONTROL			BIT(2)
#define PECIHEN			BIT(3)
#define FCSERR_ABT		BIT(4)
#define FIFOCLR			BIT(5)

/*
 * TODO: The Voltage Configuration
 *       Related DTSi and registers settings should be fulfilled
 *       in the future.
 */
/* PADCTLR (F02C0Eh) */
#define PECI_DVIE		0x04

enum peci_vtts {
	HOVTTS0P85V = 0x00,
	HOVTTS0P90V = 0x01,
	HOVTTS0P95V = 0x02,
	HOVTTS1P00V = 0x03,
	HOVTTS1P05V = 0x08,
	HOVTTS1P10V = 0x09,
	HOVTTS1P15V = 0x0A,
	HOVTTS1P20V = 0x0B,
	HOVTTS1P25V = 0x10,
};

struct peci_it8xxx2_config {
	uintptr_t base_addr;
	uint8_t irq_no;
	const struct pinctrl_dev_config *pcfg;
};

struct peci_it8xxx2_data {
	struct peci_msg *msgs;
	struct k_sem device_sync_sem;
	uint32_t bitrate;
};

PINCTRL_DT_INST_DEFINE(0);

static const struct peci_it8xxx2_config peci_it8xxx2_config0 = {
	.base_addr = DT_INST_REG_ADDR(0),
	.irq_no = DT_INST_IRQN(0),
	.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
};

static struct peci_it8xxx2_data peci_it8xxx2_data0;

/* ITE IT8XXX2 PECI Functions */

static void peci_it8xxx2_init_vtts(struct peci_it8xxx2_regs *reg_base,
					enum peci_vtts vol_opt)
{
	reg_base->PADCTLR = (reg_base->PADCTLR & PECI_DVIE) | vol_opt;
}

static void peci_it8xxx2_rst_status(struct peci_it8xxx2_regs *reg_base)
{
	reg_base->HOSTAR = HOSTAR_RST_ANYBIT;
}

static int peci_it8xxx2_check_host_busy(struct peci_it8xxx2_regs *reg_base)
{
	return (reg_base->HOSTAR & HOBY) ? (-EBUSY) : 0;
}

static int peci_it8xxx2_check_host_finish(const struct device *dev)
{
	struct peci_it8xxx2_data *data = dev->data;
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;

	int ret = k_sem_take(&data->device_sync_sem, K_MSEC(PECI_TIMEOUT_MS));

	if (ret == -EAGAIN) {
		LOG_ERR("%s: Timeout", __func__);
		return -ETIMEDOUT;
	}

	if (peci_regs->HOSTAR != FINISH) {
		LOG_ERR("[PECI] Error: HOSTAR=0x%02X\r\n", peci_regs->HOSTAR);
			return -EIO;
	}

	return 0;
}

static int peci_it8xxx2_configure(const struct device *dev, uint32_t bitrate)
{
	struct peci_it8xxx2_data *data = dev->data;
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;

	uint8_t hoctl2r_to_write;

	data->bitrate =  bitrate;

	hoctl2r_to_write =
		(peci_regs->HOCTL2R) & (~(PECI_IT8XXX2_BITRATE_BITS_MASK));

	switch (bitrate) {
	case PECI_IT8XXX2_BITRATE_2MHZ:
		break;

	case PECI_IT8XXX2_BITRATE_1MHZ:
		hoctl2r_to_write |= PECI_IT8XXX2_BITRATE_1MHZ_BITS;
		break;

	case PECI_IT8XXX2_BITRATE_1P6MHZ:
		hoctl2r_to_write |= PECI_IT8XXX2_BITRATE_1P6MHZ_BITS;
		break;

	default:
		LOG_ERR("[PECI] Error: Specified Bitrate Not Supported\r\n");
		hoctl2r_to_write |= PECI_IT8XXX2_BITRATE_1MHZ_BITS;
		data->bitrate =  PECI_IT8XXX2_BITRATE_1MHZ;
		peci_regs->HOCTL2R = hoctl2r_to_write;
		return -ENOTSUP;
	}

	peci_regs->HOCTL2R = hoctl2r_to_write;

	return 0;
}

static int peci_it8xxx2_enable(const struct device *dev)
{
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;

	peci_regs->HOCTLR |= (FIFOCLR|FCSERR_ABT|PECIHEN|CONTROL);

	return 0;
}

static int peci_it8xxx2_disable(const struct device *dev)
{
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;

	peci_regs->HOCTLR &= ~(PECIHEN);
	return 0;
}

static void peci_it8xxx2_rst_module(const struct device *dev)
{
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;
	struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE;

	LOG_ERR("[PECI] Module Reset for Status Error.\r\n");
	/* Reset IT8XXX2 PECI Module Thoroughly */
	gctrl_regs->GCTRL_RSTC4 |= IT8XXX2_GCTRL_RPECI;
	/*
	 * Due to the fact that we've checked if the peci_enable()
	 * called before calling the peci_transfer(), so the peci
	 * were definitely enabled before the error occurred.
	 * Here is the recovery mechanism for recovering the PECI
	 * bus when the errors occur.
	 */
	peci_regs->PADCTLR |= PECI_DVIE;
	peci_it8xxx2_init_vtts(peci_regs, HOVTTS0P95V);
	peci_it8xxx2_configure(dev, PECI_IT8XXX2_BITRATE_1MHZ);
	peci_it8xxx2_enable(dev);
	LOG_ERR("[PECI] Reinitialization Finished.\r\n");
}

static int peci_it8xxx2_transfer(const struct device *dev, struct peci_msg *msg)
{
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;

	struct peci_buf *peci_rx_buf = &msg->rx_buffer;
	struct peci_buf *peci_tx_buf = &msg->tx_buffer;

	int cnt, ret_code;

	ret_code = 0;

	if (!(peci_regs->HOCTLR & PECIHEN)) {
		LOG_ERR("[PECI] Please call the peci_enable() first.\r\n");
		return -ECONNREFUSED;
	}

	if (peci_it8xxx2_check_host_busy(peci_regs) != 0) {
		return -EBUSY;
	}

	peci_regs->HOTRADDR = msg->addr;
	peci_regs->HOWRLR = peci_tx_buf->len;
	peci_regs->HORDLR = peci_rx_buf->len;
	peci_regs->HOCMDR = msg->cmd_code;

	if (msg->cmd_code != PECI_CMD_PING) {
		for (cnt = 0; cnt < (peci_tx_buf->len - 1); cnt++) {
			peci_regs->HOWRDR = peci_tx_buf->buf[cnt];
		}
	}

	/* Host Available */
	irq_enable(config->irq_no);
	peci_regs->HOCTLR |= START;
	ret_code = peci_it8xxx2_check_host_finish(dev);

	if (!ret_code) {
		/* Host Transactions Finished, Fetch Data from the regs */
		if (peci_rx_buf->len) {
			for (cnt = 0; cnt < (peci_rx_buf->len); cnt++) {
				peci_rx_buf->buf[cnt] = peci_regs->HORDDR;
			}
		}
		peci_it8xxx2_rst_status(peci_regs);

	} else {
		/* Host Transactions Failure */
		peci_it8xxx2_rst_module(dev);
	}

	return (ret_code);
}

static void peci_it8xxx2_isr(const struct device *dev)
{
	struct peci_it8xxx2_data *data = dev->data;
	const struct peci_it8xxx2_config *config = dev->config;

	irq_disable(config->irq_no);
	k_sem_give(&data->device_sync_sem);
}

static const struct peci_driver_api peci_it8xxx2_driver_api = {
	.config = peci_it8xxx2_configure,
	.enable = peci_it8xxx2_enable,
	.disable = peci_it8xxx2_disable,
	.transfer = peci_it8xxx2_transfer,
};

static int peci_it8xxx2_init(const struct device *dev)
{
	struct peci_it8xxx2_data *data = dev->data;
	const struct peci_it8xxx2_config *config = dev->config;
	struct peci_it8xxx2_regs *const peci_regs =
		(struct peci_it8xxx2_regs *)config->base_addr;
	int status;

	/* Initialize Semaphore */
	k_sem_init(&data->device_sync_sem, 0, 1);

	/* Configure the GPF6 to Alternative Function 3: PECI */
	status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
	if (status < 0) {
		LOG_ERR("Failed to configure PECI pins");
		return status;
	}

	peci_regs->PADCTLR |= PECI_DVIE;
	peci_it8xxx2_init_vtts(peci_regs, HOVTTS0P95V);
	peci_it8xxx2_configure(dev, PECI_IT8XXX2_BITRATE_1MHZ);

	/* Interrupt Assignment */
	IRQ_CONNECT(DT_INST_IRQN(0),
	0,
	peci_it8xxx2_isr,
	DEVICE_DT_INST_GET(0),
	0);

	return 0;
}

DEVICE_DT_INST_DEFINE(0,
	&peci_it8xxx2_init,
	NULL,
	&peci_it8xxx2_data0,
	&peci_it8xxx2_config0,
	POST_KERNEL, CONFIG_PECI_INIT_PRIORITY,
	&peci_it8xxx2_driver_api);
