blob: be90f9aa37610a9c198cfe274706e617b39b2b63 [file] [log] [blame]
/*
* Copyright (c) 2022 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it8xxx2_shi
#include "ec_host_cmd_periph_shi.h"
#include <zephyr/drivers/ec_host_cmd_periph/ec_host_cmd_periph.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/dt-bindings/gpio/ite-it8xxx2-gpio.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 <zephyr/pm/policy.h>
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "Invalid number of ITE SHI peripherals");
LOG_MODULE_REGISTER(host_cmd_shi_ite, CONFIG_EC_HC_LOG_LEVEL);
#define EC_SHI_PREAMBLE_LENGTH 4
#define EC_SHI_PAST_END_LENGTH 4
#define SPI_RX_MAX_FIFO_SIZE DT_INST_PROP(0, buffer_rx_size)
#define SPI_TX_MAX_FIFO_SIZE DT_INST_PROP(0, buffer_tx_size)
#define SHI_MAX_RESPONSE_SIZE \
(SPI_TX_MAX_FIFO_SIZE - EC_SHI_PREAMBLE_LENGTH - EC_SHI_PAST_END_LENGTH)
BUILD_ASSERT(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER == SHI_MAX_RESPONSE_SIZE,
"EC HC TX has different size than SHI TX response size");
BUILD_ASSERT(CONFIG_EC_HOST_CMD_PERIPH_SHI_MAX_REQUEST <= SPI_RX_MAX_FIFO_SIZE,
"SHI max request size is too big");
BUILD_ASSERT(CONFIG_EC_HOST_CMD_PERIPH_SHI_MAX_RESPONSE <= SHI_MAX_RESPONSE_SIZE,
"SHI max response size is too big");
/* Parameters used by host protocols */
enum shi_state_machine {
/* Interface is disabled */
SHI_STATE_DISABLED,
/* Ready to receive next request */
SHI_STATE_READY_TO_RECV,
/* Receiving request */
SHI_STATE_RECEIVING,
/* Processing request */
SHI_STATE_PROCESSING,
/* Received bad data */
SHI_STATE_RX_BAD,
SHI_STATE_COUNT,
};
/*
* Structure shi_it8xxx2_cfg is about the setting of SHI,
* this config will be used at initial time
*/
struct shi_it8xxx2_cfg {
/* SHI alternate configuration */
const struct pinctrl_dev_config *pcfg;
/* Chip select pin */
const struct gpio_dt_spec cs;
};
struct shi_it8xxx2_data {
/* Peripheral data */
struct k_sem handler_owns;
struct k_sem dev_owns;
struct gpio_callback cs_cb;
/* Current state */
enum shi_state_machine shi_state;
/* Buffers */
uint32_t in_msg_size;
uint8_t in_msg[SPI_RX_MAX_FIFO_SIZE] __aligned(4);
uint8_t out_msg[SPI_TX_MAX_FIFO_SIZE] __aligned(4);
};
static const uint8_t out_preamble[EC_SHI_PREAMBLE_LENGTH] = {
EC_SHI_PROCESSING,
EC_SHI_PROCESSING,
EC_SHI_PROCESSING,
/* This is the byte which matters */
EC_SHI_FRAME_START,
};
static const int shi_ite_response_state[] = {
[SHI_STATE_DISABLED] = EC_SHI_NOT_READY, [SHI_STATE_READY_TO_RECV] = EC_SHI_RX_READY,
[SHI_STATE_RECEIVING] = EC_SHI_RECEIVING, [SHI_STATE_PROCESSING] = EC_SHI_PROCESSING,
[SHI_STATE_RX_BAD] = EC_SHI_RX_BAD_DATA,
};
BUILD_ASSERT(ARRAY_SIZE(shi_ite_response_state) == SHI_STATE_COUNT);
static void shi_ite_set_state(struct shi_it8xxx2_data *data, int state)
{
/* SPI peripheral state machine */
data->shi_state = state;
/* Response spi peripheral state */
IT83XX_SPI_SPISRDR = shi_ite_response_state[state];
}
static void shi_ite_reset_rx_fifo(void)
{
/* End Rx FIFO access */
IT83XX_SPI_TXRXFAR = 0x00;
/* Rx FIFO reset and count monitor reset */
IT83XX_SPI_FCR = IT83XX_SPI_RXFR | IT83XX_SPI_RXFCMR;
}
/* This routine handles spi received unexcepted data */
static void shi_ite_bad_received_data(const struct device *dev, int count)
{
struct shi_it8xxx2_data *data = dev->data;
/* State machine mismatch, timeout, or protocol we can't handle. */
shi_ite_set_state(data, SHI_STATE_RX_BAD);
/* End CPU access Rx FIFO, so it can clock in bytes from AP again. */
IT83XX_SPI_TXRXFAR = 0;
LOG_ERR("SPI rx bad data");
LOG_HEXDUMP_DBG(data->in_msg, count, "in_msg=");
}
static void shi_ite_response_host_data(const struct device *dev, uint8_t *out_msg_addr, int tx_size)
{
struct shi_it8xxx2_data *data = dev->data;
/*
* Protect sequence of filling response packet for host.
* This will ensure CPU access FIFO is disabled at SPI end interrupt no
* matter the interrupt is triggered before or after the sequence.
*/
unsigned int key = irq_lock();
if (data->shi_state == SHI_STATE_PROCESSING) {
/* Tx FIFO reset and count monitor reset */
IT83XX_SPI_TXFCR = IT83XX_SPI_TXFR | IT83XX_SPI_TXFCMR;
/* CPU Tx FIFO1 and FIFO2 access */
IT83XX_SPI_TXRXFAR = IT83XX_SPI_CPUTFA;
for (int i = 0; i < tx_size; i += 4) {
/* Write response data from out_msg buffer to Tx FIFO */
IT83XX_SPI_CPUWTFDB0 = *(uint32_t *)(out_msg_addr + i);
}
/*
* After writing data to Tx FIFO is finished, this bit will
* be to indicate the SPI peripheral controller.
*/
IT83XX_SPI_TXFCR = IT83XX_SPI_TXFS;
/* End Tx FIFO access */
IT83XX_SPI_TXRXFAR = 0;
/* SPI peripheral read Tx FIFO */
IT83XX_SPI_FCR = IT83XX_SPI_SPISRTXF;
}
irq_unlock(key);
}
/*
* 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 int shi_ite_periph_send(const struct device *dev,
const struct ec_host_cmd_periph_tx_buf *buf)
{
struct shi_it8xxx2_data *data = dev->data;
int tx_size;
if (data->shi_state != SHI_STATE_PROCESSING) {
LOG_ERR("The request data is not processing (state=%d)", data->shi_state);
return -EBUSY;
}
/* Copy preamble */
memcpy(data->out_msg, out_preamble, sizeof(out_preamble));
/* Copy data */
memcpy(data->out_msg + sizeof(out_preamble), buf->buf, buf->len);
/* Append our past-end byte, which we reserved space for. */
for (int i = 0; i < EC_SHI_PAST_END_LENGTH; i++) {
data->out_msg[sizeof(out_preamble) + buf->len + i] = EC_SHI_PAST_END;
}
tx_size = buf->len + EC_SHI_PREAMBLE_LENGTH + EC_SHI_PAST_END_LENGTH;
/* Transmit the reply */
shi_ite_response_host_data(dev, data->out_msg, tx_size);
return 0;
}
/* Store request data from Rx FIFO to in_msg buffer */
static void shi_ite_host_request_data(uint8_t *in_msg_addr, int count)
{
/* CPU Rx FIFO1 access */
IT83XX_SPI_TXRXFAR = IT83XX_SPI_CPURXF1A;
/*
* In shi_ite_parse_header, the request data will separate to write in_msg buffer so we
* cannot set CPU to end accessing Rx FIFO in this function. We will set
* IT83XX_SPI_TXRXFAR = 0 in shi_ite_reset_rx_fifo.
*/
for (int i = 0; i < count; i += 4) {
/* Get data from master to buffer */
*(uint32_t *)(in_msg_addr + i) = IT83XX_SPI_RXFRDRB0;
}
}
static int shi_ite_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;
}
/* Parse header for version of spi-protocol */
static void shi_ite_parse_header(const struct device *dev)
{
struct shi_it8xxx2_data *data = dev->data;
struct ec_host_cmd_request_header *r = (struct ec_host_cmd_request_header *)data->in_msg;
/* Store request data from Rx FIFO to in_msg buffer */
shi_ite_host_request_data(data->in_msg, sizeof(*r));
/* Protocol version 3 */
if (data->in_msg[0] == EC_HOST_REQUEST_VERSION) {
/* Check how big the packet should be */
data->in_msg_size = shi_ite_host_request_expected_size(r);
if (data->in_msg_size == 0 || data->in_msg_size > sizeof(data->in_msg)) {
shi_ite_bad_received_data(dev, data->in_msg_size);
return;
}
/* Store request data from Rx FIFO to in_msg buffer */
shi_ite_host_request_data(data->in_msg + sizeof(*r),
data->in_msg_size - sizeof(*r));
k_sem_give(&data->handler_owns);
} else {
/* Invalid version number */
LOG_ERR("Invalid version number");
shi_ite_bad_received_data(dev, 1);
}
}
static void shi_ite_int_handler(const struct device *dev)
{
struct shi_it8xxx2_data *data = dev->data;
if (data->shi_state == SHI_STATE_DISABLED) {
return;
}
/*
* The status of SPI end detection interrupt bit is set, it means that host command parse
* has been completed and AP has received the last byte which is EC_SHI_PAST_END from
* EC responded data, then AP ended the transaction.
*/
if (IT83XX_SPI_ISR & IT83XX_SPI_ENDDETECTINT) {
/* Disable CPU access Rx FIFO to clock in data from AP again */
IT83XX_SPI_TXRXFAR = 0;
/* Ready to receive */
shi_ite_set_state(data, SHI_STATE_READY_TO_RECV);
/* CS# is deasserted, so write clear all slave status */
IT83XX_SPI_ISR = 0xff;
/* Allow the MCU to go into lower power mode */
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
}
/*
* The status of Rx valid length interrupt bit is set that indicates reached target count
* (IT83XX_SPI_FTCB1R, IT83XX_SPI_FTCB0R) and the length field of the host requested data.
*/
if (IT83XX_SPI_RX_VLISR & IT83XX_SPI_RVLI) {
/* write clear slave status */
IT83XX_SPI_RX_VLISR = IT83XX_SPI_RVLI;
/* Move to processing state */
shi_ite_set_state(data, SHI_STATE_PROCESSING);
/* Parse header for version of spi-protocol */
shi_ite_parse_header(dev);
}
}
void shi_ite_cs_callback(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins)
{
struct shi_it8xxx2_data *data = CONTAINER_OF(cb, struct shi_it8xxx2_data, cs_cb);
if (data->shi_state == SHI_STATE_DISABLED) {
return;
}
/* Prevent the MCU from sleeping during the transmission */
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
/* Move to processing state */
shi_ite_set_state(data, SHI_STATE_PROCESSING);
}
static int shi_ite_init_registers(const struct device *dev)
{
const struct shi_it8xxx2_cfg *const cfg = dev->config;
/* Set FIFO data target count */
struct ec_host_cmd_request_header cmd_head;
int status;
/*
* Target count means the size of host request.
* And plus extra 4 bytes because the CPU accesses FIFO base on word. If host requested
* data length is one byte, we need to align the data length to 4 bytes.
*/
int target_count = sizeof(cmd_head) + 4;
/* Offset of data_len member of host request. */
int offset = (char *)&cmd_head.data_len - (char *)&cmd_head;
IT83XX_SPI_FTCB1R = (target_count >> 8) & 0xff;
IT83XX_SPI_FTCB0R = target_count & 0xff;
/*
* The register setting can capture the length field of host
* request.
*/
IT83XX_SPI_TCCB1 = (offset >> 8) & 0xff;
IT83XX_SPI_TCCB0 = offset & 0xff;
/*
* Memory controller configuration register 3.
* bit6 : SPI pin function select (0b:Enable, 1b:Mask)
*/
IT83XX_GCTRL_MCCR3 |= IT83XX_GCTRL_SPISLVPFE;
/* Set unused blocked byte */
IT83XX_SPI_HPR2 = 0x00;
/* Rx valid length interrupt enabled */
IT83XX_SPI_RX_VLISMR &= ~IT83XX_SPI_RVLIM;
/*
* General control register2
* bit4 : Rx FIFO2 will not be overwrited once it's full.
* bit3 : Rx FIFO1 will not be overwrited once it's full.
* bit0 : Rx FIFO1/FIFO2 will reset after each CS_N goes high.
*/
IT83XX_SPI_GCR2 = IT83XX_SPI_RXF2OC | IT83XX_SPI_RXF1OC | IT83XX_SPI_RXFAR;
/*
* Interrupt mask register (0b:Enable, 1b:Mask)
* bit5 : Rx byte reach interrupt mask
* bit2 : SPI end detection interrupt mask
*/
IT83XX_SPI_IMR &= ~IT83XX_SPI_EDIM;
/* Reset fifo and prepare to for next transaction */
shi_ite_reset_rx_fifo();
/* Ready to receive */
shi_ite_set_state(dev->data, SHI_STATE_READY_TO_RECV);
/* Interrupt status register(write one to clear) */
IT83XX_SPI_ISR = 0xff;
/* SPI peripheral controller enable (after settings are ready) */
IT83XX_SPI_SPISGCR = IT83XX_SPI_SPISCEN;
/* Set the pin to SHI alternate function. */
status = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (status < 0) {
LOG_ERR("Failed to configure SHI pins");
return status;
}
/* Enable SPI peripheral interrupt */
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), shi_ite_int_handler,
DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQN(0));
return 0;
}
static int shi_ite_init_peripheral(const struct device *dev)
{
struct shi_it8xxx2_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_ite_init(const struct device *dev)
{
const struct shi_it8xxx2_cfg *cfg = dev->config;
struct shi_it8xxx2_data *data = dev->data;
int ret;
ret = shi_ite_init_registers(dev);
if (ret) {
return ret;
}
ret = shi_ite_init_peripheral(dev);
if (ret) {
return ret;
}
/* Configure the SPI chip select */
ret = gpio_pin_configure(cfg->cs.port, cfg->cs.pin, GPIO_INPUT | cfg->cs.dt_flags);
if (ret < 0) {
LOG_ERR("Failed to configure SHI CS pin");
return ret;
}
/* Enable SPI chip select pin interrupt */
gpio_init_callback(&data->cs_cb, shi_ite_cs_callback, BIT(cfg->cs.pin));
ret = gpio_add_callback(cfg->cs.port, &data->cs_cb);
if (ret < 0) {
return -EINVAL;
}
ret = gpio_pin_interrupt_configure(cfg->cs.port, cfg->cs.pin, GPIO_INT_EDGE_FALLING);
if (ret < 0) {
LOG_ERR("Failed to configure SHI CS interrupt");
return -EINVAL;
}
pm_device_init_suspended(dev);
return pm_device_runtime_enable(dev);
}
static int shi_ite_periph_init_context(const struct device *dev,
struct ec_host_cmd_periph_rx_ctx *rx_ctx)
{
struct shi_it8xxx2_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->in_msg_size;
return 0;
}
PINCTRL_DT_INST_DEFINE(0);
#ifdef CONFIG_PM_DEVICE
static int shi_ite_pm_cb(const struct device *dev, enum pm_device_action action)
{
struct shi_it8xxx2_data *data = dev->data;
int ret = 0;
switch (action) {
case PM_DEVICE_ACTION_SUSPEND:
shi_ite_set_state(data, SHI_STATE_DISABLED);
break;
case PM_DEVICE_ACTION_RESUME:
shi_ite_set_state(data, SHI_STATE_READY_TO_RECV);
break;
default:
ret = -ENOTSUP;
break;
}
return ret;
}
#endif
/* Assume only one peripheral */
PM_DEVICE_DT_INST_DEFINE(0, shi_ite_pm_cb);
static const struct ec_host_cmd_periph_api ec_host_cmd_api = {
.init = shi_ite_periph_init_context,
.send = shi_ite_periph_send,
};
static const struct shi_it8xxx2_cfg shi_cfg = {
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
.cs = GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(0), cs_gpios, 0),
};
static struct shi_it8xxx2_data shi_data = {
.shi_state = SHI_STATE_DISABLED,
.in_msg_size = 0,
};
DEVICE_DT_INST_DEFINE(0, shi_ite_init, PM_DEVICE_DT_INST_GET(0), &shi_data, &shi_cfg, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &ec_host_cmd_api);