diff options
Diffstat (limited to 'zephyr/drivers/cros_shi/cros_shi_npcx.c')
-rw-r--r-- | zephyr/drivers/cros_shi/cros_shi_npcx.c | 899 |
1 files changed, 0 insertions, 899 deletions
diff --git a/zephyr/drivers/cros_shi/cros_shi_npcx.c b/zephyr/drivers/cros_shi/cros_shi_npcx.c deleted file mode 100644 index a290e320fc..0000000000 --- a/zephyr/drivers/cros_shi/cros_shi_npcx.c +++ /dev/null @@ -1,899 +0,0 @@ -/* 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_MS 9 - -/* - * 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_uptime_get() > 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_uptime_get() + SHI_CMD_RX_TIMEOUT_MS; - - /* 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); |