coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/stm32/spi.c

743 lines
21 KiB
C

/*
* Copyright 2013 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.
*
* SPI driver for Chrome EC.
*
* This uses DMA to handle transmission and reception.
*/
#include "chipset.h"
#include "clock.h"
#include "console.h"
#include "dma.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "link_defs.h"
#include "registers.h"
#include "spi.h"
#include "stm32-dma.h"
#include "system.h"
#include "timer.h"
#include "util.h"
/* Console output macros */
#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)
/* SPI FIFO registers */
#ifdef CHIP_FAMILY_STM32H7
#define SPI_TXDR REG8(&STM32_SPI1_REGS->txdr)
#define SPI_RXDR REG8(&STM32_SPI1_REGS->rxdr)
#else
#define SPI_TXDR STM32_SPI1_REGS->dr
#define SPI_RXDR STM32_SPI1_REGS->dr
#endif
/* DMA channel option */
static const struct dma_option dma_tx_option = {
STM32_DMAC_SPI1_TX, (void *)&SPI_TXDR,
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
#ifdef CHIP_FAMILY_STM32F4
| STM32_DMA_CCR_CHANNEL(STM32_SPI1_TX_REQ_CH)
#endif
};
static const struct dma_option dma_rx_option = {
STM32_DMAC_SPI1_RX, (void *)&SPI_RXDR,
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
#ifdef CHIP_FAMILY_STM32F4
| STM32_DMA_CCR_CHANNEL(STM32_SPI1_RX_REQ_CH)
#endif
};
/*
* Timeout to wait for SPI 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 master starts sending
* bytes as soon as it asserts chip select. That's as slow as we would
* practically want to run the SPI interface, since running it slower
* significantly impacts firmware update times.
*/
#define SPI_CMD_RX_TIMEOUT_US 8192
#ifdef CONFIG_SPI_PROTOCOL_V2
/*
* 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 SPI_PROTO2_OFFSET (EC_PROTO2_RESPONSE_HEADER_BYTES + 2)
#define SPI_PROTO2_OVERHEAD (SPI_PROTO2_OFFSET + \
EC_PROTO2_RESPONSE_TRAILER_BYTES + 1)
#endif /* defined(CONFIG_SPI_PROTOCOL_V2) */
/*
* 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.
*/
#define SPI_MAX_REQUEST_SIZE 0x220
#define SPI_MAX_RESPONSE_SIZE 0x220
/*
* 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. Search for "spi-frame-header" in U-boot to see how that's
* implemented.
*
* The preamble must be 32-bit aligned so that the response buffer is also
* 32-bit aligned.
*/
static const uint8_t out_preamble[4] = {
EC_SPI_PROCESSING,
EC_SPI_PROCESSING,
EC_SPI_PROCESSING,
EC_SPI_FRAME_START, /* This is the byte which matters */
};
/*
* Space allocation of the past-end status byte (EC_SPI_PAST_END) in the out_msg
* buffer. This seems to be dynamic because the F0 family needs to send it 4
* times in order to make sure it actually stays at the repeating byte after DMA
* ends.
*
* See crosbug.com/p/31390
*/
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32L4)
#define EC_SPI_PAST_END_LENGTH 4
#else
#define EC_SPI_PAST_END_LENGTH 1
#endif
/*
* Our input and output buffers. These must be large enough for our largest
* message, including protocol overhead, and must be 32-bit aligned.
*/
static uint8_t out_msg[SPI_MAX_RESPONSE_SIZE + sizeof(out_preamble) +
EC_SPI_PAST_END_LENGTH] __aligned(4) __uncached;
static uint8_t in_msg[SPI_MAX_REQUEST_SIZE] __aligned(4) __uncached;
static uint8_t enabled;
#ifdef CONFIG_SPI_PROTOCOL_V2
static struct host_cmd_handler_args args;
#endif
static struct host_packet spi_packet;
/*
* This is set if SPI NSS raises to high while EC is still processing a
* command.
*/
static int setup_transaction_later;
enum spi_state {
/* SPI not enabled (initial state, and when chipset is off) */
SPI_STATE_DISABLED = 0,
/* Setting up receive DMA */
SPI_STATE_PREPARE_RX,
/* Ready to receive next request */
SPI_STATE_READY_TO_RX,
/* Receiving request */
SPI_STATE_RECEIVING,
/* Processing request */
SPI_STATE_PROCESSING,
/* Sending response */
SPI_STATE_SENDING,
/*
* Received bad data - transaction started before we were ready, or
* packet header from host didn't parse properly. Ignoring received
* data.
*/
SPI_STATE_RX_BAD,
} state;
/**
* Wait until we have received a certain number of bytes
*
* Watch the DMA receive channel until it has the required number of bytes,
* or a timeout occurs
*
* We keep an eye on the NSS line - if this goes high then the transaction is
* over so there is no point in trying to receive the bytes.
*
* @param rxdma RX DMA channel to watch
* @param needed Number of bytes that are needed
* @param nss GPIO signal for NSS control line
* @return 0 if bytes received, -1 if we hit a timeout or NSS went high
*/
static int wait_for_bytes(dma_chan_t *rxdma, int needed,
enum gpio_signal nss)
{
timestamp_t deadline;
ASSERT(needed <= sizeof(in_msg));
deadline.val = 0;
while (1) {
if (dma_bytes_done(rxdma, sizeof(in_msg)) >= needed)
return 0;
if (gpio_get_level(nss))
return -1;
if (!deadline.val) {
deadline = get_time();
deadline.val += SPI_CMD_RX_TIMEOUT_US;
}
if (timestamp_expired(deadline, NULL))
return -1;
}
}
#ifdef CONFIG_SPI_PROTOCOL_V2
/**
* Send a reply on a given port.
*
* The format of a reply is as per the command interface, with a number of
* preamble bytes before it.
*
* The format of a reply is a sequence of bytes:
*
* <hdr> <status> <len> <msg bytes> <sum> [<preamble byte>...]
*
* The hdr byte is just a tag to indicate that the real message follows. It
* signals the end of any preamble required by the interface.
*
* The length is the entire packet size, including the header, length bytes,
* message payload, checksum, and postamble byte.
*
* The preamble is at least 2 bytes, but can be longer if the STM takes ages
* to react to the incoming message. Since we send our first byte as the AP
* sends us the command, we clearly can't send anything sensible for that
* byte. The second byte must be written to the output register just when the
* command byte is ready (I think), so we can't do anything there either.
* Any processing we do may increase this delay. That's the reason for the
* preamble.
*
* It is interesting to note that it seems to be possible to run the SPI
* interface faster than the CPU clock with this approach.
*
* We keep an eye on the NSS line - if this goes high then the transaction is
* over so there is no point in trying to send the reply.
*
* @param txdma TX DMA channel to send on
* @param status Status result to send
* @param msg_ptr Message payload to send, which normally starts
* SPI_PROTO2_OFFSET bytes into out_msg
* @param msg_len Number of message bytes to send
*/
static void reply(dma_chan_t *txdma,
enum ec_status status, char *msg_ptr, int msg_len)
{
char *msg = out_msg;
int need_copy = msg_ptr != msg + SPI_PROTO2_OFFSET;
int sum, i;
ASSERT(msg_len + SPI_PROTO2_OVERHEAD <= sizeof(out_msg));
/* Add our header bytes - the first one might not actually be sent */
msg[0] = EC_SPI_PROCESSING;
msg[1] = EC_SPI_FRAME_START;
msg[2] = status;
msg[3] = msg_len & 0xff;
/*
* Calculate the checksum; includes the status and message length bytes
* but not the pad and framing bytes since those are stripped by the AP
* driver.
*/
sum = status + msg_len;
for (i = 0; i < msg_len; i++) {
int ch = msg_ptr[i];
sum += ch;
if (need_copy)
msg[i + SPI_PROTO2_OFFSET] = ch;
}
/* Add the checksum and get ready to send */
msg[SPI_PROTO2_OFFSET + msg_len] = sum & 0xff;
msg[SPI_PROTO2_OFFSET + msg_len + 1] = EC_SPI_PAST_END;
dma_prepare_tx(&dma_tx_option, msg_len + SPI_PROTO2_OVERHEAD, msg);
/* Kick off the DMA to send the data */
dma_go(txdma);
}
#endif /* defined(CONFIG_SPI_PROTOCOL_V2) */
/**
* Sends a byte over SPI without DMA
*
* This is mostly used when we want to relay status bytes to the AP while we're
* receiving the message and we're thinking about it.
*
* @note It may be sent 0, 1, or >1 times, depending on whether the host clocks
* the bus or not. Basically, the EC is saying "if you ask me what my status is,
* you'll get this value. But you're not required to ask, or you can ask
* multiple times."
*
* @param byte status byte to send, one of the EC_SPI_* #defines from
* ec_commands.h
*/
static void tx_status(uint8_t byte)
{
stm32_spi_regs_t *spi __attribute__((unused)) = STM32_SPI1_REGS;
SPI_TXDR = byte;
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32L4)
/* It sends the byte 4 times in order to be sure it bypassed the FIFO
* from the STM32F0 line.
*/
spi->dr = byte;
spi->dr = byte;
spi->dr = byte;
#elif defined(CHIP_FAMILY_STM32H7)
spi->udrdr = byte;
#endif
}
/**
* Get ready to receive a message from the master.
*
* Set up our RX DMA and disable our TX DMA. Set up the data output so that
* we will send preamble bytes.
*/
static void setup_for_transaction(void)
{
stm32_spi_regs_t *spi __attribute__((unused)) = STM32_SPI1_REGS;
volatile uint8_t dummy __attribute__((unused));
/* clear this as soon as possible */
setup_transaction_later = 0;
#ifndef CHIP_FAMILY_STM32H7 /* H7 is not ready to set status here */
/* Not ready to receive yet */
tx_status(EC_SPI_NOT_READY);
#endif
/* We are no longer actively processing a transaction */
state = SPI_STATE_PREPARE_RX;
/* Stop sending response, if any */
dma_disable(STM32_DMAC_SPI1_TX);
/*
* Read dummy bytes in case there are some pending; this prevents the
* receive DMA from getting that byte right when we start it.
*/
dummy = SPI_RXDR;
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32L4)
/* 4 Bytes makes sure the RX FIFO on the F0 is empty as well. */
dummy = spi->dr;
dummy = spi->dr;
dummy = spi->dr;
#endif
/* Start DMA */
dma_start_rx(&dma_rx_option, sizeof(in_msg), in_msg);
/* Ready to receive */
state = SPI_STATE_READY_TO_RX;
tx_status(EC_SPI_OLD_READY);
#ifdef CHIP_FAMILY_STM32H7
spi->cr1 |= STM32_SPI_CR1_SPE;
#endif
}
/* Forward declaration */
static void spi_init(void);
/*
* If a setup_for_transaction() was postponed, call it now.
* Note that setup_for_transaction() cancels Tx DMA.
*/
static void check_setup_transaction_later(void)
{
if (setup_transaction_later) {
spi_init(); /* Fix for bug chrome-os-partner:31390 */
/*
* 'state' is set to SPI_STATE_READY_TO_RX. Somehow AP
* de-asserted the SPI NSS during the handler was running.
* Thus, the pending result will be dropped anyway.
*/
}
}
#ifdef CONFIG_SPI_PROTOCOL_V2
/**
* Called for V2 protocol to indicate that a command has completed
*
* Some commands can continue for a while. This function is called by
* host_command when it completes.
*
*/
static void spi_send_response(struct host_cmd_handler_args *args)
{
enum ec_status result = args->result;
dma_chan_t *txdma;
/*
* If we're not processing, then the AP has already terminated the
* transaction, and won't be listening for a response.
*/
if (state != SPI_STATE_PROCESSING)
return;
/* state == SPI_STATE_PROCESSING */
if (args->response_size > args->response_max)
result = EC_RES_INVALID_RESPONSE;
/* Transmit the reply */
txdma = dma_get_channel(STM32_DMAC_SPI1_TX);
reply(txdma, result, args->response, args->response_size);
/*
* Before the state is set to SENDING, any CS de-assertion would
* set setup_transaction_later to 1.
*/
state = SPI_STATE_SENDING;
check_setup_transaction_later();
}
#endif /* defined(CONFIG_SPI_PROTOCOL_V2) */
/**
* 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)
{
dma_chan_t *txdma;
/*
* If we're not processing, then the AP has already terminated the
* transaction, and won't be listening for a response.
*/
if (state != SPI_STATE_PROCESSING)
return;
/* state == SPI_STATE_PROCESSING */
/* Append our past-end byte, which we reserved space for. */
((uint8_t *)pkt->response)[pkt->response_size + 0] = EC_SPI_PAST_END;
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32L4)
/* Make sure we are going to be outputting it properly when the DMA
* ends due to the TX FIFO bug on the F0. See crosbug.com/p/31390
*/
((uint8_t *)pkt->response)[pkt->response_size + 1] = EC_SPI_PAST_END;
((uint8_t *)pkt->response)[pkt->response_size + 2] = EC_SPI_PAST_END;
((uint8_t *)pkt->response)[pkt->response_size + 3] = EC_SPI_PAST_END;
#endif
/* Transmit the reply */
txdma = dma_get_channel(STM32_DMAC_SPI1_TX);
dma_prepare_tx(&dma_tx_option, sizeof(out_preamble) + pkt->response_size
+ EC_SPI_PAST_END_LENGTH, out_msg);
dma_go(txdma);
#ifdef CHIP_FAMILY_STM32H7
/* clear any previous underrun */
STM32_SPI1_REGS->ifcr = STM32_SPI_SR_UDR;
#endif /* CHIP_FAMILY_STM32H7 */
/*
* Before the state is set to SENDING, any CS de-assertion would
* set setup_transaction_later to 1.
*/
state = SPI_STATE_SENDING;
check_setup_transaction_later();
}
/**
* Handle an event on the NSS pin
*
* A falling edge of NSS indicates that the master is starting a new
* transaction. A rising edge indicates that we have finished.
*
* @param signal GPIO signal for the NSS pin
*/
void spi_event(enum gpio_signal signal)
{
dma_chan_t *rxdma;
uint16_t i;
/* If not enabled, ignore glitches on NSS */
if (!enabled)
return;
/* Check chip select. If it's high, the AP ended a transaction. */
if (gpio_get_level(GPIO_SPI1_NSS)) {
enable_sleep(SLEEP_MASK_SPI);
/*
* If the buffer is still used by the host command, postpone
* the DMA rx setup.
*/
if (state == SPI_STATE_PROCESSING) {
setup_transaction_later = 1;
return;
}
/* Set up for the next transaction */
spi_init(); /* Fix for bug chrome-os-partner:31390 */
return;
}
disable_sleep(SLEEP_MASK_SPI);
/* Chip select is low = asserted */
if (state != SPI_STATE_READY_TO_RX) {
/*
* AP started a transaction but we weren't ready for it.
* Tell AP we weren't ready, and ignore the received data.
*/
CPRINTS("SPI not ready");
tx_status(EC_SPI_NOT_READY);
state = SPI_STATE_RX_BAD;
return;
}
/* We're now inside a transaction */
state = SPI_STATE_RECEIVING;
tx_status(EC_SPI_RECEIVING);
rxdma = dma_get_channel(STM32_DMAC_SPI1_RX);
/* Wait for version, command, length bytes */
if (wait_for_bytes(rxdma, 3, GPIO_SPI1_NSS))
goto spi_event_error;
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;
/* Wait for the rest of the command header */
if (wait_for_bytes(rxdma, sizeof(*r), GPIO_SPI1_NSS))
goto spi_event_error;
/*
* Check how big the packet should be. We can't just wait to
* see how much data the host sends, because it will keep
* sending dummy data until we respond.
*/
pkt_size = host_request_expected_size(r);
if (pkt_size == 0 || pkt_size > sizeof(in_msg))
goto spi_event_error;
/* Wait for the packet data */
if (wait_for_bytes(rxdma, pkt_size, GPIO_SPI1_NSS))
goto spi_event_error;
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 + sizeof(out_preamble);
/* Reserve space for the preamble and trailing past-end byte */
spi_packet.response_max = sizeof(out_msg)
- sizeof(out_preamble) - EC_SPI_PAST_END_LENGTH;
spi_packet.response_size = 0;
spi_packet.driver_result = EC_RES_SUCCESS;
/* Move to processing state */
state = SPI_STATE_PROCESSING;
tx_status(EC_SPI_PROCESSING);
host_packet_receive(&spi_packet);
return;
} else if (in_msg[0] >= EC_CMD_VERSION0) {
#ifdef CONFIG_SPI_PROTOCOL_V2
/*
* Protocol version 2
*
* TODO(crosbug.com/p/20257): Remove once kernel supports
* version 3.
*/
#ifdef CHIP_FAMILY_STM32F0
CPRINTS("WARNING: Protocol version 2 is not supported on the F0"
" line due to crosbug.com/p/31390");
#endif
args.version = in_msg[0] - EC_CMD_VERSION0;
args.command = in_msg[1];
args.params_size = in_msg[2];
/* Wait for parameters */
if (wait_for_bytes(rxdma, 3 + args.params_size, GPIO_SPI1_NSS))
goto spi_event_error;
/*
* Params are not 32-bit aligned in protocol version 2. As a
* workaround, move them to the beginning of the input buffer
* so they are aligned.
*/
if (args.params_size)
memmove(in_msg, in_msg + 3, args.params_size);
args.params = in_msg;
args.send_response = spi_send_response;
/* Allow room for the header bytes */
args.response = out_msg + SPI_PROTO2_OFFSET;
args.response_max = sizeof(out_msg) - SPI_PROTO2_OVERHEAD;
args.response_size = 0;
args.result = EC_RES_SUCCESS;
/* Move to processing state */
state = SPI_STATE_PROCESSING;
tx_status(EC_SPI_PROCESSING);
host_command_received(&args);
return;
#else /* !defined(CONFIG_SPI_PROTOCOL_V2) */
/* Protocol version 2 is deprecated. */
CPRINTS("ERROR: Protocol V2 is not supported!");
#endif /* defined(CONFIG_SPI_PROTOCOL_V2) */
}
spi_event_error:
/* Error, timeout, or protocol we can't handle. Ignore data. */
tx_status(EC_SPI_RX_BAD_DATA);
state = SPI_STATE_RX_BAD;
CPRINTS("SPI rx bad data");
CPRINTF("in_msg=[");
for (i = 0; i < dma_bytes_done(rxdma, sizeof(in_msg)); i++)
CPRINTF("%02x ", in_msg[i]);
CPRINTF("]\n");
}
static void spi_chipset_startup(void)
{
/* Enable pullup and interrupts on NSS */
gpio_set_flags(GPIO_SPI1_NSS, GPIO_INT_BOTH | GPIO_PULL_UP);
/* Set SPI pins to alternate function */
gpio_config_module(MODULE_SPI, 1);
/* Set up for next transaction */
setup_for_transaction();
enabled = 1;
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, spi_chipset_startup, HOOK_PRIO_DEFAULT);
static void spi_chipset_shutdown(void)
{
enabled = 0;
state = SPI_STATE_DISABLED;
/* Disable pullup and interrupts on NSS */
gpio_set_flags(GPIO_SPI1_NSS, GPIO_INPUT);
/* Set SPI pins to inputs so we don't leak power when AP is off */
gpio_config_module(MODULE_SPI, 0);
/* Allow deep sleep when AP off */
enable_sleep(SLEEP_MASK_SPI);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, spi_chipset_shutdown, HOOK_PRIO_DEFAULT);
static void spi_init(void)
{
stm32_spi_regs_t *spi = STM32_SPI1_REGS;
uint8_t was_enabled = enabled;
/* Reset the SPI Peripheral to clear any existing weird states. */
/* Fix for bug chrome-os-partner:31390 */
enabled = 0;
state = SPI_STATE_DISABLED;
STM32_RCC_APB2RSTR |= STM32_RCC_PB2_SPI1;
STM32_RCC_APB2RSTR &= ~STM32_RCC_PB2_SPI1;
/* 40 MHz pin speed */
STM32_GPIO_OSPEEDR(GPIO_A) |= 0xff00;
/* Enable clocks to SPI1 module */
STM32_RCC_APB2ENR |= STM32_RCC_PB2_SPI1;
/* Delay 1 APB clock cycle after the clock is enabled */
clock_wait_bus_cycles(BUS_APB, 1);
/*
* Select the right DMA request for the variants using it.
* This is not required for STM32F4 since the channel (aka request) is
* set directly in the respective dma_option. In fact, it would be
* overridden in dma-stm32f4::prepare_stream().
*/
#ifdef CHIP_FAMILY_STM32L4
dma_select_channel(STM32_DMAC_SPI1_TX, 1);
dma_select_channel(STM32_DMAC_SPI1_RX, 1);
#elif defined(CHIP_FAMILY_STM32H7)
dma_select_channel(STM32_DMAC_SPI1_TX, DMAMUX1_REQ_SPI1_TX);
dma_select_channel(STM32_DMAC_SPI1_RX, DMAMUX1_REQ_SPI1_RX);
#endif
/*
* Enable rx/tx DMA and get ready to receive our first transaction and
* "disable" FIFO by setting event to happen after only 1 byte
*/
#ifdef CHIP_FAMILY_STM32H7
spi->cfg2 = 0;
spi->cfg1 = STM32_SPI_CFG1_DATASIZE(8) | STM32_SPI_CFG1_FTHLV(4) |
STM32_SPI_CFG1_CRCSIZE(8) |
STM32_SPI_CFG1_TXDMAEN | STM32_SPI_CFG1_RXDMAEN |
STM32_SPI_CFG1_UDRCFG_CONST |
STM32_SPI_CFG1_UDRDET_BEGIN_FRM;
spi->cr1 = 0;
#else /* !CHIP_FAMILY_STM32H7 */
spi->cr2 = STM32_SPI_CR2_RXDMAEN | STM32_SPI_CR2_TXDMAEN |
STM32_SPI_CR2_FRXTH | STM32_SPI_CR2_DATASIZE(8);
/* Enable the SPI peripheral */
spi->cr1 |= STM32_SPI_CR1_SPE;
#endif /* !CHIP_FAMILY_STM32H7 */
gpio_enable_interrupt(GPIO_SPI1_NSS);
/*
* If we were already enabled or chipset is already on,
* prepare for transaction
*/
if (was_enabled || chipset_in_state(CHIPSET_STATE_ON))
spi_chipset_startup();
}
DECLARE_HOOK(HOOK_INIT, spi_init, HOOK_PRIO_INIT_SPI);
/**
* 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));
#ifdef CONFIG_SPI_PROTOCOL_V2
r->protocol_versions |= BIT(2);
#endif
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_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO,
spi_get_protocol_info,
EC_VER_MASK(0));