Files
OpenCellular/chip/npcx/shi.c
Ian Chao 957638c78c nuc: Add SHI driver for arm-based platform in chip folder.
Add npcx_evb_arm board-level driver for arm-based platform.
Add header.c: for booting from NPCX5M5G A3 Booter.
Remove lfw folder due to those functionalitie have been replaced with Booter

Modified drivers for
Patch Set 1:
1. flash.c: Implement UMA lock, tri-state and selection register lock functionalities
2. hwtimer.c: Add ITIM32 for hwtimer
3. lpc.c: Add checking for LRESET
4. system.c: Modified CODERAM_ARCH functions for NPCX5M5G A3 Booter.
5. uart.c: Add support for module 2
Patch Set 2:
6. lpc.c: Modified lpc_get_pltrst_asserted() func
Patch Set 3:
7. minimize the changes for CONFIG_CODERAM_ARCH in common layer
8. comments of Patch Set1/2
Patch Set 4:
9. Modified CONFIG_RO_MEM_OFF point to ro image and keep header as a part of ec.RO.flat.
10. Fixed RO_FRID and RW_FRID issues which caused by CONFIG_CODERAM_ARCH.
Patch Set 5:
11. Modified system.c in common folder for supporting *_STORAGE_OFF.
12. Use *_STORAGE_OFF in firmware_image.lds.S to indicate flat file layout in flash.
Patch Set 6:
13. rebase to newest version
14. system.c: Modified for the newest include/system.h
Patch Set 7:
15. Merge from version 0625

BUG=chrome-os-partner:34346
TEST=make buildall -j; test nuvoton IC specific drivers
BRANCH=none

Change-Id: Ifd7c10b81b5781ccd75bb2558dc236486976e8ed
Signed-off-by: Ian Chao <mlchao@nuvoton.com>
Reviewed-on: https://chromium-review.googlesource.com/272034
Reviewed-by: Shawn N <shawnn@chromium.org>
Tested-by: Shawn N <shawnn@chromium.org>
Commit-Queue: Shawn N <shawnn@chromium.org>
2015-06-26 18:57:32 +00:00

860 lines
25 KiB
C

/*
* Copyright (c) 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"
#if !(DEBUG_SHI)
#define CPUTS(...)
#define CPRINTS(...)
#define CPRINTF(...)
#else
#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)
#endif
/* SHI Bus definition */
#define SHI_OBUF_FULL_SIZE 64 /* Full output buffer size */
#define SHI_IBUF_FULL_SIZE 64 /* Full input buffer size */
#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 ((NPCX_IBUFSTAT + SHI_OUT_PREAMBLE_LENGTH) % \
SHI_OBUF_FULL_SIZE)
/* Start address of SHI input buffer */
#define SHI_IBUF_START_ADDR (volatile uint8_t *)(NPCX_SHI_BASE_ADDR + 0x060)
/* Current address of SHI input buffer */
#define SHI_IBUF_CUR_ADDR (SHI_IBUF_START_ADDR + NPCX_IBUFSTAT)
/*
* 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 master 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 10
/*
* 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 SHI_MAX_REQUEST_SIZE 0x220
#define SHI_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.
*/
#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_PROTO2_OFFSET (EC_PROTO2_RESPONSE_HEADER_BYTES + 1)
#define SHI_PROTO2_OVERHEAD (SHI_PROTO2_OFFSET + \
EC_PROTO2_RESPONSE_TRAILER_BYTES + 1)
#define SHI_PROTO3_OVERHEAD (EC_SPI_PAST_END_LENGTH + EC_SPI_FRAME_START_LENGTH)
/*
* Our input and output msg buffers. These must be large enough for our largest
* message, including protocol overhead, and must be 32-bit aligned.
*/
static uint8_t out_msg[SHI_MAX_RESPONSE_SIZE];
static uint8_t in_msg[SHI_MAX_REQUEST_SIZE];
/* Parameters used by host protocols */
static struct host_cmd_handler_args args;
static struct host_packet shi_packet;
/* Function pointer for handler of host request */
void (*request_handler)(void);
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,
/* Complete transaction but need to initialize */
SHI_STATE_NOT_READY,
/* Receiving request */
SHI_STATE_RECEIVING,
/* Processing request */
SHI_STATE_PROCESSING,
/* Sending response */
SHI_STATE_SENDING,
/* State machine mismatch, timeout, or protocol we can't handle. */
SHI_STATE_ERROR,
} 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 */
} shi_params;
/* Forward declaraction */
static void shi_reset_prepare(void);
static void shi_error(int need_reset);
static void shi_fill_out_status(uint8_t status);
static void shi_write_half_outbuf(void);
static void shi_write_outbuf_wait(uint16_t szbytes);
static int shi_read_inbuf_wait(uint16_t szbytes);
/*****************************************************************************/
/* V2 protocol layer functions */
/**
* 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> [<postamble 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.
*
* We keep an eye on the SHI_CS_L line - if this goes high then the transaction
* is over so there is no point in trying to send the reply.
*/
static void shi_reply_response(enum ec_status status, uint8_t *msg_ptr,
int msg_len)
{
int need_copy = msg_ptr != out_msg + SHI_PROTO2_OFFSET;
int sum, i;
ASSERT(msg_len + SHI_PROTO2_OVERHEAD <= sizeof(out_msg));
/* Add our header bytes - the first one might not actually be sent */
out_msg[0] = EC_SPI_FRAME_START;
out_msg[1] = status;
out_msg[2] = 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)
out_msg[i + SHI_PROTO2_OFFSET] = ch;
}
/* Add the checksum and get ready to send */
out_msg[SHI_PROTO2_OFFSET + msg_len] = sum & 0xff;
out_msg[SHI_PROTO2_OFFSET + msg_len + 1] = EC_SPI_PAST_END;
/* Computing sending bytes of response */
shi_params.sz_response = msg_len + SHI_PROTO2_OVERHEAD;
/*
* Before the state is set to SENDING, any CS de-assertion would
* give up sending.
*/
if (state == SHI_STATE_PROCESSING) {
/*
* Disable SHI interrupt until we have prepared
* the first package to output
*/
task_disable_irq(NPCX_IRQ_SHI);
/* Transmit the reply */
state = SHI_STATE_SENDING;
CPRINTF("SND-");
/* Start to fill output buffer with msg buffer */
shi_write_outbuf_wait(shi_params.sz_response);
/* Enable SHI interrupt */
task_enable_irq(NPCX_IRQ_SHI);
}
/*
* If we're not processing, then the AP has already terminated the
* transaction, and won't be listening for a response.
*/
else {
/* Reset SHI and prepare to next transaction again */
shi_reset_prepare();
CPRINTF("END\n");
return;
}
}
/**
* 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 shi_send_response(struct host_cmd_handler_args *args)
{
enum ec_status result = args->result;
if (args->response_size > args->response_max)
result = EC_RES_INVALID_RESPONSE;
/* Transmit the reply */
args->response_size += (EC_SPI_PAST_END_LENGTH +
EC_SPI_FRAME_START_LENGTH);
shi_reply_response(result, args->response, args->response_size);
}
void shi_handle_host_command(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;
/* Move to processing state immediately */
state = SHI_STATE_PROCESSING;
CPRINTF("PRC-");
/* Read remaining bytes from input buffer directly */
if (!shi_read_inbuf_wait(remain_bytes))
return shi_error(1);
}
/* Fill output buffer to indicate we`re processing request */
shi_fill_out_status(EC_SPI_PROCESSING);
/* Set up parameters for host request */
args.params = in_msg + 3;
args.send_response = shi_send_response;
/* Allow room for the header bytes */
args.response = out_msg + SHI_PROTO2_OFFSET;
args.response_max = sizeof(out_msg) - SHI_PROTO2_OVERHEAD;
args.response_size = 0;
args.result = EC_RES_SUCCESS;
/* Go to common-layer to handle request */
host_command_received(&args);
}
/*****************************************************************************/
/* 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)
{
/* 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;
/*
* Before the state is set to SENDING, any CS de-assertion would
* give up sending.
*/
if (state == SHI_STATE_PROCESSING) {
/*
* Disable SHI interrupt until we have prepared
* the first package to output
*/
task_disable_irq(NPCX_IRQ_SHI);
/* Transmit the reply */
state = SHI_STATE_SENDING;
CPRINTF("SND-");
/* Start to fill output buffer with msg buffer */
shi_write_outbuf_wait(shi_params.sz_response);
/* Enable SHI interrupt */
task_enable_irq(NPCX_IRQ_SHI);
}
/*
* If we're not processing, then the AP has already terminated the
* transaction, and won't be listening for a response.
*/
else {
/* Reset SHI and prepare to next transaction again */
shi_reset_prepare();
CPRINTF("END\n");
return;
}
}
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;
/* Move to processing state immediately */
state = SHI_STATE_PROCESSING;
CPRINTF("PRC-");
/* Read remaining bytes from input buffer directly */
if (!shi_read_inbuf_wait(remain_bytes))
return shi_error(1);
}
/* 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;
/* Put FRAME_START in first byte */
out_msg[0] = EC_SPI_FRAME_START;
shi_packet.response = out_msg + 1;
/* Reserve space for frame start and trailing past-end byte */
shi_packet.response_max = sizeof(out_msg) - SHI_PROTO3_OVERHEAD;
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)
{
/* Wait for version, command, length bytes */
if (!shi_read_inbuf_wait(3))
return shi_error(1);
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_error(1);
/* 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_error(0);
/* Computing total bytes need to receive */
shi_params.sz_request = pkt_size;
/* Set handler for host_package */
request_handler = shi_handle_host_package;
} else if (in_msg[0] >= EC_CMD_VERSION0) {
/*
* Protocol version 2
* TODO(crosbug.com/p/20257): Remove once kernel supports
* version 3.
*/
args.version = in_msg[0] - EC_CMD_VERSION0;
args.command = in_msg[1];
args.params_size = in_msg[2];
/* Computing remaining received bytes */
shi_params.sz_request = args.params_size + 3;
/* Set handler for host_command */
request_handler = shi_handle_host_command;
} else {
/* Invalid version number */
return shi_error(0);
}
/* run receiving handler */
request_handler();
}
/*****************************************************************************/
/* 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)
{
uint16_t i;
uint16_t offset = SHI_OBUF_VALID_OFFSET;
/* Fill out all output buffer with status byte */
for (i = offset; i < SHI_OBUF_FULL_SIZE; i++)
NPCX_OBUF(i) = status;
for (i = 0; i < offset; i++)
NPCX_OBUF(i) = status;
}
/*
* This routine write SHI next half output buffer from msg buffer
*/
static void shi_write_half_outbuf(void)
{
uint16_t i;
uint16_t size = MIN(SHI_OBUF_HALF_SIZE,
shi_params.sz_response - shi_params.sz_sending);
/* Fill half output buffer */
for (i = 0; i < size; i++, shi_params.sz_sending++)
*(shi_params.tx_buf++) = *(shi_params.tx_msg++);
}
/*
* This routine write SHI output buffer from msg buffer until
* we have sent a certain number of bytes or output buffer is full
*/
static void shi_write_outbuf_wait(uint16_t szbytes)
{
uint16_t i;
static uint16_t offset, size;
offset = SHI_OBUF_VALID_OFFSET;
shi_params.tx_buf = SHI_OBUF_START_ADDR + offset;
/* Fill half output buffer */
size = MIN(SHI_OBUF_HALF_SIZE - (offset % SHI_OBUF_HALF_SIZE),
szbytes - shi_params.sz_sending);
for (i = 0; i < size; i++, shi_params.sz_sending++)
*(shi_params.tx_buf++) = *(shi_params.tx_msg++);
/* Write data from bottom address again */
if (shi_params.tx_buf == SHI_OBUF_FULL_ADDR)
shi_params.tx_buf = SHI_OBUF_START_ADDR;
/* Fill next half output buffer */
size = MIN(SHI_OBUF_HALF_SIZE, szbytes - shi_params.sz_sending);
for (i = 0; i < size; i++, shi_params.sz_sending++)
*(shi_params.tx_buf++) = *(shi_params.tx_msg++);
}
/* 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 make sure input buffer status register is valid or it will
* return flase after timeout
*/
static int shi_check_inbuf_valid(void)
{
timestamp_t deadline = get_time();
deadline.val += SHI_GLITCH_TIMEOUT_US;
/*
* If input buffer pointer is no changed after timeout, it will
* return false
*/
while (NPCX_IBUFSTAT == shi_params.pre_ibufstat)
if (timestamp_expired(deadline, NULL))
return 0;
/* valid package */
return 1;
}
/*
* 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;
}
/* This routine handles bus error condition */
static void shi_error(int need_reset)
{
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_ERROR;
CPRINTS("ERR-[");
CPRINTF("in_msg=[");
for (i = 0; i < shi_params.sz_received; i++)
CPRINTF("%02x ", in_msg[i]);
CPRINTF("]\n");
/*
* If glitch occurred or losing clocks, EVSTAT_EOR/W
* will not generate. We need to reset SHI bus here.
*/
if (need_reset)
shi_reset_prepare();
}
/* This routine handles all interrupts of this module */
void shi_int_handler(void)
{
uint8_t stat_reg;
/* Read status register and clear interrupt status early*/
stat_reg = NPCX_EVSTAT;
NPCX_EVSTAT = stat_reg;
/*
* End of data for read/write transaction. ie SHI_CS is deasserted.
* Host completed or aborted transaction
*/
if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_EOR)) {
/* Already reset in shi_error or not */
if (state != SHI_STATE_READY_TO_RECV)
/*
* Mark not ready to prevent the other
* transaction immediately
*/
NPCX_OBUF(0) = EC_SPI_NOT_READY;
CPRINTF("CSH-");
/*
* If the buffer is still used by the host command.
* Change tx buffer to NOT_READY
*/
if (state == SHI_STATE_PROCESSING) {
/*
* Mark state to NOT_READY for waiting host executes
* response function
*/
state = SHI_STATE_NOT_READY;
CPRINTF("WAIT-");
return;
}
/* reset SHI and prepare to next transaction again */
shi_reset_prepare();
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 request_handler();
} else if (state == SHI_STATE_SENDING) {
/* Write data from bottom address again */
shi_params.tx_buf = SHI_OBUF_START_ADDR;
/* Write data from msg buffer to output buffer */
return shi_write_half_outbuf();
} else if (state == SHI_STATE_PROCESSING)
/* Wait for host handles request */
return;
else
/* Unexpected status */
return shi_error(1);
}
/*
* Indicate input/output buffer pointer reaches the full buffer size.
* Transaction is processing.
*/
if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_IBF)) {
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 request_handler();
} else if (state == SHI_STATE_SENDING)
/* Write data from msg buffer to output buffer */
return shi_write_half_outbuf();
else if (state == SHI_STATE_PROCESSING)
/* Wait for host handles request */
return;
else
/* Unexpected status */
return shi_error(1);
}
}
/*
* The interrupt priority for SHI interrupt should be higher than
* GPIO. Then we could receive CS-deasserted event even in CS-asserted ISR.
*/
DECLARE_IRQ(NPCX_IRQ_SHI, shi_int_handler, 0);
/* Handle an CS assert event on the SHI_CS_L pin */
void shi_cs_event(enum gpio_signal signal)
{
/* If not enabled, ignore glitches on SHI_CS_L */
if (state == SHI_STATE_DISABLED)
return;
/*
* TODO (ML): Glitches on SHI_CS_L will cause SHI doesn`t generate
* 'End of data for read/write transaction interrupt' and IBUFSTAT will
* keep previous value without clocks. (Workaround) Need to reset it
* manually in CS assert ISR.
*/
if (NPCX_IBUFSTAT == shi_params.pre_ibufstat) {
if (!shi_check_inbuf_valid()) {
CPRINTS("ERR-GTH");
shi_reset_prepare();
CPRINTS("END\n");
return;
}
}
/* Chip select is low = asserted */
if (state != SHI_STATE_READY_TO_RECV) {
/*
* AP started a transaction but we weren't ready for it.
* Tell AP we weren't ready, and ignore the received data.
* The driver should change status later when complete handling
* response from host
*/
NPCX_OBUF(0) = EC_SPI_NOT_READY;
CPRINTS("CSL-NRDY");
/*
* If status still is error, reset SHI Bus and
* abort this transaction.
*/
if (state == SHI_STATE_ERROR) {
CPRINTS("ERR-");
shi_reset_prepare();
CPRINTS("END\n");
return;
}
return;
}
/* We're now inside a transaction */
state = SHI_STATE_RECEIVING;
CPRINTF("CSL-RV-");
/* Setup deadline time for receiving */
shi_params.rx_deadline = get_time();
shi_params.rx_deadline.val += SHI_CMD_RX_TIMEOUT_US;
/* Read first three bytes to parse which protocol is receiving */
shi_parse_header();
}
/*****************************************************************************/
/* Hook functions for chipset and initialization */
/* Reset SHI bus and prepare next transaction */
static void shi_reset_prepare(void)
{
uint16_t i;
state = SHI_STATE_NOT_READY;
/* 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;
/* Record last IBUFSTAT for glitch case */
shi_params.pre_ibufstat = NPCX_IBUFSTAT;
/*
* 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;
CPRINTF("RDY-");
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, shi_reset_prepare, HOOK_PRIO_DEFAULT);
/* Disable SHI bus */
static void shi_disable(void)
{
state = SHI_STATE_DISABLED;
/* Disable pullup and interrupts on SHI_CS_L */
gpio_set_flags(GPIO_SHI_CS_L, GPIO_INPUT);
/* Set SPI pins to inputs so we don't leak power when AP is off */
gpio_config_module(MODULE_SPI, 0);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, 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);
/*
* 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);
/*
* 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;
/* Clear SHI events status register */
NPCX_EVSTAT = 0XFF;
/* Enable SHI_CS_L interrupt */
gpio_enable_interrupt(GPIO_SHI_CS_L);
/* If chipset is already on, prepare for transactions */
#if !(DEBUG_SHI)
if (chipset_in_state(CHIPSET_STATE_ON))
#endif
shi_reset_prepare();
}
DECLARE_HOOK(HOOK_INIT, shi_init, HOOK_PRIO_DEFAULT);
/**
* Get protocol information
*/
static int 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 = (1 << 2) | (1 << 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));