blob: c172e9fca956c8121e1fbc7ae8283ff48c02a2d3 [file] [log] [blame]
/* Copyright 2019 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* SPI driver for Chrome EC.
*
* This uses FIFO mode to handle transmission and reception.
*/
#include "chipset.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "intc.h"
#include "registers.h"
#include "spi.h"
#include "system.h"
#include "task.h"
#include "util.h"
/* Console output macros */
#define CPRINTS(format, args...) cprints(CC_SPI, format, ##args)
#define CPRINTF(format, args...) cprintf(CC_SPI, format, ##args)
#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 spi_peripheral_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,
} spi_peripheral_state;
static const int spi_response_state[] = {
[SPI_STATE_READY_TO_RECV] = EC_SPI_RX_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 peripheral state machine */
spi_peripheral_state = state;
/* Response spi peripheral 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)
{
int i;
/* 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 (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)
{
int i;
/* 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 (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 peripheral.
*/
IT83XX_SPI_TXFCR = IT83XX_SPI_TXFS;
/* End Tx FIFO access */
IT83XX_SPI_TXRXFAR = 0;
/* SPI peripheral 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 i, tx_size;
if (spi_peripheral_state != SPI_STATE_PROCESSING) {
CPRINTS("The request data is not processing.");
return;
}
/* Append our past-end byte, which we reserved space for. */
for (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)
{
int i;
/* 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 (i = 0; i < count; i += 4)
/* Get data from controller 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))
return spi_bad_received_data(pkt_size);
/* 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, EC_SPI_PREAMBLE_LENGTH);
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");
return spi_bad_received_data(1);
}
}
void spi_event(enum gpio_signal signal)
{
if (chipset_in_state(CHIPSET_STATE_ON)) {
/* EC has started receiving the request from the AP */
spi_set_state(SPI_STATE_RECEIVING);
/* Disable idle task deep sleep bit of SPI in S0. */
disable_sleep(SLEEP_MASK_SPI);
}
}
void spi_peripheral_int_handler(void)
{
if (IS_ENABLED(CONFIG_BOOTBLOCK) &&
(IT83XX_SPI_ISR & IT83XX_SPI_RX_FIFO_FULL) &&
(IT83XX_SPI_EMMCBMR & IT83XX_SPI_EMMCABM)) {
spi_host_request_data(in_msg, 128);
/* End CPU access RX FIFO */
IT83XX_SPI_TXRXFAR = 0;
/* Write to clear interrupt status */
IT83XX_SPI_ISR = 0xff;
/*
* Handle eMMC CMD0:
* GO_IDLE_STATE, GO_PRE_IDLE_STATE, and BOOT_INITIATION
*/
spi_emmc_cmd0_isr((uint32_t *)in_msg);
return;
}
/*
* 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.
*/
enable_sleep(SLEEP_MASK_SPI);
/* CS# is deasserted, so write clear all peripheral 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 peripheral status */
IT83XX_SPI_RX_VLISR = IT83XX_SPI_RVLI;
/* Parse header for version of spi-protocol */
spi_parse_header();
}
/* Clear the interrupt status */
task_clear_pending_irq(IT83XX_IRQ_SPI_PERIPHERAL);
}
static void spi_init(void)
{
/* 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;
/* Set SPI pins to alternate function */
gpio_config_module(MODULE_SPI, 1);
/*
* 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 peripheral enable (after settings are ready) */
IT83XX_SPI_SPISGCR = IT83XX_SPI_SPISCEN;
/* Enable SPI peripheral interrupt */
task_clear_pending_irq(IT83XX_IRQ_SPI_PERIPHERAL);
task_enable_irq(IT83XX_IRQ_SPI_PERIPHERAL);
/* Enable SPI chip select pin interrupt */
gpio_clear_pending_interrupt(GPIO_SPI0_CS);
gpio_enable_interrupt(GPIO_SPI0_CS);
}
DECLARE_HOOK(HOOK_INIT, spi_init, HOOK_PRIO_INIT_SPI);
/* reset peripheral SPI module */
static void spi_reset(void)
{
/*
* Reset SPI module before sysjump. New FW images (RO/RW) will
* re-configure it.
*/
IT83XX_GCTRL_RSTC5 |= BIT(1);
}
DECLARE_HOOK(HOOK_SYSJUMP, spi_reset, HOOK_PRIO_DEFAULT);
#if defined(SECTION_IS_RO) && defined(CONFIG_BOOTBLOCK)
/* AP has booted */
void emmc_ap_jump_to_bl(enum gpio_signal signal)
{
/* Transmission completed. Set SPI pin mux to AP communication mode */
IT83XX_GCTRL_PIN_MUX0 &= ~BIT(7);
/* Reset and re-initialize SPI module to communication mode */
spi_reset();
spi_init();
/* Disable interrupt of detection of AP's BOOTBLOCK_EN_L */
gpio_disable_interrupt(GPIO_BOOTBLOCK_EN_L);
enable_sleep(SLEEP_MASK_EMMC);
CPRINTS("eMMC emulation disabled. AP Jumped to BL");
}
#endif
/* Get protocol information */
static 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));
enum ec_status spi_get_protocol_info(struct host_cmd_handler_args *args)
{
return _spi_get_protocol_info(args);
}