From 16f1f97ff6d453191689fa4cda61aae34f9e2c58 Mon Sep 17 00:00:00 2001 From: tim Date: Mon, 22 Mar 2021 08:23:08 +0800 Subject: zephyr/drivers: it8xxx2: add SPI host interface driver Add spi host interface driver which is required to communicate with the EC when the CPU is the ARM processor. BUG=b:185202623 BRANCH=none TEST=replaced board hayato's EC with it81202_evb and the host command can be received and responded. Signed-off-by: tim Change-Id: I25ccdd0b29bf46b6a4ad451150f63b47dc9d3b97 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2813841 Reviewed-by: Jack Rosenthal Reviewed-by: Denis Brockus Commit-Queue: Jack Rosenthal --- zephyr/drivers/cros_shi/CMakeLists.txt | 1 + zephyr/drivers/cros_shi/Kconfig | 8 + zephyr/drivers/cros_shi/cros_shi_it8xxx2.c | 342 +++++++++++++++++++++++++++++ 3 files changed, 351 insertions(+) create mode 100644 zephyr/drivers/cros_shi/cros_shi_it8xxx2.c diff --git a/zephyr/drivers/cros_shi/CMakeLists.txt b/zephyr/drivers/cros_shi/CMakeLists.txt index 6bc531b185..f0b3c8bb5a 100644 --- a/zephyr/drivers/cros_shi/CMakeLists.txt +++ b/zephyr/drivers/cros_shi/CMakeLists.txt @@ -2,4 +2,5 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +zephyr_library_sources_ifdef(CONFIG_CROS_SHI_IT8XXX2 cros_shi_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_CROS_SHI_NPCX cros_shi_npcx.c) diff --git a/zephyr/drivers/cros_shi/Kconfig b/zephyr/drivers/cros_shi/Kconfig index 0d46fab3be..54c393e00f 100644 --- a/zephyr/drivers/cros_shi/Kconfig +++ b/zephyr/drivers/cros_shi/Kconfig @@ -33,3 +33,11 @@ config CROS_SHI_NPCX_DEBUG print the debug messages for SHI module endif # CROS_SHI_NPCX + +config CROS_SHI_IT8XXX2 + bool "ITE it81202 spi host interface driver for Zephyr" + depends on SOC_FAMILY_RISCV_ITE && AP_ARM + default y if PLATFORM_EC_HOSTCMD + help + This option enables spi host interface driver which is required to + communicate with the EC when the CPU is the ARM processor. diff --git a/zephyr/drivers/cros_shi/cros_shi_it8xxx2.c b/zephyr/drivers/cros_shi/cros_shi_it8xxx2.c new file mode 100644 index 0000000000..1fd3cc4e96 --- /dev/null +++ b/zephyr/drivers/cros_shi/cros_shi_it8xxx2.c @@ -0,0 +1,342 @@ +/* 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 ite_it8xxx2_cros_shi + +#include +#include +#include +#include +#include +#include + +#include "console.h" +#include "host_command.h" + +/* Console output macros */ +#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_SPI, format, ## args) + +LOG_MODULE_REGISTER(cros_shi, LOG_LEVEL_ERR); + +#define SPI_RX_MAX_FIFO_SIZE 256 +#define SPI_TX_MAX_FIFO_SIZE 256 + +#define EC_SPI_PREAMBLE_LENGTH 4 +#define EC_SPI_PAST_END_LENGTH 4 + +/* Max data size for a version 3 request/response packet. */ +#define SPI_MAX_REQUEST_SIZE SPI_RX_MAX_FIFO_SIZE +#define SPI_MAX_RESPONSE_SIZE (SPI_TX_MAX_FIFO_SIZE - \ + EC_SPI_PREAMBLE_LENGTH - EC_SPI_PAST_END_LENGTH) + +static const uint8_t out_preamble[EC_SPI_PREAMBLE_LENGTH] = { + EC_SPI_PROCESSING, + EC_SPI_PROCESSING, + EC_SPI_PROCESSING, + /* This is the byte which matters */ + EC_SPI_FRAME_START, +}; + +/* Store read and write data buffer */ +static uint8_t in_msg[SPI_RX_MAX_FIFO_SIZE] __aligned(4); +static uint8_t out_msg[SPI_TX_MAX_FIFO_SIZE] __aligned(4); + +/* Parameters used by host protocols */ +static struct host_packet spi_packet; + +enum shi_state_machine { + /* Ready to receive next request */ + SPI_STATE_READY_TO_RECV, + /* Receiving request */ + SPI_STATE_RECEIVING, + /* Processing request */ + SPI_STATE_PROCESSING, + /* Received bad data */ + SPI_STATE_RX_BAD, + + SPI_STATE_COUNT, +}; + +static enum shi_state_machine shi_state; + +static const int spi_response_state[] = { + [SPI_STATE_READY_TO_RECV] = EC_SPI_OLD_READY, + [SPI_STATE_RECEIVING] = EC_SPI_RECEIVING, + [SPI_STATE_PROCESSING] = EC_SPI_PROCESSING, + [SPI_STATE_RX_BAD] = EC_SPI_RX_BAD_DATA, +}; +BUILD_ASSERT(ARRAY_SIZE(spi_response_state) == SPI_STATE_COUNT); + +static void spi_set_state(int state) +{ + /* SPI slave state machine */ + shi_state = state; + /* Response spi slave state */ + IT83XX_SPI_SPISRDR = spi_response_state[state]; +} + +static void reset_rx_fifo(void) +{ + /* End Rx FIFO access */ + IT83XX_SPI_TXRXFAR = 0x00; + /* Rx FIFO reset and count monitor reset */ + IT83XX_SPI_FCR = IT83XX_SPI_RXFR | IT83XX_SPI_RXFCMR; +} + +/* This routine handles spi received unexcepted data */ +static void spi_bad_received_data(int count) +{ + /* State machine mismatch, timeout, or protocol we can't handle. */ + spi_set_state(SPI_STATE_RX_BAD); + /* End CPU access Rx FIFO, so it can clock in bytes from AP again. */ + IT83XX_SPI_TXRXFAR = 0; + + CPRINTS("SPI rx bad data"); + CPRINTF("in_msg=["); + for (int i = 0; i < count; i++) + CPRINTF("%02x ", in_msg[i]); + CPRINTF("]\n"); +} + +static void spi_response_host_data(uint8_t *out_msg_addr, int tx_size) +{ + /* Tx FIFO reset and count monitor reset */ + IT83XX_SPI_TXFCR = IT83XX_SPI_TXFR | IT83XX_SPI_TXFCMR; + /* CPU Tx FIFO1 and FIFO2 access */ + IT83XX_SPI_TXRXFAR = IT83XX_SPI_CPUTFA; + + for (int i = 0; i < tx_size; i += 4) { + /* Write response data from out_msg buffer to Tx FIFO */ + IT83XX_SPI_CPUWTFDB0 = *(uint32_t *)(out_msg_addr + i); + } + + /* + * After writing data to Tx FIFO is finished, this bit will + * be to indicate the SPI slave controller. + */ + IT83XX_SPI_TXFCR = IT83XX_SPI_TXFS; + /* End Tx FIFO access */ + IT83XX_SPI_TXRXFAR = 0; + /* SPI slave read Tx FIFO */ + IT83XX_SPI_FCR = IT83XX_SPI_SPISRTXF; +} + +/* + * 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 spi_send_response_packet(struct host_packet *pkt) +{ + int tx_size; + + if (shi_state != SPI_STATE_PROCESSING) { + CPRINTS("The request data is not processing."); + return; + } + + /* Append our past-end byte, which we reserved space for. */ + for (int i = 0; i < EC_SPI_PAST_END_LENGTH; i++) { + ((uint8_t *)pkt->response)[pkt->response_size + i] + = EC_SPI_PAST_END; + } + + tx_size = pkt->response_size + EC_SPI_PREAMBLE_LENGTH + + EC_SPI_PAST_END_LENGTH; + + /* Transmit the reply */ + spi_response_host_data(out_msg, tx_size); +} + +/* Store request data from Rx FIFO to in_msg buffer */ +static void spi_host_request_data(uint8_t *in_msg_addr, int count) +{ + /* CPU Rx FIFO1 access */ + IT83XX_SPI_TXRXFAR = IT83XX_SPI_CPURXF1A; + /* + * In spi_parse_header, the request data will separate to + * write in_msg buffer so we cannot set CPU to end accessing + * Rx FIFO in this function. We will set IT83XX_SPI_TXRXFAR = 0 + * in reset_rx_fifo. + */ + + for (int i = 0; i < count; i += 4) { + /* Get data from master to buffer */ + *(uint32_t *)(in_msg_addr + i) = IT83XX_SPI_RXFRDRB0; + } +} + +/* Parse header for version of spi-protocol */ +static void spi_parse_header(void) +{ + struct ec_host_request *r = (struct ec_host_request *)in_msg; + + /* Store request data from Rx FIFO to in_msg buffer */ + spi_host_request_data(in_msg, sizeof(*r)); + + /* Protocol version 3 */ + if (in_msg[0] == EC_HOST_REQUEST_VERSION) { + int pkt_size; + + /* Check how big the packet should be */ + pkt_size = host_request_expected_size(r); + + if (pkt_size == 0 || pkt_size > sizeof(in_msg)) { + spi_bad_received_data(pkt_size); + return; + } + + /* Store request data from Rx FIFO to in_msg buffer */ + spi_host_request_data(in_msg + sizeof(*r), + pkt_size - sizeof(*r)); + + /* Set up parameters for host request */ + spi_packet.send_response = spi_send_response_packet; + spi_packet.request = in_msg; + spi_packet.request_temp = NULL; + spi_packet.request_max = sizeof(in_msg); + spi_packet.request_size = pkt_size; + + /* Response must start with the preamble */ + memcpy(out_msg, out_preamble, sizeof(out_preamble)); + + spi_packet.response = out_msg + EC_SPI_PREAMBLE_LENGTH; + /* Reserve space for frame start and trailing past-end byte */ + spi_packet.response_max = SPI_MAX_RESPONSE_SIZE; + spi_packet.response_size = 0; + spi_packet.driver_result = EC_RES_SUCCESS; + + /* Move to processing state */ + spi_set_state(SPI_STATE_PROCESSING); + + /* Go to common-layer to handle request */ + host_packet_receive(&spi_packet); + } else { + /* Invalid version number */ + CPRINTS("Invalid version number"); + spi_bad_received_data(1); + } +} + +static void shi_ite_int_handler(const void *arg) +{ + /* + * The status of SPI end detection interrupt bit is set, it + * means that host command parse has been completed and AP + * has received the last byte which is EC_SPI_PAST_END from + * EC responded data, then AP ended the transaction. + */ + if (IT83XX_SPI_ISR & IT83XX_SPI_ENDDETECTINT) { + /* Ready to receive */ + spi_set_state(SPI_STATE_READY_TO_RECV); + /* + * Once there is no SPI active, enable idle task deep + * sleep bit of SPI in S3 or lower. + * TODO(b:185176098): enable_sleep(SLEEP_MASK_SPI); + */ + + /* CS# is deasserted, so write clear all slave status */ + IT83XX_SPI_ISR = 0xff; + } + /* + * The status of Rx valid length interrupt bit is set that + * indicates reached target count(IT83XX_SPI_FTCB1R, + * IT83XX_SPI_FTCB0R) and the length field of the host + * requested data. + */ + if (IT83XX_SPI_RX_VLISR & IT83XX_SPI_RVLI) { + /* write clear slave status */ + IT83XX_SPI_RX_VLISR = IT83XX_SPI_RVLI; + /* Parse header for version of spi-protocol */ + spi_parse_header(); + } + +} + +static int cros_shi_ite_init(const struct device *dev) +{ + /* Set FIFO data target count */ + struct ec_host_request cmd_head; + + /* + * Target count means the size of host request. + * And plus extra 4 bytes because the CPU accesses FIFO base on + * word. If host requested data length is one byte, we need to + * align the data length to 4 bytes. + */ + int target_count = sizeof(cmd_head) + 4; + /* Offset of data_len member of host request. */ + int offset = (char *)&cmd_head.data_len - (char *)&cmd_head; + + IT83XX_SPI_FTCB1R = (target_count >> 8) & 0xff; + IT83XX_SPI_FTCB0R = target_count & 0xff; + /* + * The register setting can capture the length field of host + * request. + */ + IT83XX_SPI_TCCB1 = (offset >> 8) & 0xff; + IT83XX_SPI_TCCB0 = offset & 0xff; + + /* + * Memory controller configuration register 3. + * bit6 : SPI pin function select (0b:Enable, 1b:Mask) + */ + IT83XX_GCTRL_MCCR3 |= IT83XX_GCTRL_SPISLVPFE; + /* Set unused blocked byte */ + IT83XX_SPI_HPR2 = 0x00; + /* Rx valid length interrupt enabled */ + IT83XX_SPI_RX_VLISMR &= ~IT83XX_SPI_RVLIM; + /* + * General control register2 + * bit4 : Rx FIFO2 will not be overwrited once it's full. + * bit3 : Rx FIFO1 will not be overwrited once it's full. + * bit0 : Rx FIFO1/FIFO2 will reset after each CS_N goes high. + */ + IT83XX_SPI_GCR2 = IT83XX_SPI_RXF2OC | IT83XX_SPI_RXF1OC + | IT83XX_SPI_RXFAR; + /* + * Interrupt mask register (0b:Enable, 1b:Mask) + * bit5 : Rx byte reach interrupt mask + * bit2 : SPI end detection interrupt mask + */ + IT83XX_SPI_IMR &= ~IT83XX_SPI_EDIM; + /* Reset fifo and prepare to for next transaction */ + reset_rx_fifo(); + /* Ready to receive */ + spi_set_state(SPI_STATE_READY_TO_RECV); + /* Interrupt status register(write one to clear) */ + IT83XX_SPI_ISR = 0xff; + /* SPI slave controller enable (after settings are ready) */ + IT83XX_SPI_SPISGCR = IT83XX_SPI_SPISCEN; + + /* Enable SPI slave interrupt */ + IRQ_CONNECT(DT_INST_IRQN(0), 0, shi_ite_int_handler, 0, 0); + irq_enable(DT_INST_IRQN(0)); + + return 0; +} +SYS_INIT(cros_shi_ite_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +/* Get protocol information */ +enum ec_status spi_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 = SPI_MAX_REQUEST_SIZE; + r->max_response_packet_size = SPI_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, + spi_get_protocol_info, + EC_VER_MASK(0)); -- cgit v1.2.1