ec_host_cmd: add NPCX SHI peripheral for the host commands
This commit adds the support for host commands being transported by the Serial Host Interface on the NPCX SoC. Signed-off-by: Michał Barnaś <mb@semihalf.com>
This commit is contained in:
parent
cbb6742855
commit
3ae105e76b
9 changed files with 1264 additions and 0 deletions
|
@ -9,3 +9,8 @@ zephyr_library_sources_ifdef(
|
|||
zephyr_library_sources_ifdef(
|
||||
CONFIG_EC_HOST_CMD_PERIPH_ESPI
|
||||
ec_host_cmd_periph_espi.c)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_EC_HOST_CMD_PERIPH_SHI_NPCX
|
||||
ec_host_cmd_periph_shi_npcx.c)
|
||||
|
||||
|
|
|
@ -35,6 +35,45 @@ config EC_HOST_CMD_PERIPH_ESPI
|
|||
Enable support for Embedded Controller host commands using
|
||||
the eSPI bus.
|
||||
|
||||
config EC_HOST_CMD_PERIPH_SHI
|
||||
bool "Host commands support using SHI"
|
||||
help
|
||||
Enable support for Embedded Controller host commands using
|
||||
the Serial Host Interface.
|
||||
|
||||
endchoice
|
||||
|
||||
if EC_HOST_CMD_PERIPH_SHI
|
||||
|
||||
choice EC_HOST_CMD_PERIPH_SHI_DRIVER
|
||||
prompt "SHI driver"
|
||||
default EC_HOST_CMD_PERIPH_SHI_NPCX if SOC_FAMILY_NPCX
|
||||
|
||||
config EC_HOST_CMD_PERIPH_SHI_NPCX
|
||||
bool "SHI by Nuvoton"
|
||||
depends on DT_HAS_NUVOTON_NPCX_SHI_ENABLED
|
||||
help
|
||||
This option enables the driver for SHI peripheral in the
|
||||
Nuvoton NPCX chip.
|
||||
|
||||
endchoice
|
||||
|
||||
config EC_HOST_CMD_PERIPH_SHI_MAX_REQUEST
|
||||
int "Max data size for the version 3 request packet"
|
||||
default 544 if EC_HOST_CMD_PERIPH_SHI_NPCX
|
||||
help
|
||||
This option indicates maximum data size for a version 3 request
|
||||
packet. This must be big enough to handle a request header of host
|
||||
command, flash write offset/size, and 512 bytes of flash data.
|
||||
|
||||
config EC_HOST_CMD_PERIPH_SHI_MAX_RESPONSE
|
||||
int "Max data size for the version 3 response packet"
|
||||
default 544 if EC_HOST_CMD_PERIPH_SHI_NPCX
|
||||
help
|
||||
This option indicates maximum data size for a version 3 response
|
||||
packet. This must be big enough to handle a response header of host
|
||||
command, flash read offset/size, and 512 bytes of flash data.
|
||||
|
||||
endif # EC_HOST_CMD_PERIPH_SHI
|
||||
|
||||
endif # EC_HOST_CMD_PERIPH
|
||||
|
|
101
drivers/ec_host_cmd_periph/ec_host_cmd_periph_shi.h
Normal file
101
drivers/ec_host_cmd_periph/ec_host_cmd_periph_shi.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_EC_HOST_CMD_PERIPH_SHI_H_
|
||||
#define ZEPHYR_DRIVERS_EC_HOST_CMD_PERIPH_SHI_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
|
||||
/*
|
||||
* Byte codes returned by EC over SPI interface.
|
||||
*
|
||||
* These can be used by the AP to debug the EC interface, and to determine
|
||||
* when the EC is not in a state where it will ever get around to responding
|
||||
* to the AP.
|
||||
*
|
||||
* Example of sequence of bytes read from EC for a current good transfer:
|
||||
* 1. - - AP asserts chip select (CS#)
|
||||
* 2. EC_SHI_OLD_READY - AP sends first byte(s) of request
|
||||
* 3. - - EC starts handling CS# interrupt
|
||||
* 4. EC_SHI_RECEIVING - AP sends remaining byte(s) of request
|
||||
* 5. EC_SHI_PROCESSING - EC starts processing request; AP is clocking in
|
||||
* bytes looking for EC_SHI_FRAME_START
|
||||
* 6. - - EC finishes processing and sets up response
|
||||
* 7. EC_SHI_FRAME_START - AP reads frame byte
|
||||
* 8. (response packet) - AP reads response packet
|
||||
* 9. EC_SHI_PAST_END - Any additional bytes read by AP
|
||||
* 10 - - AP deasserts chip select
|
||||
* 11 - - EC processes CS# interrupt and sets up DMA for
|
||||
* next request
|
||||
*
|
||||
* If the AP is waiting for EC_SHI_FRAME_START and sees any value other than
|
||||
* the following byte values:
|
||||
* EC_SHI_OLD_READY
|
||||
* EC_SHI_RX_READY
|
||||
* EC_SHI_RECEIVING
|
||||
* EC_SHI_PROCESSING
|
||||
*
|
||||
* Then the EC found an error in the request, or was not ready for the request
|
||||
* and lost data. The AP should give up waiting for EC_SHI_FRAME_START,
|
||||
* because the EC is unable to tell when the AP is done sending its request.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Framing byte which precedes a response packet from the EC. After sending a
|
||||
* request, the AP will clock in bytes until it sees the framing byte, then
|
||||
* clock in the response packet.
|
||||
*/
|
||||
#define EC_SHI_FRAME_START 0xec
|
||||
|
||||
/*
|
||||
* Padding bytes which are clocked out after the end of a response packet.
|
||||
*/
|
||||
#define EC_SHI_PAST_END 0xed
|
||||
|
||||
/*
|
||||
* EC is ready to receive, and has ignored the byte sent by the AP. EC expects
|
||||
* that the AP will send a valid packet header (starting with
|
||||
* EC_COMMAND_PROTOCOL_3) in the next 32 bytes.
|
||||
*
|
||||
* NOTE: Some SPI configurations place the Most Significant Bit on SDO when
|
||||
* CS goes low. This macro has the Most Significant Bit set to zero,
|
||||
* so SDO will not be driven high when CS goes low.
|
||||
*/
|
||||
#define EC_SHI_RX_READY 0x78
|
||||
|
||||
/*
|
||||
* EC has started receiving the request from the AP, but hasn't started
|
||||
* processing it yet.
|
||||
*/
|
||||
#define EC_SHI_RECEIVING 0xf9
|
||||
|
||||
/* EC has received the entire request from the AP and is processing it. */
|
||||
#define EC_SHI_PROCESSING 0xfa
|
||||
|
||||
/*
|
||||
* EC received bad data from the AP, such as a packet header with an invalid
|
||||
* length. EC will ignore all data until chip select deasserts.
|
||||
*/
|
||||
#define EC_SHI_RX_BAD_DATA 0xfb
|
||||
|
||||
/*
|
||||
* EC received data from the AP before it was ready. That is, the AP asserted
|
||||
* chip select and started clocking data before the EC was ready to receive it.
|
||||
* EC will ignore all data until chip select deasserts.
|
||||
*/
|
||||
#define EC_SHI_NOT_READY 0xfc
|
||||
|
||||
/*
|
||||
* EC was ready to receive a request from the AP. EC has treated the byte sent
|
||||
* by the AP as part of a request packet, or (for old-style ECs) is processing
|
||||
* a fully received packet but is not ready to respond yet.
|
||||
*/
|
||||
#define EC_SHI_OLD_READY 0xfd
|
||||
|
||||
/* Supported version of host commands protocol. */
|
||||
#define EC_HOST_REQUEST_VERSION 3
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_EC_HOST_CMD_PERIPH_SHI_H_ */
|
976
drivers/ec_host_cmd_periph/ec_host_cmd_periph_shi_npcx.c
Normal file
976
drivers/ec_host_cmd_periph/ec_host_cmd_periph_shi_npcx.c
Normal file
|
@ -0,0 +1,976 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nuvoton_npcx_shi
|
||||
|
||||
#include "ec_host_cmd_periph_shi.h"
|
||||
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/ec_host_cmd_periph/ec_host_cmd_periph.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/device_runtime.h>
|
||||
|
||||
#include <soc_miwu.h>
|
||||
|
||||
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "Invalid number of NPCX SHI peripherals");
|
||||
|
||||
LOG_MODULE_REGISTER(host_cmd_shi_npcx, CONFIG_EC_HC_LOG_LEVEL);
|
||||
|
||||
/* Driver convenience defines */
|
||||
#define HAL_INSTANCE(dev) (struct shi_reg *)(((const struct shi_npcx_config *)(dev)->config)->base)
|
||||
|
||||
/* Full output buffer size */
|
||||
#define SHI_OBUF_FULL_SIZE DT_INST_PROP(0, buffer_tx_size)
|
||||
/* Full input buffer size */
|
||||
#define SHI_IBUF_FULL_SIZE DT_INST_PROP(0, buffer_rx_size)
|
||||
/* Configure the IBUFLVL2 = the size of V3 protocol header */
|
||||
#define SHI_IBUFLVL2_THRESHOLD (sizeof(struct ec_host_cmd_request_header))
|
||||
/* Half output buffer size */
|
||||
#define SHI_OBUF_HALF_SIZE (SHI_OBUF_FULL_SIZE / 2)
|
||||
/* Half input buffer size */
|
||||
#define SHI_IBUF_HALF_SIZE (SHI_IBUF_FULL_SIZE / 2)
|
||||
|
||||
/*
|
||||
* 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 SPI controller 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 EC_SHI_CMD_RX_TIMEOUT_US 8192
|
||||
|
||||
/*
|
||||
* 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 EC_SHI_OUT_PREAMBLE_LENGTH 2
|
||||
|
||||
/*
|
||||
* Space allocation of the past-end status byte (EC_SHI_PAST_END) in the out_msg buffer.
|
||||
*/
|
||||
#define EC_SHI_PAST_END_LENGTH 1
|
||||
|
||||
/*
|
||||
* Space allocation of the frame status byte (EC_SHI_FRAME_START) in the out_msg buffer.
|
||||
*/
|
||||
#define EC_SHI_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 EC_SHI_PROTO3_OVERHEAD (EC_SHI_PAST_END_LENGTH + EC_SHI_FRAME_START_LENGTH)
|
||||
|
||||
/*
|
||||
* Our input and output msg buffers. These must be large enough for our largest
|
||||
* message, including protocol overhead. The pointers after the protocol
|
||||
* overhead, as passed to the host command handler, must be 32-bit aligned.
|
||||
*/
|
||||
#define SHI_OUT_START_PAD (4 * (EC_SHI_FRAME_START_LENGTH / 4 + 1))
|
||||
#define SHI_OUT_END_PAD (4 * (EC_SHI_PAST_END_LENGTH / 4 + 1))
|
||||
|
||||
enum shi_npcx_state {
|
||||
SHI_STATE_NONE = -1,
|
||||
/* SHI not enabled (initial state, and when chipset is off) */
|
||||
SHI_STATE_DISABLED = 0,
|
||||
/* Ready to receive next request */
|
||||
SHI_STATE_READY_TO_RECV,
|
||||
/* Receiving request */
|
||||
SHI_STATE_RECEIVING,
|
||||
/* Processing request */
|
||||
SHI_STATE_PROCESSING,
|
||||
/* Canceling response since CS deasserted and output NOT_READY byte */
|
||||
SHI_STATE_CNL_RESP_NOT_RDY,
|
||||
/* Sending response */
|
||||
SHI_STATE_SENDING,
|
||||
/* Received data is invalid */
|
||||
SHI_STATE_BAD_RECEIVED_DATA,
|
||||
};
|
||||
|
||||
/* Device config */
|
||||
struct shi_npcx_config {
|
||||
/* Serial Host Interface (SHI) base address */
|
||||
uintptr_t base;
|
||||
/* Clock configuration */
|
||||
struct npcx_clk_cfg clk_cfg;
|
||||
/* Pin control configuration */
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
/* Chip-select interrupts */
|
||||
int irq;
|
||||
struct npcx_wui shi_cs_wui;
|
||||
};
|
||||
|
||||
struct shi_npcx_data {
|
||||
/* Handler mutexes */
|
||||
struct k_sem handler_owns;
|
||||
struct k_sem dev_owns;
|
||||
/* Communication status */
|
||||
enum shi_npcx_state state;
|
||||
enum shi_npcx_state last_error_state;
|
||||
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 */
|
||||
uint32_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 */
|
||||
uint64_t rx_deadline; /* Deadline of receiving */
|
||||
/* Buffers */
|
||||
uint8_t out_msg_padded[SHI_OUT_START_PAD + CONFIG_EC_HOST_CMD_PERIPH_SHI_MAX_RESPONSE +
|
||||
SHI_OUT_END_PAD] __aligned(4);
|
||||
uint8_t *const out_msg;
|
||||
uint8_t in_msg[CONFIG_EC_HOST_CMD_PERIPH_SHI_MAX_REQUEST] __aligned(4);
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
static void shi_npcx_reset_prepare(const struct device *dev);
|
||||
|
||||
/* Read pointer of input or output buffer by consecutive reading */
|
||||
static uint32_t shi_npcx_read_buf_pointer(struct shi_reg *const inst)
|
||||
{
|
||||
uint8_t stat;
|
||||
|
||||
/* Wait for two consecutive equal values read */
|
||||
do {
|
||||
stat = inst->IBUFSTAT;
|
||||
} while (stat != inst->IBUFSTAT);
|
||||
|
||||
return (uint32_t)stat;
|
||||
}
|
||||
|
||||
/*
|
||||
* Valid offset of SHI output buffer to write.
|
||||
* When SIMUL bit is set, IBUFPTR can be used instead of OBUFPTR
|
||||
*/
|
||||
static uint32_t shi_npcx_valid_obuf_offset(struct shi_reg *const inst)
|
||||
{
|
||||
return (shi_npcx_read_buf_pointer(inst) + EC_SHI_OUT_PREAMBLE_LENGTH) % SHI_OBUF_FULL_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine write SHI next half output buffer from msg buffer
|
||||
*/
|
||||
static void shi_npcx_write_half_outbuf(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
const uint32_t size = MIN(SHI_OBUF_HALF_SIZE, data->sz_response - data->sz_sending);
|
||||
uint8_t *obuf_ptr = (uint8_t *)data->tx_buf;
|
||||
const uint8_t *obuf_end = obuf_ptr + size;
|
||||
uint8_t *msg_ptr = data->tx_msg;
|
||||
|
||||
/* Fill half output buffer */
|
||||
while (obuf_ptr != obuf_end) {
|
||||
*obuf_ptr++ = *msg_ptr++;
|
||||
}
|
||||
|
||||
data->sz_sending += size;
|
||||
data->tx_buf = obuf_ptr;
|
||||
data->tx_msg = msg_ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine read SHI input buffer to msg buffer until
|
||||
* we have received a certain number of bytes
|
||||
*/
|
||||
static int shi_npcx_read_inbuf_wait(const struct device *dev, uint32_t szbytes)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
|
||||
/* Copy data to msg buffer from input buffer */
|
||||
for (uint32_t i = 0; i < szbytes; i++, data->sz_received++) {
|
||||
/*
|
||||
* If input buffer pointer equals pointer which wants to read,
|
||||
* it means data is not ready.
|
||||
*/
|
||||
while (data->rx_buf == inst->IBUF + shi_npcx_read_buf_pointer(inst)) {
|
||||
if (k_cycle_get_64() >= data->rx_deadline) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy data to msg buffer */
|
||||
*data->rx_msg++ = *data->rx_buf++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This routine fills out all SHI output buffer with status byte */
|
||||
static void shi_npcx_fill_out_status(struct shi_reg *const inst, uint8_t status)
|
||||
{
|
||||
uint8_t start, end;
|
||||
volatile uint8_t *fill_ptr;
|
||||
volatile uint8_t *fill_end;
|
||||
volatile uint8_t *obuf_end;
|
||||
|
||||
/*
|
||||
* Disable interrupts in case the interfere by the other interrupts.
|
||||
* Use __disable_irq/__enable_irq instead of using irq_lock/irq_unlock
|
||||
* here because irq_lock/irq_unlock leave some system exceptions (like
|
||||
* SVC, NMI, and faults) still enabled.
|
||||
*/
|
||||
__disable_irq();
|
||||
|
||||
/*
|
||||
* Fill out output buffer with status byte and leave a gap for PREAMBLE.
|
||||
* The gap guarantees the synchronization. The critical section should
|
||||
* be done within this gap. No racing happens.
|
||||
*/
|
||||
start = shi_npcx_valid_obuf_offset(inst);
|
||||
end = (start + SHI_OBUF_FULL_SIZE - EC_SHI_OUT_PREAMBLE_LENGTH) % SHI_OBUF_FULL_SIZE;
|
||||
|
||||
fill_ptr = inst->OBUF + start;
|
||||
fill_end = inst->OBUF + end;
|
||||
obuf_end = inst->OBUF + SHI_OBUF_FULL_SIZE;
|
||||
while (fill_ptr != fill_end) {
|
||||
*fill_ptr++ = status;
|
||||
if (fill_ptr == obuf_end) {
|
||||
fill_ptr = inst->OBUF;
|
||||
}
|
||||
}
|
||||
|
||||
/* End of critical section */
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/* This routine handles shi received unexpected data */
|
||||
static void shi_npcx_bad_received_data(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
|
||||
/* State machine mismatch, timeout, or protocol we can't handle. */
|
||||
shi_npcx_fill_out_status(inst, EC_SHI_RX_BAD_DATA);
|
||||
data->state = SHI_STATE_BAD_RECEIVED_DATA;
|
||||
|
||||
LOG_ERR("SHI bad data recv");
|
||||
LOG_DBG("BAD-");
|
||||
LOG_HEXDUMP_DBG(data->in_msg, data->sz_received, "in_msg=");
|
||||
|
||||
/* Reset shi's state machine for error recovery */
|
||||
shi_npcx_reset_prepare(dev);
|
||||
|
||||
LOG_DBG("END");
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine write SHI output buffer from msg buffer over halt of it.
|
||||
* It make sure we have enough time to handle next operations.
|
||||
*/
|
||||
static void shi_npcx_write_first_pkg_outbuf(const struct device *dev, uint16_t szbytes)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
uint8_t size, offset;
|
||||
volatile uint8_t *obuf_ptr;
|
||||
volatile uint8_t *obuf_end;
|
||||
uint8_t *msg_ptr;
|
||||
uint32_t half_buf_remain; /* Remains in half buffer are free to write */
|
||||
|
||||
/* Start writing at our current OBUF position */
|
||||
offset = shi_npcx_valid_obuf_offset(inst);
|
||||
obuf_ptr = inst->OBUF + offset;
|
||||
msg_ptr = data->tx_msg;
|
||||
|
||||
/* Fill up to OBUF mid point, or OBUF end */
|
||||
half_buf_remain = SHI_OBUF_HALF_SIZE - (offset % SHI_OBUF_HALF_SIZE);
|
||||
size = MIN(half_buf_remain, szbytes - data->sz_sending);
|
||||
obuf_end = obuf_ptr + size;
|
||||
while (obuf_ptr != obuf_end) {
|
||||
*obuf_ptr++ = *msg_ptr++;
|
||||
}
|
||||
|
||||
/* Track bytes sent for later accounting */
|
||||
data->sz_sending += size;
|
||||
|
||||
/* Write data to beginning of OBUF if we've reached the end */
|
||||
if (obuf_ptr == inst->OBUF + SHI_IBUF_FULL_SIZE) {
|
||||
obuf_ptr = inst->OBUF;
|
||||
}
|
||||
|
||||
/* Fill next half output buffer */
|
||||
size = MIN(SHI_OBUF_HALF_SIZE, szbytes - data->sz_sending);
|
||||
obuf_end = obuf_ptr + size;
|
||||
while (obuf_ptr != obuf_end) {
|
||||
*obuf_ptr++ = *msg_ptr++;
|
||||
}
|
||||
|
||||
/* Track bytes sent / last OBUF position written for later accounting */
|
||||
data->sz_sending += size;
|
||||
data->tx_buf = obuf_ptr;
|
||||
data->tx_msg = msg_ptr;
|
||||
}
|
||||
|
||||
static void shi_npcx_handle_host_package(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
uint32_t sz_inbuf_int = data->sz_request / SHI_IBUF_HALF_SIZE;
|
||||
uint32_t cnt_inbuf_int = data->sz_received / SHI_IBUF_HALF_SIZE;
|
||||
|
||||
if (sz_inbuf_int - cnt_inbuf_int) {
|
||||
/* Need to receive data from buffer */
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t remain_bytes = data->sz_request - data->sz_received;
|
||||
|
||||
/* Read remaining bytes from input buffer */
|
||||
if (!shi_npcx_read_inbuf_wait(dev, remain_bytes)) {
|
||||
return shi_npcx_bad_received_data(dev);
|
||||
}
|
||||
|
||||
/* Move to processing state */
|
||||
data->state = SHI_STATE_PROCESSING;
|
||||
LOG_DBG("PRC-");
|
||||
|
||||
/* Fill output buffer to indicate we`re processing request */
|
||||
shi_npcx_fill_out_status(inst, EC_SHI_PROCESSING);
|
||||
data->out_msg[0] = EC_SHI_FRAME_START;
|
||||
|
||||
/* Wake-up the HC handler thread */
|
||||
k_sem_give(&data->handler_owns);
|
||||
}
|
||||
|
||||
static int shi_npcx_host_request_expected_size(const struct ec_host_cmd_request_header *r)
|
||||
{
|
||||
/* Check host request version */
|
||||
if (r->prtcl_ver != EC_HOST_REQUEST_VERSION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reserved byte should be 0 */
|
||||
if (r->reserved) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sizeof(*r) + r->data_len;
|
||||
}
|
||||
|
||||
static void shi_npcx_parse_header(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
/* We're now inside a transaction */
|
||||
data->state = SHI_STATE_RECEIVING;
|
||||
LOG_DBG("RV-");
|
||||
|
||||
/* Setup deadline time for receiving */
|
||||
data->rx_deadline = k_cycle_get_64() + k_us_to_cyc_near64(EC_SHI_CMD_RX_TIMEOUT_US);
|
||||
|
||||
/* Wait for version, command, length bytes */
|
||||
if (!shi_npcx_read_inbuf_wait(dev, 3)) {
|
||||
return shi_npcx_bad_received_data(dev);
|
||||
}
|
||||
|
||||
if (data->in_msg[0] == EC_HOST_REQUEST_VERSION) {
|
||||
/* Protocol version 3 */
|
||||
struct ec_host_cmd_request_header *r =
|
||||
(struct ec_host_cmd_request_header *)data->in_msg;
|
||||
int pkt_size;
|
||||
|
||||
/*
|
||||
* If request is over half of input buffer, we need to modify the algorithm again.
|
||||
*/
|
||||
__ASSERT_NO_MSG(sizeof(*r) < SHI_IBUF_HALF_SIZE);
|
||||
|
||||
/* Wait for the rest of the command header */
|
||||
if (!shi_npcx_read_inbuf_wait(dev, sizeof(*r) - 3)) {
|
||||
return shi_npcx_bad_received_data(dev);
|
||||
}
|
||||
|
||||
/* Check how big the packet should be */
|
||||
pkt_size = shi_npcx_host_request_expected_size(r);
|
||||
if (pkt_size == 0 || pkt_size > sizeof(data->in_msg)) {
|
||||
return shi_npcx_bad_received_data(dev);
|
||||
}
|
||||
|
||||
/* Computing total bytes need to receive */
|
||||
data->sz_request = pkt_size;
|
||||
|
||||
shi_npcx_handle_host_package(dev);
|
||||
} else {
|
||||
/* Invalid version number */
|
||||
return shi_npcx_bad_received_data(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void shi_npcx_sec_ibf_int_enable(struct shi_reg *const inst, int enable)
|
||||
{
|
||||
if (enable) {
|
||||
/* Setup IBUFLVL2 threshold and enable it */
|
||||
inst->SHICFG5 |= BIT(NPCX_SHICFG5_IBUFLVL2DIS);
|
||||
SET_FIELD(inst->SHICFG5, NPCX_SHICFG5_IBUFLVL2, SHI_IBUFLVL2_THRESHOLD);
|
||||
inst->SHICFG5 &= ~BIT(NPCX_SHICFG5_IBUFLVL2DIS);
|
||||
|
||||
/* Enable IBHF2 event */
|
||||
inst->EVENABLE2 |= BIT(NPCX_EVENABLE2_IBHF2EN);
|
||||
} else {
|
||||
/* Disable IBHF2 event first */
|
||||
inst->EVENABLE2 &= ~BIT(NPCX_EVENABLE2_IBHF2EN);
|
||||
|
||||
/* Disable IBUFLVL2 and set threshold back to zero */
|
||||
inst->SHICFG5 |= BIT(NPCX_SHICFG5_IBUFLVL2DIS);
|
||||
SET_FIELD(inst->SHICFG5, NPCX_SHICFG5_IBUFLVL2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* This routine copies SHI half input buffer data to msg buffer */
|
||||
static void shi_npcx_read_half_inbuf(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
/*
|
||||
* Copy to read buffer until reaching middle/top address of
|
||||
* input buffer or completing receiving data
|
||||
*/
|
||||
do {
|
||||
/* Restore data to msg buffer */
|
||||
*data->rx_msg++ = *data->rx_buf++;
|
||||
data->sz_received++;
|
||||
} while (data->sz_received % SHI_IBUF_HALF_SIZE && data->sz_received != data->sz_request);
|
||||
}
|
||||
|
||||
/*
|
||||
* Avoid spamming the console with prints every IBF / IBHF interrupt, if
|
||||
* we find ourselves in an unexpected state.
|
||||
*/
|
||||
static void shi_npcx_log_unexpected_state(const struct device *dev, char *isr_name)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
if (data->state != data->last_error_state) {
|
||||
LOG_ERR("Unexpected state %d in %s ISR", data->state, isr_name);
|
||||
}
|
||||
|
||||
data->last_error_state = data->state;
|
||||
}
|
||||
|
||||
static void shi_npcx_handle_cs_assert(const struct device *dev)
|
||||
{
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
/* If not enabled, ignore glitches on SHI_CS_L */
|
||||
if (data->state == SHI_STATE_DISABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOT_READY should be sent and there're no spi transaction now. */
|
||||
if (data->state == SHI_STATE_CNL_RESP_NOT_RDY) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Chip select is low = asserted */
|
||||
if (data->state != SHI_STATE_READY_TO_RECV) {
|
||||
/* State machine should be reset in EVSTAT_EOR ISR */
|
||||
LOG_ERR("Unexpected state %d in CS ISR", data->state);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("CSL-");
|
||||
|
||||
/*
|
||||
* Clear possible EOR event from previous transaction since it's
|
||||
* irrelevant now that CS is re-asserted.
|
||||
*/
|
||||
inst->EVSTAT = BIT(NPCX_EVSTAT_EOR);
|
||||
}
|
||||
|
||||
static void shi_npcx_handle_cs_deassert(const struct device *dev)
|
||||
{
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
/*
|
||||
* If the buffer is still used by the host command.
|
||||
* Change state machine for response handler.
|
||||
*/
|
||||
if (data->state == SHI_STATE_PROCESSING) {
|
||||
/*
|
||||
* Mark not ready to prevent the other
|
||||
* transaction immediately
|
||||
*/
|
||||
shi_npcx_fill_out_status(inst, EC_SHI_NOT_READY);
|
||||
|
||||
data->state = SHI_STATE_CNL_RESP_NOT_RDY;
|
||||
|
||||
/*
|
||||
* Disable SHI interrupt, it will remain disabled until shi_send_response_packet()
|
||||
* is called and CS is asserted for a new transaction.
|
||||
*/
|
||||
irq_disable(DT_INST_IRQN(0));
|
||||
|
||||
LOG_DBG("CNL-");
|
||||
return;
|
||||
/* Next transaction but we're not ready */
|
||||
} else if (data->state == SHI_STATE_CNL_RESP_NOT_RDY) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Error state for checking*/
|
||||
if (data->state != SHI_STATE_SENDING) {
|
||||
shi_npcx_log_unexpected_state(dev, "CSNRE");
|
||||
}
|
||||
|
||||
/* reset SHI and prepare to next transaction again */
|
||||
shi_npcx_reset_prepare(dev);
|
||||
LOG_DBG("END\n");
|
||||
}
|
||||
|
||||
static void shi_npcx_handle_input_buf_half_full(const struct device *dev)
|
||||
{
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
if (data->state == SHI_STATE_RECEIVING) {
|
||||
/* Read data from input to msg buffer */
|
||||
shi_npcx_read_half_inbuf(dev);
|
||||
return shi_npcx_handle_host_package(dev);
|
||||
} else if (data->state == SHI_STATE_SENDING) {
|
||||
/* Write data from msg buffer to output buffer */
|
||||
if (data->tx_buf == inst->OBUF + SHI_OBUF_FULL_SIZE) {
|
||||
/* Write data from bottom address again */
|
||||
data->tx_buf = inst->OBUF;
|
||||
shi_npcx_write_half_outbuf(dev);
|
||||
}
|
||||
} else if (data->state == SHI_STATE_PROCESSING) {
|
||||
/* Wait for host to handle request */
|
||||
} else {
|
||||
/* Unexpected status */
|
||||
shi_npcx_log_unexpected_state(dev, "IBHF");
|
||||
}
|
||||
}
|
||||
|
||||
static void shi_npcx_handle_input_buf_full(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
|
||||
if (data->state == SHI_STATE_RECEIVING) {
|
||||
/* read data from input to msg buffer */
|
||||
shi_npcx_read_half_inbuf(dev);
|
||||
/* Read to bottom address again */
|
||||
data->rx_buf = inst->IBUF;
|
||||
return shi_npcx_handle_host_package(dev);
|
||||
} else if (data->state == SHI_STATE_SENDING) {
|
||||
/* Write data from msg buffer to output buffer */
|
||||
if (data->tx_buf == inst->OBUF + SHI_OBUF_HALF_SIZE) {
|
||||
shi_npcx_write_half_outbuf(dev);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (data->state == SHI_STATE_PROCESSING) {
|
||||
/* Wait for host to handle request */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unexpected status */
|
||||
shi_npcx_log_unexpected_state(dev, "IBF");
|
||||
}
|
||||
|
||||
static void shi_npcx_isr(const struct device *dev)
|
||||
{
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
uint8_t stat;
|
||||
uint8_t stat2;
|
||||
|
||||
/* Read status register and clear interrupt status early */
|
||||
stat = inst->EVSTAT;
|
||||
inst->EVSTAT = stat;
|
||||
stat2 = inst->EVSTAT2;
|
||||
|
||||
/* SHI CS pin is asserted in EVSTAT2 */
|
||||
if (IS_BIT_SET(stat2, NPCX_EVSTAT2_CSNFE)) {
|
||||
/* Clear pending bit of CSNFE */
|
||||
inst->EVSTAT2 = BIT(NPCX_EVSTAT2_CSNFE);
|
||||
LOG_DBG("CSNFE-");
|
||||
|
||||
/*
|
||||
* BUSY bit is set when SHI_CS is asserted. If not, leave it for
|
||||
* SHI_CS de-asserted event.
|
||||
*/
|
||||
if (!IS_BIT_SET(inst->SHICFG2, NPCX_SHICFG2_BUSY)) {
|
||||
LOG_DBG("CSNB-");
|
||||
return;
|
||||
}
|
||||
|
||||
shi_npcx_handle_cs_assert(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* End of data for read/write transaction. i.e. SHI_CS is deasserted.
|
||||
* Host completed or aborted transaction
|
||||
*
|
||||
* EOR has the limitation that it will not be set even if the SHI_CS is deasserted without
|
||||
* SPI clocks. The new SHI module introduce the CSNRE bit which will be set when SHI_CS is
|
||||
* deasserted regardless of SPI clocks.
|
||||
*/
|
||||
if (IS_BIT_SET(stat2, NPCX_EVSTAT2_CSNRE)) {
|
||||
/* Clear pending bit of CSNRE */
|
||||
inst->EVSTAT2 = BIT(NPCX_EVSTAT2_CSNRE);
|
||||
|
||||
/*
|
||||
* We're not in proper state.
|
||||
* Mark not ready to abort next transaction
|
||||
*/
|
||||
LOG_DBG("CSH-");
|
||||
return shi_npcx_handle_cs_deassert(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of bytes received reaches the size of
|
||||
* protocol V3 header(=8) after CS asserted.
|
||||
*/
|
||||
if (IS_BIT_SET(stat2, NPCX_EVSTAT2_IBHF2)) {
|
||||
/* Clear IBHF2 */
|
||||
inst->EVSTAT2 = BIT(NPCX_EVSTAT2_IBHF2);
|
||||
LOG_DBG("HDR-");
|
||||
|
||||
/* Disable second IBF interrupt and start to parse header */
|
||||
shi_npcx_sec_ibf_int_enable(inst, 0);
|
||||
shi_npcx_parse_header(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate input/output buffer pointer reaches the half buffer size.
|
||||
* Transaction is processing.
|
||||
*/
|
||||
if (IS_BIT_SET(stat, NPCX_EVSTAT_IBHF)) {
|
||||
return shi_npcx_handle_input_buf_half_full(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate input/output buffer pointer reaches the full buffer size.
|
||||
* Transaction is processing.
|
||||
*/
|
||||
if (IS_BIT_SET(stat, NPCX_EVSTAT_IBF)) {
|
||||
return shi_npcx_handle_input_buf_full(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void shi_npcx_reset_prepare(const struct device *dev)
|
||||
{
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
uint32_t i;
|
||||
|
||||
data->state = SHI_STATE_DISABLED;
|
||||
|
||||
irq_disable(DT_INST_IRQN(0));
|
||||
|
||||
/* Disable SHI unit to clear all status bits */
|
||||
inst->SHICFG1 &= ~BIT(NPCX_SHICFG1_EN);
|
||||
|
||||
/* Initialize parameters of next transaction */
|
||||
data->rx_msg = data->in_msg;
|
||||
data->tx_msg = data->out_msg;
|
||||
data->rx_buf = inst->IBUF;
|
||||
data->tx_buf = inst->OBUF;
|
||||
data->sz_received = 0;
|
||||
data->sz_sending = 0;
|
||||
data->sz_request = 0;
|
||||
data->sz_response = 0;
|
||||
|
||||
/*
|
||||
* Fill output buffer to indicate we`re
|
||||
* ready to receive next transaction.
|
||||
*/
|
||||
for (i = 1; i < SHI_OBUF_FULL_SIZE; i++) {
|
||||
inst->OBUF[i] = EC_SHI_RECEIVING;
|
||||
}
|
||||
inst->OBUF[0] = EC_SHI_RX_READY;
|
||||
|
||||
/* SHI/Host Write/input buffer wrap-around enable */
|
||||
inst->SHICFG1 = BIT(NPCX_SHICFG1_IWRAP) | BIT(NPCX_SHICFG1_WEN) | BIT(NPCX_SHICFG1_EN);
|
||||
|
||||
data->state = SHI_STATE_READY_TO_RECV;
|
||||
data->last_error_state = SHI_STATE_NONE;
|
||||
|
||||
shi_npcx_sec_ibf_int_enable(inst, 1);
|
||||
irq_enable(DT_INST_IRQN(0));
|
||||
|
||||
LOG_DBG("RDY-");
|
||||
}
|
||||
|
||||
static int shi_npcx_enable(const struct device *dev)
|
||||
{
|
||||
const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
|
||||
const struct shi_npcx_config *const config = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = clock_control_on(clk_dev, (clock_control_subsys_t *)&config->clk_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Turn on SHI clock fail %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
shi_npcx_reset_prepare(dev);
|
||||
npcx_miwu_irq_disable(&config->shi_cs_wui);
|
||||
|
||||
/* Configure pin control for SHI */
|
||||
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("shi_npcx pinctrl setup failed (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
|
||||
npcx_miwu_irq_enable(&config->shi_cs_wui);
|
||||
irq_enable(DT_INST_IRQN(0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_npcx_disable(const struct device *dev)
|
||||
{
|
||||
const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
|
||||
const struct shi_npcx_config *const config = dev->config;
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
data->state = SHI_STATE_DISABLED;
|
||||
|
||||
irq_disable(DT_INST_IRQN(0));
|
||||
npcx_miwu_irq_disable(&config->shi_cs_wui);
|
||||
|
||||
/* Configure pin control back to GPIO */
|
||||
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("KB Raw pinctrl setup failed (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clock_control_off(clk_dev, (clock_control_subsys_t *)&config->clk_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Turn off SHI clock fail %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_npcx_init_registers(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
const struct shi_npcx_config *const config = dev->config;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
|
||||
|
||||
/* Turn on shi device clock first */
|
||||
ret = clock_control_on(clk_dev, (clock_control_subsys_t *)&config->clk_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Turn on SHI clock fail %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If booter doesn't set the host interface type */
|
||||
if (!NPCX_BOOTER_IS_HIF_TYPE_SET()) {
|
||||
npcx_host_interface_sel(NPCX_HIF_TYPE_ESPI_SHI);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
inst->SHICFG1 = BIT(NPCX_SHICFG1_IWRAP);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
inst->SHICFG2 = BIT(NPCX_SHICFG2_SIMUL);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
inst->EVENABLE =
|
||||
BIT(NPCX_EVENABLE_EOREN) | BIT(NPCX_EVENABLE_IBHFEN) | BIT(NPCX_EVENABLE_IBFEN);
|
||||
|
||||
/*
|
||||
* EVENABLE2 (Event Enable 2) setting
|
||||
* [2] - CSNFEEN = 1: SHI_CS Falling Edge Interrupt Enable
|
||||
* [1] - CSNREEN = 1: SHI_CS Rising Edge Interrupt Enable
|
||||
* [0] - IBHF2EN = 0: Input Buffer Half Full 2 Interrupt Enable
|
||||
*/
|
||||
inst->EVENABLE2 = BIT(NPCX_EVENABLE2_CSNREEN) | BIT(NPCX_EVENABLE2_CSNFEEN);
|
||||
|
||||
/* Clear SHI events status register */
|
||||
inst->EVSTAT = 0xff;
|
||||
|
||||
npcx_miwu_interrupt_configure(&config->shi_cs_wui, NPCX_MIWU_MODE_EDGE, NPCX_MIWU_TRIG_LOW);
|
||||
|
||||
/* SHI interrupt installation */
|
||||
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), shi_npcx_isr, DEVICE_DT_INST_GET(0),
|
||||
0);
|
||||
|
||||
shi_npcx_enable(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int shi_npcx_init_peripheral(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
/* Allow writing to rx buff at startup and block on reading. */
|
||||
k_sem_init(&data->handler_owns, 0, 1);
|
||||
k_sem_init(&data->dev_owns, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_npcx_init(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = shi_npcx_init_registers(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = shi_npcx_init_peripheral(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_device_init_suspended(dev);
|
||||
return pm_device_runtime_enable(dev);
|
||||
}
|
||||
|
||||
static int shi_npcx_periph_init_context(const struct device *dev,
|
||||
struct ec_host_cmd_periph_rx_ctx *rx_ctx)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
rx_ctx->dev_owns = &data->dev_owns;
|
||||
rx_ctx->handler_owns = &data->handler_owns;
|
||||
rx_ctx->buf = data->in_msg;
|
||||
rx_ctx->len = &data->sz_received;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_npcx_periph_send(const struct device *dev,
|
||||
const struct ec_host_cmd_periph_tx_buf *in_buf)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
uint8_t *out_buf = data->out_msg + EC_SHI_FRAME_START_LENGTH;
|
||||
|
||||
/*
|
||||
* Disable interrupts. This routine is not called from interrupt context and buffer
|
||||
* underrun will likely occur if it is preempted after writing its initial reply byte.
|
||||
* Also, we must be sure our state doesn't unexpectedly change, in case we're expected
|
||||
* to take RESP_NOT_RDY actions.
|
||||
*/
|
||||
__disable_irq();
|
||||
|
||||
memcpy(out_buf, in_buf->buf, in_buf->len);
|
||||
|
||||
if (data->state == SHI_STATE_PROCESSING) {
|
||||
/* Append our past-end byte, which we reserved space for. */
|
||||
((uint8_t *)out_buf)[in_buf->len] = EC_SHI_PAST_END;
|
||||
|
||||
/* Computing sending bytes of response */
|
||||
data->sz_response = in_buf->len + EC_SHI_PROTO3_OVERHEAD;
|
||||
|
||||
/* Start to fill output buffer with msg buffer */
|
||||
shi_npcx_write_first_pkg_outbuf(dev, data->sz_response);
|
||||
|
||||
/* Transmit the reply */
|
||||
data->state = SHI_STATE_SENDING;
|
||||
LOG_DBG("SND-");
|
||||
} else if (data->state == SHI_STATE_CNL_RESP_NOT_RDY) {
|
||||
/*
|
||||
* If we're not processing, then the AP has already terminated
|
||||
* the transaction, and won't be listening for a response.
|
||||
* Reset state machine for next transaction.
|
||||
*/
|
||||
shi_npcx_reset_prepare(dev);
|
||||
LOG_DBG("END\n");
|
||||
} else {
|
||||
LOG_ERR("Unexpected state %d in response handler", data->state);
|
||||
}
|
||||
|
||||
__enable_irq();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ec_host_cmd_periph_api ec_host_cmd_api = {
|
||||
.init = shi_npcx_periph_init_context,
|
||||
.send = shi_npcx_periph_send,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
static int shi_npcx_pm_cb(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
shi_npcx_disable(dev);
|
||||
break;
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
shi_npcx_enable(dev);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Assume only one peripheral */
|
||||
PM_DEVICE_DT_INST_DEFINE(0, shi_npcx_pm_cb);
|
||||
|
||||
PINCTRL_DT_INST_DEFINE(0);
|
||||
static const struct shi_npcx_config shi_cfg = {
|
||||
.base = DT_INST_REG_ADDR(0),
|
||||
.clk_cfg = NPCX_DT_CLK_CFG_ITEM(0),
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
|
||||
.irq = DT_INST_IRQN(0),
|
||||
.shi_cs_wui = NPCX_DT_WUI_ITEM_BY_NAME(0, shi_cs_wui),
|
||||
};
|
||||
|
||||
static struct shi_npcx_data shi_data = {
|
||||
.state = SHI_STATE_DISABLED,
|
||||
.last_error_state = SHI_STATE_NONE,
|
||||
.out_msg = shi_data.out_msg_padded + SHI_OUT_START_PAD - EC_SHI_FRAME_START_LENGTH,
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, shi_npcx_init, PM_DEVICE_DT_INST_GET(0), &shi_data, &shi_cfg, POST_KERNEL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &ec_host_cmd_api);
|
|
@ -354,6 +354,16 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
shi0: shi@4000f000 {
|
||||
compatible = "nuvoton,npcx-shi";
|
||||
reg = <0x4000f000 0x120>;
|
||||
interrupts = <18 1>;
|
||||
clocks = <&pcc NPCX_CLOCK_BUS_APB3 NPCX_PWDWN_CTL5 1>;
|
||||
status = "disabled";
|
||||
buffer-rx-size = <128>;
|
||||
buffer-tx-size = <128>;
|
||||
};
|
||||
|
||||
host_sub: lpc@400c1000 {
|
||||
compatible = "nuvoton,npcx-host-sub";
|
||||
/* host sub-module register address & size */
|
||||
|
|
28
dts/bindings/shi/nuvoton,npcx-shi.yaml
Normal file
28
dts/bindings/shi/nuvoton,npcx-shi.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Nuvoton, NPCX Serial Host Interface (SHI) node
|
||||
|
||||
compatible: "nuvoton,npcx-shi"
|
||||
|
||||
include: [pinctrl-device.yaml, shi-device.yaml]
|
||||
|
||||
properties:
|
||||
pinctrl-0:
|
||||
required: true
|
||||
|
||||
pinctrl-names:
|
||||
required: true
|
||||
|
||||
clocks:
|
||||
required: true
|
||||
description: configurations of device source clock controller
|
||||
|
||||
shi-cs-wui:
|
||||
type: phandle
|
||||
required: true
|
||||
description: |
|
||||
Mapping table between Wake-Up Input (WUI) and SHI_CS signal.
|
||||
|
||||
For example the WUI mapping on NPCX7 would be
|
||||
shi-cs-wui = <&wui_io53>;
|
20
dts/bindings/shi/shi-device.yaml
Normal file
20
dts/bindings/shi/shi-device.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
include: [base.yaml]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
buffer-rx-size:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Size of the internal buffer used for receiving data on the bus.
|
||||
|
||||
buffer-tx-size:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Size of the internal buffer used for transmitting data over the bus.
|
|
@ -1633,4 +1633,88 @@ struct kbs_reg {
|
|||
#define KBS_CFG_INDX_RTYTO 2 /* Keyboard Scan Retry Timeout */
|
||||
#define KBS_CFG_INDX_CNUM 3 /* Keyboard Scan Columns Number */
|
||||
#define KBS_CFG_INDX_CDIV 4 /* Keyboard Scan Clock Divisor */
|
||||
|
||||
/* SHI (Serial Host Interface) registers */
|
||||
struct shi_reg {
|
||||
volatile uint8_t reserved1;
|
||||
/* 0x001: SHI Configuration 1 */
|
||||
volatile uint8_t SHICFG1;
|
||||
/* 0x002: SHI Configuration 2 */
|
||||
volatile uint8_t SHICFG2;
|
||||
volatile uint8_t reserved2[2];
|
||||
/* 0x005: Event Enable */
|
||||
volatile uint8_t EVENABLE;
|
||||
/* 0x006: Event Status */
|
||||
volatile uint8_t EVSTAT;
|
||||
/* 0x007: SHI Capabilities */
|
||||
volatile uint8_t CAPABILITY;
|
||||
/* 0x008: Status */
|
||||
volatile uint8_t STATUS;
|
||||
volatile uint8_t reserved3;
|
||||
/* 0x00A: Input Buffer Status */
|
||||
volatile uint8_t IBUFSTAT;
|
||||
/* 0x00B: Output Buffer Status */
|
||||
volatile uint8_t OBUFSTAT;
|
||||
/* 0x00C: SHI Configuration 3 */
|
||||
volatile uint8_t SHICFG3;
|
||||
/* 0x00D: SHI Configuration 4 */
|
||||
volatile uint8_t SHICFG4;
|
||||
/* 0x00E: SHI Configuration 5 */
|
||||
volatile uint8_t SHICFG5;
|
||||
/* 0x00F: Event Status 2 */
|
||||
volatile uint8_t EVSTAT2;
|
||||
/* 0x010: Event Enable 2 */
|
||||
volatile uint8_t EVENABLE2;
|
||||
volatile uint8_t reserved4[15];
|
||||
/* 0x20~0x9F: Output Buffer */
|
||||
volatile uint8_t OBUF[128];
|
||||
/* 0xA0~0x11F: Input Buffer */
|
||||
volatile uint8_t IBUF[128];
|
||||
};
|
||||
|
||||
/* SHI register fields */
|
||||
#define NPCX_SHICFG1_EN 0
|
||||
#define NPCX_SHICFG1_MODE 1
|
||||
#define NPCX_SHICFG1_WEN 2
|
||||
#define NPCX_SHICFG1_AUTIBF 3
|
||||
#define NPCX_SHICFG1_AUTOBE 4
|
||||
#define NPCX_SHICFG1_DAS 5
|
||||
#define NPCX_SHICFG1_CPOL 6
|
||||
#define NPCX_SHICFG1_IWRAP 7
|
||||
#define NPCX_SHICFG2_SIMUL 0
|
||||
#define NPCX_SHICFG2_BUSY 1
|
||||
#define NPCX_SHICFG2_ONESHOT 2
|
||||
#define NPCX_SHICFG2_SLWU 3
|
||||
#define NPCX_SHICFG2_REEN 4
|
||||
#define NPCX_SHICFG2_RESTART 5
|
||||
#define NPCX_SHICFG2_REEVEN 6
|
||||
#define NPCX_EVENABLE_OBEEN 0
|
||||
#define NPCX_EVENABLE_OBHEEN 1
|
||||
#define NPCX_EVENABLE_IBFEN 2
|
||||
#define NPCX_EVENABLE_IBHFEN 3
|
||||
#define NPCX_EVENABLE_EOREN 4
|
||||
#define NPCX_EVENABLE_EOWEN 5
|
||||
#define NPCX_EVENABLE_STSREN 6
|
||||
#define NPCX_EVENABLE_IBOREN 7
|
||||
#define NPCX_EVSTAT_OBE 0
|
||||
#define NPCX_EVSTAT_OBHE 1
|
||||
#define NPCX_EVSTAT_IBF 2
|
||||
#define NPCX_EVSTAT_IBHF 3
|
||||
#define NPCX_EVSTAT_EOR 4
|
||||
#define NPCX_EVSTAT_EOW 5
|
||||
#define NPCX_EVSTAT_STSR 6
|
||||
#define NPCX_EVSTAT_IBOR 7
|
||||
#define NPCX_STATUS_OBES 6
|
||||
#define NPCX_STATUS_IBFS 7
|
||||
#define NPCX_SHICFG3_OBUFLVLDIS 7
|
||||
#define NPCX_SHICFG4_IBUFLVLDIS 7
|
||||
#define NPCX_SHICFG5_IBUFLVL2 FIELD(0, 6)
|
||||
#define NPCX_SHICFG5_IBUFLVL2DIS 7
|
||||
#define NPCX_EVSTAT2_IBHF2 0
|
||||
#define NPCX_EVSTAT2_CSNRE 1
|
||||
#define NPCX_EVSTAT2_CSNFE 2
|
||||
#define NPCX_EVENABLE2_IBHF2EN 0
|
||||
#define NPCX_EVENABLE2_CSNREEN 1
|
||||
#define NPCX_EVENABLE2_CSNFEEN 2
|
||||
|
||||
#endif /* _NUVOTON_NPCX_REG_DEF_H */
|
||||
|
|
|
@ -21,6 +21,7 @@ config EC_HOST_CMD_HANDLER_STACK_SIZE
|
|||
|
||||
config EC_HOST_CMD_HANDLER_TX_BUFFER
|
||||
int "Buffer size in bytes for TX buffer shared by all EC host commands"
|
||||
default EC_HOST_CMD_PERIPH_SHI_MAX_RESPONSE if EC_HOST_CMD_PERIPH_SHI
|
||||
default 256
|
||||
|
||||
config EC_HOST_CMD_HANDLER_PRIO
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue