blob: bb2bfaafe6ffb7c222c7960197c60e84e17efa84 [file] [log] [blame]
/*
* Copyright (c) 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 "console.h"
#include "dma.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "registers.h"
#include "spi.h"
#include "timer.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_SPI, outstr)
#define CPRINTF(format, args...) cprintf(CC_SPI, format, ## args)
/* DMA channel option */
static const struct dma_option dma_tx_option = {
STM32_DMAC_SPI1_TX, (void *)&STM32_SPI1_REGS->dr,
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_16_BIT
};
static const struct dma_option dma_rx_option = {
STM32_DMAC_SPI1_RX, (void *)&STM32_SPI1_REGS->dr,
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_16_BIT
};
/* Special byte values */
#define SPI_STALL_BYTE 0xfd /* Bytes sent when EC isn't ready to respond */
#define SPI_PAD_BYTE 0xff /* Bytes which precede the framing byte */
#define SPI_FRAMING_BYTE 0xec /* Used by AP to find response start */
/*
* Timeout to wait for SPI request packet
*
* TODO(sjg@chromium.org): Support much slower SPI clocks. For 4096 we have a
* delay of 4ms. For the largest message (68 bytes) this is 130KhZ, assuming
* that the master starts sending bytes as soon as it drops NSS. In practice,
* this timeout seems adequately high for a 1MHz clock which is as slow as we
* would reasonably want it.
*/
#define SPI_CMD_RX_TIMEOUT_US 8192
/* Offset of output parameters needs to account for pad and framing bytes */
#define SPI_PROTO2_OFFSET (EC_PROTO2_RESPONSE_HEADER_BYTES + 2)
#define SPI_PROTO2_OVERHEAD (SPI_PROTO2_OFFSET + \
EC_PROTO2_RESPONSE_TRAILER_BYTES)
/*
* 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] = {
SPI_PAD_BYTE,
SPI_PAD_BYTE,
SPI_PAD_BYTE,
SPI_FRAMING_BYTE, /* This is the byte which matters */
};
/*
* 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)]
__attribute__((aligned(4)));
static uint8_t in_msg[SPI_MAX_REQUEST_SIZE] __attribute__((aligned(4)));
static uint8_t active;
static uint8_t enabled;
static struct host_cmd_handler_args args;
static struct host_packet spi_packet;
/**
* 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_regs GPIO register for NSS control line
* @param nss_mask Bit to check in GPIO register (when high, we abort)
* @return 0 if bytes received, -1 if we hit a timeout or NSS went high
*/
static int wait_for_bytes(stm32_dma_chan_t *rxdma, int needed,
uint16_t *nss_reg, uint32_t nss_mask)
{
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 (REG16(nss_reg) & nss_mask)
return -1;
if (!deadline.val) {
deadline = get_time();
deadline.val += SPI_CMD_RX_TIMEOUT_US;
}
if (timestamp_expired(deadline, NULL))
return -1;
}
}
/**
* 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(stm32_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] = SPI_PAD_BYTE;
msg[1] = SPI_FRAMING_BYTE;
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;
dma_prepare_tx(&dma_tx_option, msg_len + SPI_PROTO2_OVERHEAD, msg);
/* Kick off the DMA to send the data */
dma_go(txdma);
}
/**
* 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 = STM32_SPI1_REGS;
int dmac __attribute__((unused));
/* We are no longer actively processing a transaction */
active = 0;
/* Output stall byte by default */
spi->dr = SPI_STALL_BYTE;
dma_disable(STM32_DMAC_SPI1_TX);
*in_msg = SPI_PAD_BYTE;
/* read a byte in case there is one, and the rx dma gets it */
dmac = spi->dr;
dma_start_rx(&dma_rx_option, sizeof(in_msg), in_msg);
}
/**
* 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;
stm32_dma_chan_t *txdma;
/* If we are too late, don't bother */
if (!active)
return;
if (args->response_size > EC_PROTO2_MAX_PARAM_SIZE)
result = EC_RES_INVALID_RESPONSE;
if ((uint8_t *)args->response >= out_msg &&
(uint8_t *)args->response < out_msg + sizeof(out_msg))
ASSERT(args->response == out_msg + SPI_PROTO2_OFFSET);
/* Transmit the reply */
txdma = dma_get_channel(STM32_DMAC_SPI1_TX);
reply(txdma, result, args->response, args->response_size);
}
/**
* 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)
{
stm32_dma_chan_t *txdma;
/* If we are too late, don't bother */
if (!active)
return;
/* Transmit the reply */
txdma = dma_get_channel(STM32_DMAC_SPI1_TX);
dma_prepare_tx(&dma_tx_option,
sizeof(out_preamble) + pkt->response_size, out_msg);
dma_go(txdma);
}
/**
* 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 finsihed
*
* @param signal GPIO signal for the NSS pin
*/
void spi_event(enum gpio_signal signal)
{
stm32_dma_chan_t *rxdma;
uint16_t *nss_reg;
uint32_t nss_mask;
/* If not enabled, ignore glitches on NSS */
if (!enabled)
return;
/*
* If NSS is rising, we have finished the transaction, so prepare
* for the next.
*/
nss_reg = gpio_get_level_reg(GPIO_SPI1_NSS, &nss_mask);
if (REG16(nss_reg) & nss_mask)
goto spi_event_error;
/* Otherwise, NSS is low and we're now inside a transaction */
active = 1;
rxdma = dma_get_channel(STM32_DMAC_SPI1_RX);
/* Wait for version, command, length bytes */
if (wait_for_bytes(rxdma, 3, nss_reg, nss_mask))
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), nss_reg, nss_mask))
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, nss_reg, nss_mask))
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);
spi_packet.response_max =
sizeof(out_msg) - sizeof(out_preamble);
spi_packet.response_size = 0;
spi_packet.driver_result = EC_RES_SUCCESS;
host_packet_receive(&spi_packet);
return;
} else if (in_msg[0] >= EC_CMD_VERSION0) {
/*
* Protocol version 2
*
* TODO: remove once all systems upgraded to version 3
*/
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,
nss_reg, nss_mask))
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;
/* Process the command and send the reply */
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;
host_command_received(&args);
return;
}
spi_event_error:
/* Error, or protocol we can't handle. Set up for next transaction */
setup_for_transaction();
}
static void spi_init(void)
{
stm32_spi_regs_t *spi = STM32_SPI1_REGS;
/* 40 MHz pin speed */
STM32_GPIO_OSPEEDR(GPIO_A) |= 0xff00;
/* Enable clocks to SPI1 module */
STM32_RCC_APB2ENR |= STM32_RCC_PB2_SPI1;
/* Enable rx DMA and get ready to receive our first transaction */
spi->cr2 = STM32_SPI_CR2_RXDMAEN | STM32_SPI_CR2_TXDMAEN;
/* Enable the SPI peripheral */
spi->cr1 |= STM32_SPI_CR1_SPE;
gpio_enable_interrupt(GPIO_SPI1_NSS);
}
DECLARE_HOOK(HOOK_INIT, spi_init, HOOK_PRIO_DEFAULT);
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_set_alternate_function(GPIO_A, 0xf0, GPIO_ALT_SPI);
/* Set up for next transaction */
setup_for_transaction();
enabled = 1;
}
DECLARE_HOOK(HOOK_CHIPSET_STARTUP, spi_chipset_startup, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_CHIPSET_RESUME, spi_chipset_startup, HOOK_PRIO_DEFAULT);
static void spi_chipset_shutdown(void)
{
enabled = active = 0;
/* Disable pullup and interrupts on NSS */
gpio_set_flags(GPIO_SPI1_NSS, 0);
/* Set SPI pins to inputs so we don't leak power when AP is off */
gpio_set_alternate_function(GPIO_A, 0xf0, -1);
}
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, spi_chipset_shutdown, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, spi_chipset_shutdown, HOOK_PRIO_DEFAULT);
/**
* Get protocol information
*/
static int 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 = (1 << 2) | (1 << 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));