/* * 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));