diff options
Diffstat (limited to 'chip/npcx/shi.c')
-rw-r--r-- | chip/npcx/shi.c | 1082 |
1 files changed, 0 insertions, 1082 deletions
diff --git a/chip/npcx/shi.c b/chip/npcx/shi.c deleted file mode 100644 index 503a52807e..0000000000 --- a/chip/npcx/shi.c +++ /dev/null @@ -1,1082 +0,0 @@ -/* - * Copyright 2015 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. - */ - -/* - * SHI driver for Chrome EC. - * - * This uses Input/Output buffer to handle SPI transmission and reception. - */ - -#include "chipset.h" -#include "clock.h" -#include "console.h" -#include "gpio.h" -#include "task.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "spi.h" -#include "system.h" -#include "timer.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_SPI, outstr) -#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_SPI, format, ## args) - -#if !(DEBUG_SHI) -#define DEBUG_CPUTS(...) -#define DEBUG_CPRINTS(...) -#define DEBUG_CPRINTF(...) -#else -#define DEBUG_CPUTS(outstr) cputs(CC_SPI, outstr) -#define DEBUG_CPRINTS(format, args...) cprints(CC_SPI, format, ## args) -#define DEBUG_CPRINTF(format, args...) cprintf(CC_SPI, format, ## args) -#endif - -/* SHI Bus definition */ -#ifdef NPCX_SHI_V2 -#define SHI_OBUF_FULL_SIZE 128 /* Full output buffer size */ -#define SHI_IBUF_FULL_SIZE 128 /* Full input buffer size */ -/* Configure the IBUFLVL2 = the size of V3 protocol header */ -#define SHI_IBUFLVL2_THRESHOLD (sizeof(struct ec_host_request)) -#else -#define SHI_OBUF_FULL_SIZE 64 /* Full output buffer size */ -#define SHI_IBUF_FULL_SIZE 64 /* Full input buffer size */ -#endif -#define SHI_OBUF_HALF_SIZE (SHI_OBUF_FULL_SIZE/2) /* Half output buffer size */ -#define SHI_IBUF_HALF_SIZE (SHI_IBUF_FULL_SIZE/2) /* Half input buffer size */ - -/* Start address of SHI output buffer */ -#define SHI_OBUF_START_ADDR (volatile uint8_t *)(NPCX_SHI_BASE_ADDR + 0x020) -/* Middle address of SHI output buffer */ -#define SHI_OBUF_HALF_ADDR (SHI_OBUF_START_ADDR + SHI_OBUF_HALF_SIZE) -/* Top address of SHI output buffer */ -#define SHI_OBUF_FULL_ADDR (SHI_OBUF_START_ADDR + SHI_IBUF_FULL_SIZE) -/* - * Valid offset of SHI output buffer to write. - * When SIMUL bit is set, IBUFPTR can be used instead of OBUFPTR - */ -#define SHI_OBUF_VALID_OFFSET ((shi_read_buf_pointer() + \ - SHI_OUT_PREAMBLE_LENGTH) % SHI_OBUF_FULL_SIZE) -/* Start address of SHI input buffer */ -#define SHI_IBUF_START_ADDR (&NPCX_IBUF(0)) -/* Current address of SHI input buffer */ -#define SHI_IBUF_CUR_ADDR (SHI_IBUF_START_ADDR + shi_read_buf_pointer()) - -/* - * 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 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 - -/* Timeout for glitch case. Make sure it will exceed 8 SPI clocks */ -#define SHI_GLITCH_TIMEOUT_US 10000 - -/* - * 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) - - -#ifdef NPCX_SHI_BYPASS_OVER_256B -/* The boundary which SHI will output invalid data on MISO. */ -#define SHI_BYPASS_BOUNDARY 256 -/* Increase FRAME_START_LENGTH in case shi outputs invalid FRAME_START byte */ -#undef EC_SPI_FRAME_START_LENGTH -#define EC_SPI_FRAME_START_LENGTH 2 -#endif - -/* - * 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 - * sizoef(ec_params_flash_write): 8 - * payload 512 - * - */ -#define SHI_MAX_REQUEST_SIZE 0x220 - -#ifdef NPCX_SHI_BYPASS_OVER_256B -/* Make sure SHI_MAX_RESPONSE_SIZE won't exceed 256 bytes */ -#define SHI_MAX_RESPONSE_SIZE (160 + sizeof(struct ec_host_response)) -BUILD_ASSERT(SHI_MAX_RESPONSE_SIZE <= SHI_BYPASS_BOUNDARY); -#else -#define SHI_MAX_RESPONSE_SIZE 0x220 -#endif - -/* - * 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 shi_state { - /* 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, -#ifdef NPCX_SHI_BYPASS_OVER_256B - /* Keep output buffer as PROCESSING byte until reaching 256B boundary */ - SHI_STATE_WAIT_ALIGNMENT, -#endif - /* Sending response */ - SHI_STATE_SENDING, - /* Received data is valid. */ - SHI_STATE_BAD_RECEIVED_DATA, -}; - -volatile enum shi_state state; - -/* 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 */ - timestamp_t rx_deadline; /* deadline of receiving */ - uint8_t pre_ibufstat; /* Previous IBUFSTAT value */ -#ifdef NPCX_SHI_BYPASS_OVER_256B - uint16_t bytes_in_256b; /* Sent bytes in 256 bytes boundary */ -#endif -} shi_params; - -/* Forward declaration */ -static void shi_reset_prepare(void); -static void shi_bad_received_data(void); -static void shi_fill_out_status(uint8_t status); -static void shi_write_half_outbuf(void); -static void shi_write_first_pkg_outbuf(uint16_t szbytes); -static int shi_read_inbuf_wait(uint16_t szbytes); -static uint8_t shi_read_buf_pointer(void); - -/*****************************************************************************/ -/* V3 protocol layer functions */ - -/** - * Called to send a response back to the host. - * - * Some commands can continue for a while. This function is called by - * host_command when it completes. - * - */ -static void shi_send_response_packet(struct host_packet *pkt) -{ - /* - * 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. - */ - interrupt_disable(); - if (state == SHI_STATE_PROCESSING) { - /* Append our past-end byte, which we reserved space for. */ - ((uint8_t *) pkt->response)[pkt->response_size + 0] = - 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(shi_params.sz_response); -#ifdef NPCX_SHI_BYPASS_OVER_256B - /* - * If response package is over 256B boundary, - * keep sending PROCESSING byte - */ - if (state != SHI_STATE_WAIT_ALIGNMENT) { -#endif - /* Transmit the reply */ - state = SHI_STATE_SENDING; - DEBUG_CPRINTF("SND-"); -#ifdef NPCX_SHI_BYPASS_OVER_256B - } -#endif - } - /* - * 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. - */ - else if (state == SHI_STATE_CNL_RESP_NOT_RDY) { - shi_reset_prepare(); - DEBUG_CPRINTF("END\n"); - } else - DEBUG_CPRINTS("Unexpected state %d in response handler", state); - interrupt_enable(); -} - -void shi_handle_host_package(void) -{ - uint16_t sz_inbuf_int = shi_params.sz_request / SHI_IBUF_HALF_SIZE; - uint16_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; - else { - uint16_t remain_bytes = shi_params.sz_request - - shi_params.sz_received; - - /* Read remaining bytes from input buffer directly */ - if (!shi_read_inbuf_wait(remain_bytes)) - return shi_bad_received_data(); - /* Move to processing state immediately */ - state = SHI_STATE_PROCESSING; - DEBUG_CPRINTF("PRC-"); - } - /* Fill output buffer to indicate we`re processing request */ - shi_fill_out_status(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; - - -#ifdef NPCX_SHI_BYPASS_OVER_256B - /* Move FRAME_START to second byte */ - out_msg[0] = EC_SPI_PROCESSING; - out_msg[1] = EC_SPI_FRAME_START; -#else - /* Put FRAME_START in first byte */ - out_msg[0] = EC_SPI_FRAME_START; -#endif - 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); -} - -/* Parse header for version of spi-protocol */ -static void shi_parse_header(void) -{ - /* We're now inside a transaction */ - state = SHI_STATE_RECEIVING; - DEBUG_CPRINTF("RV-"); - - /* Setup deadline time for receiving */ - shi_params.rx_deadline = get_time(); - shi_params.rx_deadline.val += SHI_CMD_RX_TIMEOUT_US; - - /* Wait for version, command, length bytes */ - if (!shi_read_inbuf_wait(3)) - return shi_bad_received_data(); - - 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 32 bytes, - * we need to modified the algorithm again. - */ - ASSERT(sizeof(*r) < SHI_IBUF_HALF_SIZE); - - /* Wait for the rest of the command header */ - if (!shi_read_inbuf_wait(sizeof(*r) - 3)) - return shi_bad_received_data(); - - /* 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(); - - /* Computing total bytes need to receive */ - shi_params.sz_request = pkt_size; - - shi_handle_host_package(); - } else { - /* Invalid version number */ - return shi_bad_received_data(); - } -} - -/*****************************************************************************/ -/* IC specific low-level driver */ - -/* This routine fills out all SHI output buffer with status byte */ -static void shi_fill_out_status(uint8_t status) -{ - uint8_t start, end; - uint8_t *fill_ptr; - uint8_t *fill_end; - uint8_t *obuf_end; - - /* Disable interrupts in case the interfere by the other interrupts */ - interrupt_disable(); - - /* - * 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_OBUF_VALID_OFFSET; - end = ((start + SHI_OBUF_FULL_SIZE - SHI_OUT_PREAMBLE_LENGTH) - % SHI_OBUF_FULL_SIZE); - - fill_ptr = (uint8_t *)SHI_OBUF_START_ADDR + start; - fill_end = (uint8_t *)SHI_OBUF_START_ADDR + end; - obuf_end = (uint8_t *)SHI_OBUF_START_ADDR + SHI_OBUF_FULL_SIZE; - while (fill_ptr != fill_end) { - *(fill_ptr++) = status; - if (fill_ptr == obuf_end) - fill_ptr = (uint8_t *)SHI_OBUF_START_ADDR; - } - - /* End of critical section */ - interrupt_enable(); -} - -#ifdef NPCX_SHI_V2 - /* - * This routine configures at which level the Input Buffer Half Full 2(IBHF2)) - * event triggers an interrupt to core. - */ -static void shi_sec_ibf_int_enable(int enable) -{ - if (enable) { - /* Setup IBUFLVL2 threshold and enable it */ - SET_BIT(NPCX_SHICFG5, NPCX_SHICFG5_IBUFLVL2DIS); - SET_FIELD(NPCX_SHICFG5, NPCX_SHICFG5_IBUFLVL2, - SHI_IBUFLVL2_THRESHOLD); - CLEAR_BIT(NPCX_SHICFG5, NPCX_SHICFG5_IBUFLVL2DIS); - /* Enable IBHF2 event */ - SET_BIT(NPCX_EVENABLE2, NPCX_EVENABLE2_IBHF2EN); - } else { - /* Disable IBHF2 event first */ - CLEAR_BIT(NPCX_EVENABLE2, NPCX_EVENABLE2_IBHF2EN); - /* Disable IBUFLVL2 and set threshold back to zero */ - SET_BIT(NPCX_SHICFG5, NPCX_SHICFG5_IBUFLVL2DIS); - SET_FIELD(NPCX_SHICFG5, NPCX_SHICFG5_IBUFLVL2, 0); - } -} -#else -/* - * This routine makes sure it's valid transaction or glitch on CS bus. - */ -static int shi_is_cs_glitch(void) -{ - timestamp_t deadline; - - deadline.val = get_time().val + SHI_GLITCH_TIMEOUT_US; - /* - * If input buffer pointer is no changed after timeout, it will - * return true - */ - while (shi_params.pre_ibufstat == shi_read_buf_pointer()) - if (timestamp_expired(deadline, NULL)) - return 1; - /* valid package */ - return 0; -} -#endif - -/* - * This routine write SHI next half output buffer from msg buffer - */ -static void shi_write_half_outbuf(void) -{ - const uint8_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 write SHI output buffer from msg buffer over halt of it. - * It make sure we have enought time to handle next operations. - */ -static void shi_write_first_pkg_outbuf(uint16_t szbytes) -{ - uint8_t size, offset; - uint8_t *obuf_ptr; - uint8_t *obuf_end; - uint8_t *msg_ptr; - -#ifdef NPCX_SHI_BYPASS_OVER_256B - /* - * If response package is across 256 bytes boundary, - * bypass needs to extend PROCESSING bytes after reaching the boundary. - */ - if (shi_params.bytes_in_256b + SHI_OBUF_FULL_SIZE + szbytes - > SHI_BYPASS_BOUNDARY) { - state = SHI_STATE_WAIT_ALIGNMENT; - /* Set pointer of output buffer to the start address */ - shi_params.tx_buf = SHI_OBUF_START_ADDR; - DEBUG_CPRINTF("WAT-"); - return; - } -#endif - - /* Start writing at our current OBUF position */ - offset = SHI_OBUF_VALID_OFFSET; - obuf_ptr = (uint8_t *)SHI_OBUF_START_ADDR + offset; - msg_ptr = shi_params.tx_msg; - - /* Fill up to OBUF mid point, or OBUF end */ - size = MIN(SHI_OBUF_HALF_SIZE - (offset % 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 for later accounting */ - shi_params.sz_sending += size; - - /* Write data to beginning of OBUF if we've reached the end */ - if (obuf_ptr == SHI_OBUF_FULL_ADDR) - obuf_ptr = (uint8_t *)SHI_OBUF_START_ADDR; - - /* 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; -} - -/* 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); -} - -/* - * This routine read SHI input buffer to msg buffer until - * we have received a certain number of bytes - */ -static int shi_read_inbuf_wait(uint16_t szbytes) -{ - uint16_t i; - - /* Copy data to msg buffer from input buffer */ - for (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 == SHI_IBUF_CUR_ADDR) - if (timestamp_expired(shi_params.rx_deadline, NULL)) - return 0; - /* Restore data to msg buffer */ - *(shi_params.rx_msg++) = *(shi_params.rx_buf++); - } - return 1; -} - -/* Read pointer of input or output buffer by consecutive reading */ -static uint8_t shi_read_buf_pointer(void) -{ - uint8_t stat; - /* Wait for two consecutive equal values are read */ - do { - stat = NPCX_IBUFSTAT; - } while (stat != NPCX_IBUFSTAT); - - return stat; -} - -/* This routine handles shi recevied unexcepted data */ -static void shi_bad_received_data(void) -{ - uint16_t i; - - /* State machine mismatch, timeout, or protocol we can't handle. */ - shi_fill_out_status(EC_SPI_RX_BAD_DATA); - state = SHI_STATE_BAD_RECEIVED_DATA; - - CPRINTF("BAD-"); - CPRINTF("in_msg=["); - for (i = 0; i < shi_params.sz_received; i++) - CPRINTF("%02x ", in_msg[i]); - CPRINTF("]\n"); - - /* Reset shi's state machine for error recovery */ - shi_reset_prepare(); - - DEBUG_CPRINTF("END\n"); -} - -/* - * Avoid spamming the console with prints every IBF / IBHF interrupt, if - * we find ourselves in an unexpected state. - */ -static int last_error_state = -1; - -static void log_unexpected_state(char *isr_name) -{ -#if !(DEBUG_SHI) - if (state != last_error_state) - CPRINTS("Unexpected state %d in %s ISR", state, isr_name); -#endif - last_error_state = state; -} - -static void shi_handle_cs_assert(void) -{ - /* If not enabled, ignore glitches on SHI_CS_L */ - if (state == SHI_STATE_DISABLED) - return; - - /* SHI V2 module filters cs glitch by hardware automatically */ -#ifndef NPCX_SHI_V2 - /* - * IBUFSTAT resets on the 7th clock cycle after CS assertion, which - * may not have happened yet. We use NPCX_IBUFSTAT for calculating - * buffer fill depth, so make sure it's valid before proceeding. - */ - if (shi_is_cs_glitch()) { - CPRINTS("ERR-GTH"); - shi_reset_prepare(); - DEBUG_CPRINTF("END\n"); - return; - } -#endif - - /* 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 */ - CPRINTS("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. - */ - NPCX_EVSTAT = 1 << NPCX_EVSTAT_EOR; - - /* Do not deep sleep during SHI transaction */ - disable_sleep(SLEEP_MASK_SPI); - -#ifndef NPCX_SHI_V2 - /* - * Enable SHI interrupt - we will either succeed to parse our host - * command or reset on failure from here. - */ - task_enable_irq(NPCX_IRQ_SHI); - - /* Read first three bytes to parse which protocol is receiving */ - shi_parse_header(); -#endif -} - -/* This routine handles all interrupts of this module */ -void shi_int_handler(void) -{ - uint8_t stat_reg; -#ifdef NPCX_SHI_V2 - uint8_t stat2_reg; -#endif - - /* Read status register and clear interrupt status early */ - stat_reg = NPCX_EVSTAT; - NPCX_EVSTAT = stat_reg; -#ifdef NPCX_SHI_V2 - stat2_reg = NPCX_EVSTAT2; - - /* SHI CS pin is asserted in EVSTAT2 */ - if (IS_BIT_SET(stat2_reg, NPCX_EVSTAT2_CSNFE)) { - /* clear CSNFE bit */ - NPCX_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(NPCX_SHICFG2, NPCX_SHICFG2_BUSY)) { - DEBUG_CPRINTF("CSNB-"); - return; - } - shi_handle_cs_assert(); - } -#endif - - /* - * End of data for read/write transaction. ie SHI_CS is deasserted. - * Host completed or aborted transaction - */ -#ifdef NPCX_SHI_V2 - /* - * 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_reg, NPCX_EVSTAT2_CSNRE)) { - /* Clear pending bit of CSNRE */ - NPCX_EVSTAT2 = BIT(NPCX_EVSTAT2_CSNRE); -#else - if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_EOR)) { -#endif - /* - * We're not in proper state. - * Mark not ready to abort next transaction - */ - DEBUG_CPRINTF("CSH-"); - /* - * 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(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. - */ - task_disable_irq(NPCX_IRQ_SHI); - - 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) -#ifdef NPCX_SHI_V2 - log_unexpected_state("CSNRE"); -#else - log_unexpected_state("IBEOR"); -#endif - - /* reset SHI and prepare to next transaction again */ - shi_reset_prepare(); - DEBUG_CPRINTF("END\n"); - return; - } - - /* - * Indicate input/output buffer pointer reaches the half buffer size. - * Transaction is processing. - */ - if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_IBHF)) { - if (state == SHI_STATE_RECEIVING) { - /* Read data from input to msg buffer */ - shi_read_half_inbuf(); - return shi_handle_host_package(); - } else if (state == SHI_STATE_SENDING) { - /* Write data from msg buffer to output buffer */ - if (shi_params.tx_buf == SHI_OBUF_START_ADDR + - SHI_OBUF_FULL_SIZE) { - /* Write data from bottom address again */ - shi_params.tx_buf = SHI_OBUF_START_ADDR; - return shi_write_half_outbuf(); - } else /* ignore it */ - return; - } else if (state == SHI_STATE_PROCESSING) { - /* Wait for host to handle request */ - } -#ifdef NPCX_SHI_BYPASS_OVER_256B - else if (state == SHI_STATE_WAIT_ALIGNMENT) { - /* - * If pointer of output buffer will reach 256 bytes - * boundary soon, start to fill response data. - */ - if (shi_params.bytes_in_256b == SHI_BYPASS_BOUNDARY - - SHI_OBUF_FULL_SIZE) { - state = SHI_STATE_SENDING; - DEBUG_CPRINTF("SND-"); - return shi_write_half_outbuf(); - } - } -#endif - else - /* Unexpected status */ - log_unexpected_state("IBHF"); - } - -#ifdef NPCX_SHI_V2 - /* - * The size of input buffer reaches the size of - * protocol V3 header(=8) after CS asserted. - */ - if (IS_BIT_SET(stat2_reg, NPCX_EVSTAT2_IBHF2)) { - /* Clear IBHF2 */ - NPCX_EVSTAT2 = BIT(NPCX_EVSTAT2_IBHF2); - DEBUG_CPRINTF("HDR-"); - /* Disable second IBF interrupt and start to parse header */ - shi_sec_ibf_int_enable(0); - shi_parse_header(); - } -#endif - - /* - * Indicate input/output buffer pointer reaches the full buffer size. - * Transaction is processing. - */ - if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_IBF)) { -#ifdef NPCX_SHI_BYPASS_OVER_256B - /* Record the sent bytes within 256B boundary */ - shi_params.bytes_in_256b = (shi_params.bytes_in_256b + - SHI_OBUF_FULL_SIZE) % SHI_BYPASS_BOUNDARY; -#endif - 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 = SHI_IBUF_START_ADDR; - return shi_handle_host_package(); - } else if (state == SHI_STATE_SENDING) - /* Write data from msg buffer to output buffer */ - if (shi_params.tx_buf == SHI_OBUF_START_ADDR + - SHI_OBUF_HALF_SIZE) - return shi_write_half_outbuf(); - else /* ignore it */ - return; - else if (state == SHI_STATE_PROCESSING -#ifdef NPCX_SHI_BYPASS_OVER_256B - || state == SHI_STATE_WAIT_ALIGNMENT -#endif - ) - /* Wait for host handles request */ - return; - else - /* Unexpected status */ - log_unexpected_state("IBF"); - } -} -DECLARE_IRQ(NPCX_IRQ_SHI, shi_int_handler, 2); - -/* Handle an CS assert event on the SHI_CS_L pin */ -void shi_cs_event(enum gpio_signal signal) -{ -#ifdef NPCX_SHI_V2 - /* - * New SHI module handles the CS low event in the SHI module interrupt - * handler (checking CSNFE bit) instead of in GPIO(MIWU) interrupt - * handler. But there is still a need to configure the MIWU to generate - * event to wake up EC from deep sleep. Immediately return to bypass - * the CS low interrupt event from MIWU module. - */ - return; -#else - shi_handle_cs_assert(); -#endif - -} - -/*****************************************************************************/ -/* Hook functions for chipset and initialization */ - -/* - * Reset SHI bus and prepare next transaction - * Please make sure it is executed when there're no spi transactions - */ -static void shi_reset_prepare(void) -{ - uint16_t i; - - /* We no longer care about SHI interrupts, so disable them. */ - task_disable_irq(NPCX_IRQ_SHI); - - /* Disable SHI unit to clear all status bits */ - CLEAR_BIT(NPCX_SHICFG1, NPCX_SHICFG1_EN); - - /* Initialize parameters of next transaction */ - shi_params.rx_msg = in_msg; - shi_params.tx_msg = out_msg; - shi_params.rx_buf = SHI_IBUF_START_ADDR; - shi_params.tx_buf = SHI_OBUF_HALF_ADDR; - shi_params.sz_received = 0; - shi_params.sz_sending = 0; - shi_params.sz_request = 0; - shi_params.sz_response = 0; -#ifdef NPCX_SHI_BYPASS_OVER_256B - shi_params.bytes_in_256b = 0; -#endif - /* Record last IBUFSTAT for glitch case */ - shi_params.pre_ibufstat = shi_read_buf_pointer(); - - /* - * Fill output buffer to indicate we`re - * ready to receive next transaction. - */ - for (i = 1; i < SHI_OBUF_FULL_SIZE; i++) - NPCX_OBUF(i) = EC_SPI_RECEIVING; - NPCX_OBUF(0) = EC_SPI_OLD_READY; - - /* Enable SHI & WEN functionality */ - NPCX_SHICFG1 = 0x85; - - /* Ready to receive */ - state = SHI_STATE_READY_TO_RECV; - last_error_state = -1; - - /* Setup second IBF interrupt and enable SHI's interrupt */ -#ifdef NPCX_SHI_V2 - shi_sec_ibf_int_enable(1); - task_enable_irq(NPCX_IRQ_SHI); -#endif - - /* Allow deep sleep at the end of SHI transaction */ - enable_sleep(SLEEP_MASK_SPI); - - DEBUG_CPRINTF("RDY-"); -} - -static void shi_enable(void) -{ - int gpio_flags; - - shi_reset_prepare(); - - /* Ensure SHI_CS_L interrupt is disabled */ - gpio_disable_interrupt(GPIO_SHI_CS_L); - - /* Enable PU, if requested */ - gpio_flags = GPIO_INPUT | GPIO_INT_F_FALLING; -#ifdef NPCX_SHI_CS_PU - gpio_flags |= GPIO_PULL_UP; -#endif - gpio_set_flags(GPIO_SHI_CS_L, gpio_flags); - - /* - * Mux SHI related pins - * SHI_SDI SHI_SDO SHI_CS# SHI_SCLK are selected to device pins - */ - SET_BIT(NPCX_DEVALT(ALT_GROUP_C), NPCX_DEVALTC_SHI_SL); - - task_clear_pending_irq(NPCX_IRQ_SHI); - - /* Enable SHI_CS_L interrupt */ - gpio_enable_interrupt(GPIO_SHI_CS_L); - - /* - * If CS is already asserted prior to enabling our GPIO interrupt then - * we have missed the falling edge and we need to handle the - * deassertion interrupt. - */ - task_enable_irq(NPCX_IRQ_SHI); -} -#ifdef CONFIG_CHIPSET_RESUME_INIT_HOOK -DECLARE_HOOK(HOOK_CHIPSET_RESUME_INIT, shi_enable, HOOK_PRIO_DEFAULT); -#else -DECLARE_HOOK(HOOK_CHIPSET_RESUME, shi_enable, HOOK_PRIO_DEFAULT); -#endif - -static void shi_reenable_on_sysjump(void) -{ -#if !(DEBUG_SHI) - if (system_jumped_late() && chipset_in_state(CHIPSET_STATE_ON)) -#endif - shi_enable(); -} -/* Call hook after chipset sets initial power state */ -DECLARE_HOOK(HOOK_INIT, - shi_reenable_on_sysjump, - HOOK_PRIO_INIT_CHIPSET + 1); - -/* Disable SHI bus */ -static void shi_disable(void) -{ - state = SHI_STATE_DISABLED; - - task_disable_irq(NPCX_IRQ_SHI); - - /* Disable SHI_CS_L interrupt */ - gpio_disable_interrupt(GPIO_SHI_CS_L); - - /* Restore SHI_CS_L back to default state */ - gpio_reset(GPIO_SHI_CS_L); - - /* - * Mux SHI related pins - * SHI_SDI SHI_SDO SHI_CS# SHI_SCLK are selected to GPIO - */ - CLEAR_BIT(NPCX_DEVALT(ALT_GROUP_C), NPCX_DEVALTC_SHI_SL); - - /* - * 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); -} -#ifdef CONFIG_CHIPSET_RESUME_INIT_HOOK -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND_COMPLETE, shi_disable, HOOK_PRIO_DEFAULT); -#else -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, shi_disable, HOOK_PRIO_DEFAULT); -#endif -DECLARE_HOOK(HOOK_SYSJUMP, shi_disable, HOOK_PRIO_DEFAULT); - -static void shi_init(void) -{ - /* Power on SHI module first */ - CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5), NPCX_PWDWN_CTL5_SHI_PD); - -#ifdef NPCX_SHI_V2 - /* Enable SHI module Version 2 */ - SET_BIT(NPCX_DEVALT(ALT_GROUP_F), NPCX_DEVALTF_SHI_NEW); -#endif - - /* - * 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 - */ - NPCX_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 - */ - NPCX_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 - */ - NPCX_EVENABLE = 0x1C; - -#ifdef NPCX_SHI_V2 - /* - * 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 - */ - NPCX_EVENABLE2 = 0x06; -#endif - - /* Clear SHI events status register */ - NPCX_EVSTAT = 0XFF; -} -/* Call hook before chipset sets initial power state and calls resume hooks */ -DECLARE_HOOK(HOOK_INIT, shi_init, HOOK_PRIO_INIT_CHIPSET - 1); - -/** - * Get protocol information - */ -static enum ec_status shi_get_protocol_info(struct host_cmd_handler_args *args) -{ - struct ec_response_get_protocol_info *r = args->response; - - memset(r, 0, sizeof(*r)); - r->protocol_versions = BIT(3); - r->max_request_packet_size = SHI_MAX_REQUEST_SIZE; - r->max_response_packet_size = SHI_MAX_RESPONSE_SIZE; - r->flags = EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED; - - args->response_size = sizeof(*r); - - return EC_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, shi_get_protocol_info, -EC_VER_MASK(0)); |