/* Copyright 2021 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#define DT_DRV_COMPAT nuvoton_npcx_cros_shi

#include <arch/arm/aarch32/cortex_m/cmsis.h>
#include <assert.h>
#include <dt-bindings/clock/npcx_clock.h>
#include <drivers/clock_control.h>
#include <drivers/cros_shi.h>
#include <drivers/gpio.h>
#include <logging/log.h>
#include <kernel.h>
#include <soc.h>
#include <soc/nuvoton_npcx/reg_def_cros.h>

#include "host_command.h"
#include "soc_miwu.h"
#include "system.h"

#ifdef CONFIG_CROS_SHI_NPCX_DEBUG
#define DEBUG_CPRINTS(format, args...) cprints(CC_SPI, format, ##args)
#define DEBUG_CPRINTF(format, args...) cprintf(CC_SPI, format, ##args)
#else
#define DEBUG_CPRINTS(...)
#define DEBUG_CPRINTF(...)
#endif

LOG_MODULE_REGISTER(cros_shi, LOG_LEVEL_DBG);

#define SHI_NODE DT_NODELABEL(shi)
#define SHI_VER_CTRL_PH DT_PHANDLE_BY_IDX(SHI_NODE, ver_ctrl, 0)
#define SHI_VER_CTRL_ALT_FILED(f) DT_PHA_BY_IDX(SHI_VER_CTRL_PH, alts, 0, f)

/* Full output buffer size */
#define SHI_OBUF_FULL_SIZE 128
/* Full input buffer size  */
#define SHI_IBUF_FULL_SIZE 128
/* Configure the IBUFLVL2 = the size of V3 protocol header */
#define SHI_IBUFLVL2_THRESHOLD (sizeof(struct ec_host_request))
/* Half output buffer size */
#define SHI_OBUF_HALF_SIZE (SHI_OBUF_FULL_SIZE / 2)
/* Half input buffer size */
#define SHI_IBUF_HALF_SIZE (SHI_IBUF_FULL_SIZE / 2)

/*
 * Timeout to wait for SHI request packet
 *
 * This affects the slowest SPI clock we can support. A delay of 8192 us
 * permits a 512-byte request at 500 KHz, assuming the SPI controller starts
 * sending bytes as soon as it asserts chip select. That's as slow as we would
 * practically want to run the SHI interface, since running it slower
 * significantly impacts firmware update times.
 */
#define SHI_CMD_RX_TIMEOUT_US 8192

/*
 * The AP blindly clocks back bytes over the SPI interface looking for a
 * framing byte.  So this preamble must always precede the actual response
 * packet.
 */
#define SHI_OUT_PREAMBLE_LENGTH 2

/*
 * Space allocation of the past-end status byte (EC_SPI_PAST_END) in the out_msg
 * buffer.
 */
#define EC_SPI_PAST_END_LENGTH 1
/*
 * Space allocation of the frame status byte (EC_SPI_FRAME_START) in the out_msg
 * buffer.
 */
#define EC_SPI_FRAME_START_LENGTH 1

/*
 * Offset of output parameters needs to account for pad and framing bytes and
 * one last past-end byte at the end so any additional bytes clocked out by
 * the AP will have a known and identifiable value.
 */
#define SHI_PROTO3_OVERHEAD (EC_SPI_PAST_END_LENGTH + EC_SPI_FRAME_START_LENGTH)

/*
 * Max data size for a version 3 request/response packet.  This is big enough
 * to handle a request/response header, flash write offset/size, and 512 bytes
 * of flash data:
 *  sizeof(ec_host_request):          8
 *  sizeof(ec_params_flash_write):    8
 *  payload                         512
 */
#define SHI_MAX_REQUEST_SIZE CONFIG_CROS_SHI_MAX_REQUEST
#define SHI_MAX_RESPONSE_SIZE CONFIG_CROS_SHI_MAX_RESPONSE

/*
 * Our input and output msg buffers. These must be large enough for our largest
 * message, including protocol overhead.  The pointers after the protocol
 * overhead, as passed to the host command handler, must be 32-bit aligned.
 */
#define SHI_OUT_START_PAD (4 * (EC_SPI_FRAME_START_LENGTH / 4 + 1))
#define SHI_OUT_END_PAD (4 * (EC_SPI_PAST_END_LENGTH / 4 + 1))
static uint8_t out_msg_padded[SHI_OUT_START_PAD + SHI_MAX_RESPONSE_SIZE +
			      SHI_OUT_END_PAD] __aligned(4);
static uint8_t *const out_msg =
	out_msg_padded + SHI_OUT_START_PAD - EC_SPI_FRAME_START_LENGTH;
static uint8_t in_msg[SHI_MAX_REQUEST_SIZE] __aligned(4);

/* Parameters used by host protocols */
static struct host_packet shi_packet;

enum cros_shi_npcx_state {
	SHI_STATE_NONE = -1,
	/* SHI not enabled (initial state, and when chipset is off) */
	SHI_STATE_DISABLED = 0,
	/* Ready to receive next request */
	SHI_STATE_READY_TO_RECV,
	/* Receiving request */
	SHI_STATE_RECEIVING,
	/* Processing request */
	SHI_STATE_PROCESSING,
	/* Canceling response since CS deasserted and output NOT_READY byte */
	SHI_STATE_CNL_RESP_NOT_RDY,
	/* Sending response */
	SHI_STATE_SENDING,
	/* Received data is invalid */
	SHI_STATE_BAD_RECEIVED_DATA,
};

static enum cros_shi_npcx_state state;

/* Device config */
struct cros_shi_npcx_config {
	/* Serial Host Interface (SHI) base address */
	uintptr_t base;
	/* clock configuration */
	struct npcx_clk_cfg clk_cfg;
	/* pinmux configuration */
	const uint8_t alts_size;
	const struct npcx_alt *alts_list;
	/* SHI IRQ */
	int irq;
	struct npcx_wui shi_cs_wui;
};

/* SHI bus parameters */
struct shi_bus_parameters {
	uint8_t *rx_msg; /* Entry pointer of msg rx buffer   */
	uint8_t *tx_msg; /* Entry pointer of msg tx buffer   */
	volatile uint8_t *rx_buf; /* Entry pointer of receive buffer  */
	volatile uint8_t *tx_buf; /* Entry pointer of transmit buffer */
	uint16_t sz_received; /* Size of received data in bytes   */
	uint16_t sz_sending; /* Size of sending data in bytes    */
	uint16_t sz_request; /* request bytes need to receive    */
	uint16_t sz_response; /* response bytes need to receive   */
	uint64_t rx_deadline; /* deadline of receiving            */
} shi_params;

static const struct npcx_alt cros_shi_alts[] = NPCX_DT_ALT_ITEMS_LIST(0);

static const struct cros_shi_npcx_config cros_shi_cfg = {
	.base = DT_INST_REG_ADDR(0),
	.clk_cfg = NPCX_DT_CLK_CFG_ITEM(0),
	.alts_size = ARRAY_SIZE(cros_shi_alts),
	.alts_list = cros_shi_alts,
	.irq = DT_INST_IRQN(0),
	.shi_cs_wui = NPCX_DT_WUI_ITEM_BY_NAME(0, shi_cs_wui),
};

struct cros_shi_npcx_data {
	struct host_packet shi_packet;
	sys_slist_t callbacks;
};

/* Driver convenience defines */
#define DRV_CONFIG(dev) ((const struct cros_shi_npcx_config *)(dev)->config)
#define DRV_DATA(dev) ((struct cros_shi_npcx_data *)(dev)->data)
#define HAL_INSTANCE(dev) (struct shi_reg *)(DRV_CONFIG(dev)->base)

/* Forward declaration */
static void cros_shi_npcx_reset_prepare(struct shi_reg *const inst);

/* Read pointer of input or output buffer by consecutive reading */
static uint32_t shi_read_buf_pointer(struct shi_reg *const inst)
{
	uint8_t stat;

	/* Wait for two consecutive equal values are read */
	do {
		stat = inst->IBUFSTAT;
	} while (stat != inst->IBUFSTAT);

	return (uint32_t)stat;
}

/*
 * Valid offset of SHI output buffer to write.
 * When SIMUL bit is set, IBUFPTR can be used instead of OBUFPTR
 */
static uint32_t shi_valid_obuf_offset(struct shi_reg *const inst)
{
	return (shi_read_buf_pointer(inst) + SHI_OUT_PREAMBLE_LENGTH) %
	       SHI_OBUF_FULL_SIZE;
}

/*
 * This routine write SHI next half output buffer from msg buffer
 */
static void shi_write_half_outbuf(void)
{
	const uint32_t size =
		MIN(SHI_OBUF_HALF_SIZE,
		    shi_params.sz_response - shi_params.sz_sending);
	uint8_t *obuf_ptr = (uint8_t *)shi_params.tx_buf;
	const uint8_t *obuf_end = obuf_ptr + size;
	uint8_t *msg_ptr = shi_params.tx_msg;

	/* Fill half output buffer */
	while (obuf_ptr != obuf_end)
		*obuf_ptr++ = *msg_ptr++;

	shi_params.sz_sending += size;
	shi_params.tx_buf = obuf_ptr;
	shi_params.tx_msg = msg_ptr;
}

/*
 * This routine read SHI input buffer to msg buffer until
 * we have received a certain number of bytes
 */
static int shi_read_inbuf_wait(struct shi_reg *const inst, uint32_t szbytes)
{
	/* Copy data to msg buffer from input buffer */
	for (uint32_t i = 0; i < szbytes; i++, shi_params.sz_received++) {
		/*
		 * If input buffer pointer equals pointer which wants to read,
		 * it means data is not ready.
		 */
		while (shi_params.rx_buf ==
		       inst->IBUF + shi_read_buf_pointer(inst)) {
			if (k_cycle_get_64() >= shi_params.rx_deadline) {
				return 0;
			}
		}
		/* Restore data to msg buffer */
		*shi_params.rx_msg++ = *shi_params.rx_buf++;
	}
	return 1;
}

/* This routine fills out all SHI output buffer with status byte */
static void shi_fill_out_status(struct shi_reg *const inst, uint8_t status)
{
	uint8_t start, end;
	volatile uint8_t *fill_ptr;
	volatile uint8_t *fill_end;
	volatile uint8_t *obuf_end;

	/*
	 * Disable interrupts in case the interfere by the other interrupts.
	 * Use __disable_irq/__enable_irq instead of using irq_lock/irq_unlock
	 * here because irq_lock/irq_unlock leave some system exceptions (like
	 * SVC, NMI, and faults) still enabled.
	 */
	__disable_irq();

	/*
	 * Fill out output buffer with status byte and leave a gap for PREAMBLE.
	 * The gap guarantees the synchronization. The critical section should
	 * be done within this gap. No racing happens.
	 */
	start = shi_valid_obuf_offset(inst);
	end = (start + SHI_OBUF_FULL_SIZE - SHI_OUT_PREAMBLE_LENGTH) %
	      SHI_OBUF_FULL_SIZE;

	fill_ptr = inst->OBUF + start;
	fill_end = inst->OBUF + end;
	obuf_end = inst->OBUF + SHI_OBUF_FULL_SIZE;
	while (fill_ptr != fill_end) {
		*fill_ptr++ = status;
		if (fill_ptr == obuf_end)
			fill_ptr = inst->OBUF;
	}

	/* End of critical section */
	__enable_irq();
}

/* This routine handles shi received unexpected data */
static void shi_bad_received_data(struct shi_reg *const inst)
{
	/* State machine mismatch, timeout, or protocol we can't handle. */
	shi_fill_out_status(inst, EC_SPI_RX_BAD_DATA);
	state = SHI_STATE_BAD_RECEIVED_DATA;

	DEBUG_CPRINTF("BAD-");
	DEBUG_CPRINTF("in_msg=[");
	for (uint32_t i = 0; i < shi_params.sz_received; i++)
		DEBUG_CPRINTF("%02x ", in_msg[i]);
	DEBUG_CPRINTF("]\n");

	/* Reset shi's state machine for error recovery */
	cros_shi_npcx_reset_prepare(inst);

	DEBUG_CPRINTF("END\n");
}

/*
 * This routine write SHI output buffer from msg buffer over halt of it.
 * It make sure we have enough time to handle next operations.
 */
static void shi_write_first_pkg_outbuf(struct shi_reg *const inst,
				       uint16_t szbytes)
{
	uint8_t size, offset;
	volatile uint8_t *obuf_ptr;
	volatile uint8_t *obuf_end;
	uint8_t *msg_ptr;
	uint32_t half_buf_remain; /* Remains in half buffer are free to write */

	/* Start writing at our current OBUF position */
	offset = shi_valid_obuf_offset(inst);
	obuf_ptr = inst->OBUF + offset;
	msg_ptr = shi_params.tx_msg;

	/* Fill up to OBUF mid point, or OBUF end */
	half_buf_remain = SHI_OBUF_HALF_SIZE - (offset % SHI_OBUF_HALF_SIZE);
	size = MIN(half_buf_remain, szbytes - shi_params.sz_sending);
	obuf_end = obuf_ptr + size;
	while (obuf_ptr != obuf_end)
		*obuf_ptr++ = *msg_ptr++;

	/* Track bytes sent for later accounting */
	shi_params.sz_sending += size;

	/* Write data to beginning of OBUF if we've reached the end */
	if (obuf_ptr == inst->OBUF + SHI_IBUF_FULL_SIZE)
		obuf_ptr = inst->OBUF;

	/* Fill next half output buffer */
	size = MIN(SHI_OBUF_HALF_SIZE, szbytes - shi_params.sz_sending);
	obuf_end = obuf_ptr + size;
	while (obuf_ptr != obuf_end)
		*obuf_ptr++ = *msg_ptr++;

	/* Track bytes sent / last OBUF position written for later accounting */
	shi_params.sz_sending += size;
	shi_params.tx_buf = obuf_ptr;
	shi_params.tx_msg = msg_ptr;
}

/**
 * Called to send a response back to the host.
 *
 * Some commands can continue for a while. This function is called by
 * host_command task after processing request is completed. It fills up the
 * FIFOs with response package and the remaining data is handled in shi's ISR.
 */
static void shi_send_response_packet(struct host_packet *pkt)
{
	struct shi_reg *const inst = (struct shi_reg *)(cros_shi_cfg.base);

	/*
	 * Disable interrupts. This routine is not called from interrupt
	 * context and buffer underrun will likely occur if it is
	 * preempted after writing its initial reply byte. Also, we must be
	 * sure our state doesn't unexpectedly change, in case we're expected
	 * to take RESP_NOT_RDY actions.
	 */
	__disable_irq();

	if (state == SHI_STATE_PROCESSING) {
		/* Append our past-end byte, which we reserved space for. */
		((uint8_t *)pkt->response)[pkt->response_size] =
			EC_SPI_PAST_END;

		/* Computing sending bytes of response */
		shi_params.sz_response =
			pkt->response_size + SHI_PROTO3_OVERHEAD;

		/* Start to fill output buffer with msg buffer */
		shi_write_first_pkg_outbuf(inst, shi_params.sz_response);
		/* Transmit the reply */
		state = SHI_STATE_SENDING;
		DEBUG_CPRINTF("SND-");
	} else if (state == SHI_STATE_CNL_RESP_NOT_RDY) {
		/*
		 * If we're not processing, then the AP has already terminated
		 * the transaction, and won't be listening for a response.
		 * Reset state machine for next transaction.
		 */
		cros_shi_npcx_reset_prepare(inst);
		DEBUG_CPRINTF("END\n");
	} else
		DEBUG_CPRINTS("Unexpected state %d in response handler", state);

	__enable_irq();
}

void shi_handle_host_package(struct shi_reg *const inst)
{
	uint32_t sz_inbuf_int = shi_params.sz_request / SHI_IBUF_HALF_SIZE;
	uint32_t cnt_inbuf_int = shi_params.sz_received / SHI_IBUF_HALF_SIZE;

	if (sz_inbuf_int - cnt_inbuf_int)
		/* Need to receive data from buffer */
		return;
	uint32_t remain_bytes = shi_params.sz_request - shi_params.sz_received;

	/* Read remaining bytes from input buffer */
	if (!shi_read_inbuf_wait(inst, remain_bytes))
		return shi_bad_received_data(inst);

	/* Move to processing state */
	state = SHI_STATE_PROCESSING;
	DEBUG_CPRINTF("PRC-");

	/* Fill output buffer to indicate we`re processing request */
	shi_fill_out_status(inst, EC_SPI_PROCESSING);

	/* Set up parameters for host request */
	shi_packet.send_response = shi_send_response_packet;

	shi_packet.request = in_msg;
	shi_packet.request_temp = NULL;
	shi_packet.request_max = sizeof(in_msg);
	shi_packet.request_size = shi_params.sz_request;

	/* Put FRAME_START in first byte */
	out_msg[0] = EC_SPI_FRAME_START;
	shi_packet.response = out_msg + EC_SPI_FRAME_START_LENGTH;

	/* Reserve space for frame start and trailing past-end byte */
	shi_packet.response_max = SHI_MAX_RESPONSE_SIZE;
	shi_packet.response_size = 0;
	shi_packet.driver_result = EC_RES_SUCCESS;

	/* Go to common layer to handle request */
	host_packet_receive(&shi_packet);
}

static void shi_parse_header(struct shi_reg *const inst)
{
	/* We're now inside a transaction */
	state = SHI_STATE_RECEIVING;
	DEBUG_CPRINTF("RV-");

	/* Setup deadline time for receiving */
	shi_params.rx_deadline =
		k_cycle_get_64() + k_us_to_cyc_near64(SHI_CMD_RX_TIMEOUT_US);

	/* Wait for version, command, length bytes */
	if (!shi_read_inbuf_wait(inst, 3))
		return shi_bad_received_data(inst);

	if (in_msg[0] == EC_HOST_REQUEST_VERSION) {
		/* Protocol version 3 */
		struct ec_host_request *r = (struct ec_host_request *)in_msg;
		int pkt_size;
		/*
		 * If request is over half of input buffer,
		 * we need to modified the algorithm again.
		 */
		__ASSERT_NO_MSG(sizeof(*r) < SHI_IBUF_HALF_SIZE);

		/* Wait for the rest of the command header */
		if (!shi_read_inbuf_wait(inst, sizeof(*r) - 3))
			return shi_bad_received_data(inst);

		/* Check how big the packet should be */
		pkt_size = host_request_expected_size(r);
		if (pkt_size == 0 || pkt_size > sizeof(in_msg))
			return shi_bad_received_data(inst);

		/* Computing total bytes need to receive */
		shi_params.sz_request = pkt_size;

		shi_handle_host_package(inst);
	} else {
		/* Invalid version number */
		return shi_bad_received_data(inst);
	}
}

static void shi_sec_ibf_int_enable(struct shi_reg *const inst, int enable)
{
	if (enable) {
		/* Setup IBUFLVL2 threshold and enable it */
		inst->SHICFG5 |= BIT(NPCX_SHICFG5_IBUFLVL2DIS);
		SET_FIELD(inst->SHICFG5, NPCX_SHICFG5_IBUFLVL2,
			  SHI_IBUFLVL2_THRESHOLD);
		inst->SHICFG5 &= ~BIT(NPCX_SHICFG5_IBUFLVL2DIS);
		/* Enable IBHF2 event */
		inst->EVENABLE2 |= BIT(NPCX_EVENABLE2_IBHF2EN);
	} else {
		/* Disable IBHF2 event first */
		inst->EVENABLE2 &= ~BIT(NPCX_EVENABLE2_IBHF2EN);
		/* Disable IBUFLVL2 and set threshold back to zero */
		inst->SHICFG5 |= BIT(NPCX_SHICFG5_IBUFLVL2DIS);
		SET_FIELD(inst->SHICFG5, NPCX_SHICFG5_IBUFLVL2, 0);
	}
}

/* This routine copies SHI half input buffer data to msg buffer */
static void shi_read_half_inbuf(void)
{
	/*
	 * Copy to read buffer until reaching middle/top address of
	 * input buffer or completing receiving data
	 */
	do {
		/* Restore data to msg buffer */
		*shi_params.rx_msg++ = *shi_params.rx_buf++;
		shi_params.sz_received++;
	} while (shi_params.sz_received % SHI_IBUF_HALF_SIZE &&
		 shi_params.sz_received != shi_params.sz_request);
}

/*
 * Avoid spamming the console with prints every IBF / IBHF interrupt, if
 * we find ourselves in an unexpected state.
 */
static enum cros_shi_npcx_state last_error_state = SHI_STATE_NONE;

static void log_unexpected_state(char *isr_name)
{
	if (state != last_error_state)
		DEBUG_CPRINTF("Unexpected state %d in %s ISR", state, isr_name);
	last_error_state = state;
}

static void shi_handle_cs_assert(struct shi_reg *const inst)
{
	/* If not enabled, ignore glitches on SHI_CS_L */
	if (state == SHI_STATE_DISABLED)
		return;

	/* NOT_READY should be sent and there're no spi transaction now. */
	if (state == SHI_STATE_CNL_RESP_NOT_RDY)
		return;

	/* Chip select is low = asserted */
	if (state != SHI_STATE_READY_TO_RECV) {
		/* State machine should be reset in EVSTAT_EOR ISR */
		DEBUG_CPRINTF("Unexpected state %d in CS ISR", state);
		return;
	}

	DEBUG_CPRINTF("CSL-");

	/*
	 * Clear possible EOR event from previous transaction since it's
	 * irrelevant now that CS is re-asserted.
	 */
	inst->EVSTAT = BIT(NPCX_EVSTAT_EOR);

	/* Do not deep sleep during SHI transaction */
	disable_sleep(SLEEP_MASK_SPI);
}

static void shi_handle_cs_deassert(struct shi_reg *const inst)
{
	/*
	 * If the buffer is still used by the host command.
	 * Change state machine for response handler.
	 */
	if (state == SHI_STATE_PROCESSING) {
		/*
		 * Mark not ready to prevent the other
		 * transaction immediately
		 */
		shi_fill_out_status(inst, EC_SPI_NOT_READY);

		state = SHI_STATE_CNL_RESP_NOT_RDY;

		/*
		 * Disable SHI interrupt, it will remain disabled
		 * until shi_send_response_packet() is called and
		 * CS is asserted for a new transaction.
		 */
		irq_disable(DT_INST_IRQN(0));

		DEBUG_CPRINTF("CNL-");
		return;
		/* Next transaction but we're not ready */
	} else if (state == SHI_STATE_CNL_RESP_NOT_RDY) {
		return;
	}

	/* Error state for checking*/
	if (state != SHI_STATE_SENDING) {
		log_unexpected_state("CSNRE");
	}
	/* reset SHI and prepare to next transaction again */
	cros_shi_npcx_reset_prepare(inst);
	DEBUG_CPRINTF("END\n");
}

static void shi_handle_input_buf_half_full(struct shi_reg *const inst)
{
	if (state == SHI_STATE_RECEIVING) {
		/* Read data from input to msg buffer */
		shi_read_half_inbuf();
		return shi_handle_host_package(inst);
	} else if (state == SHI_STATE_SENDING) {
		/* Write data from msg buffer to output buffer */
		if (shi_params.tx_buf == inst->OBUF + SHI_OBUF_FULL_SIZE) {
			/* Write data from bottom address again */
			shi_params.tx_buf = inst->OBUF;
			return shi_write_half_outbuf();
		} else /* ignore it */
			return;
	} else if (state == SHI_STATE_PROCESSING) {
		/* Wait for host to handle request */
	} else {
		/* Unexpected status */
		log_unexpected_state("IBHF");
	}
}

static void shi_handle_input_buf_full(struct shi_reg *const inst)
{
	if (state == SHI_STATE_RECEIVING) {
		/* read data from input to msg buffer */
		shi_read_half_inbuf();
		/* Read to bottom address again */
		shi_params.rx_buf = inst->IBUF;
		return shi_handle_host_package(inst);
	} else if (state == SHI_STATE_SENDING) {
		/* Write data from msg buffer to output buffer */
		if (shi_params.tx_buf == inst->OBUF + SHI_OBUF_HALF_SIZE)
			return shi_write_half_outbuf();
		else /* ignore it */
			return;
	} else if (state == SHI_STATE_PROCESSING) {
		/* Wait for host to handle request */
		return;
	}
	/* Unexpected status */
	log_unexpected_state("IBF");
}

static void cros_shi_npcx_isr(const struct device *dev)
{
	uint8_t stat;
	uint8_t stat2;
	struct shi_reg *const inst = HAL_INSTANCE(dev);

	/* Read status register and clear interrupt status early */
	stat = inst->EVSTAT;
	inst->EVSTAT = stat;
	stat2 = inst->EVSTAT2;

	/* SHI CS pin is asserted in EVSTAT2 */
	if (IS_BIT_SET(stat2, NPCX_EVSTAT2_CSNFE)) {
		/* Clear pending bit of CSNFE */
		inst->EVSTAT2 = BIT(NPCX_EVSTAT2_CSNFE);
		DEBUG_CPRINTF("CSNFE-");
		/*
		 * BUSY bit is set when SHI_CS is asserted. If not, leave it for
		 * SHI_CS de-asserted event.
		 */
		if (!IS_BIT_SET(inst->SHICFG2, NPCX_SHICFG2_BUSY)) {
			DEBUG_CPRINTF("CSNB-");
			return;
		}
		shi_handle_cs_assert(inst);
	}

	/*
	 * End of data for read/write transaction. i.e. SHI_CS is deasserted.
	 * Host completed or aborted transaction
	 *
	 * EOR has the limitation that it will not be set even if the SHI_CS is
	 * deasserted without SPI clocks. The new SHI module introduce the
	 * CSNRE bit which will be set when SHI_CS is deasserted regardless of
	 * SPI clocks.
	 */
	if (IS_BIT_SET(stat2, NPCX_EVSTAT2_CSNRE)) {
		/* Clear pending bit of CSNRE */
		inst->EVSTAT2 = BIT(NPCX_EVSTAT2_CSNRE);
		/*
		 * We're not in proper state.
		 * Mark not ready to abort next transaction
		 */
		DEBUG_CPRINTF("CSH-");
		return shi_handle_cs_deassert(inst);
	}

	/*
	 * The number of bytes received reaches the size of
	 * protocol V3 header(=8) after CS asserted.
	 */
	if (IS_BIT_SET(stat2, NPCX_EVSTAT2_IBHF2)) {
		/* Clear IBHF2 */
		inst->EVSTAT2 = BIT(NPCX_EVSTAT2_IBHF2);
		DEBUG_CPRINTF("HDR-");
		/* Disable second IBF interrupt and start to parse header */
		shi_sec_ibf_int_enable(inst, 0);
		shi_parse_header(inst);
	}

	/*
	 * Indicate input/output buffer pointer reaches the half buffer size.
	 * Transaction is processing.
	 */
	if (IS_BIT_SET(stat, NPCX_EVSTAT_IBHF)) {
		return shi_handle_input_buf_half_full(inst);
	}

	/*
	 * Indicate input/output buffer pointer reaches the full buffer size.
	 * Transaction is processing.
	 */
	if (IS_BIT_SET(stat, NPCX_EVSTAT_IBF)) {
		return shi_handle_input_buf_full(inst);
	}
}

static void cros_shi_npcx_reset_prepare(struct shi_reg *const inst)
{
	uint32_t i;

	state = SHI_STATE_DISABLED;

	irq_disable(DT_INST_IRQN(0));

	/* Disable SHI unit to clear all status bits */
	inst->SHICFG1 &= ~BIT(NPCX_SHICFG1_EN);

	/* Initialize parameters of next transaction */
	shi_params.rx_msg = in_msg;
	shi_params.tx_msg = out_msg;
	shi_params.rx_buf = inst->IBUF;
	shi_params.tx_buf = inst->IBUF + SHI_OBUF_HALF_SIZE;
	shi_params.sz_received = 0;
	shi_params.sz_sending = 0;
	shi_params.sz_request = 0;
	shi_params.sz_response = 0;

	/*
	 * Fill output buffer to indicate we`re
	 * ready to receive next transaction.
	 */
	for (i = 1; i < SHI_OBUF_FULL_SIZE; i++)
		inst->OBUF[i] = EC_SPI_RECEIVING;
	inst->OBUF[0] = EC_SPI_OLD_READY;

	/* SHI/Host Write/input buffer wrap-around enable */
	inst->SHICFG1 = BIT(NPCX_SHICFG1_IWRAP) | BIT(NPCX_SHICFG1_WEN) |
			BIT(NPCX_SHICFG1_EN);

	state = SHI_STATE_READY_TO_RECV;
	last_error_state = SHI_STATE_NONE;

	shi_sec_ibf_int_enable(inst, 1);
	irq_enable(DT_INST_IRQN(0));

	/* Allow deep sleep at the end of SHI transaction */
	enable_sleep(SLEEP_MASK_SPI);

	DEBUG_CPRINTF("RDY-");
}

static int cros_shi_npcx_enable(const struct device *dev)
{
	const struct cros_shi_npcx_config *const config = DRV_CONFIG(dev);
	struct shi_reg *const inst = HAL_INSTANCE(dev);

	cros_shi_npcx_reset_prepare(inst);
	npcx_miwu_irq_disable(&config->shi_cs_wui);

	/* Configure pin-mux from GPIO to SHI. */
	npcx_pinctrl_mux_configure(config->alts_list, config->alts_size, 1);

	NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
	npcx_miwu_irq_enable(&config->shi_cs_wui);
	irq_enable(DT_INST_IRQN(0));

	return 0;
}

static int cros_shi_npcx_disable(const struct device *dev)
{
	const struct cros_shi_npcx_config *const config = DRV_CONFIG(dev);

	state = SHI_STATE_DISABLED;

	irq_disable(DT_INST_IRQN(0));
	npcx_miwu_irq_disable(&config->shi_cs_wui);

	/* Configure pin-mux from SHI to GPIO. */
	npcx_pinctrl_mux_configure(config->alts_list, config->alts_size, 0);

	/*
	 * Allow deep sleep again in case CS dropped before ec was
	 * informed in hook function and turn off SHI's interrupt in time.
	 */
	enable_sleep(SLEEP_MASK_SPI);

	return 0;
}

static int shi_npcx_init(const struct device *dev)
{
	int ret;
	const struct cros_shi_npcx_config *const config = DRV_CONFIG(dev);
	struct shi_reg *const inst = HAL_INSTANCE(dev);
	const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);

	/* Turn on shi device clock first */
	ret = clock_control_on(clk_dev,
			       (clock_control_subsys_t *)&config->clk_cfg);
	if (ret < 0) {
		DEBUG_CPRINTF("Turn on SHI clock fail %d", ret);
		return ret;
	}

	/* If booter doesn't set the host interface type */
	if (!NPCX_BOOTER_IS_HIF_TYPE_SET()) {
		npcx_host_interface_sel(NPCX_HIF_TYPE_ESPI_SHI);
	}

	/*
	 * SHICFG1 (SHI Configuration 1) setting
	 * [7] - IWRAP	= 1: Wrap input buffer to the first address
	 * [6] - CPOL	= 0: Sampling on rising edge and output on falling edge
	 * [5] - DAS	= 0: return STATUS reg data after Status command
	 * [4] - AUTOBE	= 0: Automatically update the OBES bit in STATUS reg
	 * [3] - AUTIBF	= 0: Automatically update the IBFS bit in STATUS reg
	 * [2] - WEN    = 0: Enable host write to input buffer
	 * [1] - Reserved 0
	 * [0] - ENABLE	= 0: Disable SHI at the beginning
	 */
	inst->SHICFG1 = 0x80;

	/*
	 * SHICFG2 (SHI Configuration 2) setting
	 * [7] - Reserved 0
	 * [6] - REEVEN = 0: Restart events are not used
	 * [5] - Reserved 0
	 * [4] - REEN   = 0: Restart transactions are not used
	 * [3] - SLWU   = 0: Seem-less wake-up is enabled by default
	 * [2] - ONESHOT= 0: WEN is cleared at the end of a write transaction
	 * [1] - BUSY   = 0: SHI bus is busy 0: idle.
	 * [0] - SIMUL	= 1: Turn on simultaneous Read/Write
	 */
	inst->SHICFG2 = 0x01;

	/*
	 * EVENABLE (Event Enable) setting
	 * [7] - IBOREN = 0: Input buffer overrun interrupt enable
	 * [6] - STSREN = 0: status read interrupt disable
	 * [5] - EOWEN  = 0: End-of-Data for Write Transaction Interrupt Enable
	 * [4] - EOREN  = 1: End-of-Data for Read Transaction Interrupt Enable
	 * [3] - IBHFEN = 1: Input Buffer Half Full Interrupt Enable
	 * [2] - IBFEN  = 1: Input Buffer Full Interrupt Enable
	 * [1] - OBHEEN = 0: Output Buffer Half Empty Interrupt Enable
	 * [0] - OBEEN  = 0: Output Buffer Empty Interrupt Enable
	 */
	inst->EVENABLE = 0x1C;

	/*
	 * EVENABLE2 (Event Enable 2) setting
	 * [2] - CSNFEEN = 1: SHI_CS Falling Edge Interrupt Enable
	 * [1] - CSNREEN = 1: SHI_CS Rising Edge Interrupt Enable
	 * [0] - IBHF2EN = 0: Input Buffer Half Full 2 Interrupt Enable
	 */
	inst->EVENABLE2 = 0x06;

	/* Clear SHI events status register */
	inst->EVSTAT = 0xff;

	npcx_miwu_interrupt_configure(&config->shi_cs_wui, NPCX_MIWU_MODE_EDGE,
				      NPCX_MIWU_TRIG_LOW);
	/* SHI interrupt installation */
	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
		    cros_shi_npcx_isr, DEVICE_DT_INST_GET(0), 0);

	return ret;
}

static const struct cros_shi_driver_api cros_shi_npcx_driver_api = {
	.enable = cros_shi_npcx_enable,
	.disable = cros_shi_npcx_disable,
};

static struct cros_shi_npcx_data cros_shi_data;
DEVICE_DT_INST_DEFINE(0, shi_npcx_init, /* pm_control_fn= */ NULL,
		      &cros_shi_data, &cros_shi_cfg, PRE_KERNEL_1,
		      CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
		      &cros_shi_npcx_driver_api);

/* KBS register structure check */
NPCX_REG_SIZE_CHECK(shi_reg, 0x120);
NPCX_REG_OFFSET_CHECK(shi_reg, SHICFG1, 0x001);
NPCX_REG_OFFSET_CHECK(shi_reg, EVENABLE, 0x005);
NPCX_REG_OFFSET_CHECK(shi_reg, IBUFSTAT, 0x00a);
NPCX_REG_OFFSET_CHECK(shi_reg, EVENABLE2, 0x010);
NPCX_REG_OFFSET_CHECK(shi_reg, OBUF, 0x020);
NPCX_REG_OFFSET_CHECK(shi_reg, IBUF, 0x0A0);
