/* * Copyright (c) 2022 Google LLC * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include LOG_MODULE_REGISTER(host_cmd_espi, CONFIG_EC_HC_LOG_LEVEL); #define RX_HEADER_SIZE (sizeof(struct ec_host_cmd_request_header)) /* eSPI Host Command state */ enum ec_host_cmd_espi_state { /* Interface is disabled */ ESPI_STATE_DISABLED, /* Ready to receive next request */ ESPI_STATE_READY_TO_RECV, /* Processing request */ ESPI_STATE_PROCESSING, /* Processing request */ ESPI_STATE_SENDING, ESPI_STATE_COUNT, }; struct ec_host_cmd_espi_ctx { /* eSPI device instance */ const struct device *espi_dev; /* Context for read operation */ struct ec_host_cmd_rx_ctx *rx_ctx; /* Transmit buffer */ struct ec_host_cmd_tx_buf *tx; /* eSPI callback */ struct espi_callback espi_cb; /* eSPI Host Command state */ enum ec_host_cmd_espi_state state; }; #define EC_HOST_CMD_ESPI_DEFINE(_name) \ static struct ec_host_cmd_espi_ctx _name##_hc_espi; \ struct ec_host_cmd_backend _name = { \ .api = &ec_host_cmd_api, \ .ctx = (struct ec_host_cmd_espi_ctx *)&_name##_hc_espi, \ } static void espi_handler(const struct device *dev, struct espi_callback *cb, struct espi_event espi_evt) { struct ec_host_cmd_espi_ctx *hc_espi = CONTAINER_OF(cb, struct ec_host_cmd_espi_ctx, espi_cb); uint16_t event_type = (uint16_t)espi_evt.evt_details; /* tx stores the shared memory buf pointer and size, so use it */ const struct ec_host_cmd_request_header *rx_header = hc_espi->tx->buf; const size_t shared_size = hc_espi->tx->len_max; const uint16_t rx_valid_data_size = rx_header->data_len + RX_HEADER_SIZE; if (event_type != ESPI_PERIPHERAL_EC_HOST_CMD) { return; } /* Make sure we've received a Host Command in a good state not to override buffers for * a Host Command that is currently being processed. There is a moment between sending * a response and setting state to ESPI_STATE_READY_TO_RECV when we can receive a new * host command, so accept the sending state as well. */ if (hc_espi->state != ESPI_STATE_READY_TO_RECV && hc_espi->state != ESPI_STATE_SENDING) { LOG_ERR("Received HC in bad state"); return; } /* Only support version 3 and make sure the number of bytes to copy is not * bigger than rx buf size or the shared memory size */ if (rx_header->prtcl_ver != 3 || rx_valid_data_size > CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_SIZE || rx_valid_data_size > shared_size) { memcpy(hc_espi->rx_ctx->buf, (void *)rx_header, RX_HEADER_SIZE); hc_espi->rx_ctx->len = RX_HEADER_SIZE; } else { memcpy(hc_espi->rx_ctx->buf, (void *)rx_header, rx_valid_data_size); hc_espi->rx_ctx->len = rx_valid_data_size; } /* Even in case of errors, let the general handler send response */ hc_espi->state = ESPI_STATE_PROCESSING; ec_host_cmd_rx_notify(); } static int ec_host_cmd_espi_init(const struct ec_host_cmd_backend *backend, struct ec_host_cmd_rx_ctx *rx_ctx, struct ec_host_cmd_tx_buf *tx) { struct ec_host_cmd_espi_ctx *hc_espi = (struct ec_host_cmd_espi_ctx *)backend->ctx; hc_espi->state = ESPI_STATE_DISABLED; if (!device_is_ready(hc_espi->espi_dev)) { return -ENODEV; } hc_espi->rx_ctx = rx_ctx; hc_espi->tx = tx; espi_init_callback(&hc_espi->espi_cb, espi_handler, ESPI_BUS_PERIPHERAL_NOTIFICATION); espi_add_callback(hc_espi->espi_dev, &hc_espi->espi_cb); /* Use shared memory as the tx buffer */ espi_read_lpc_request(hc_espi->espi_dev, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY, (uint32_t *)&tx->buf); espi_read_lpc_request(hc_espi->espi_dev, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY_SIZE, &tx->len_max); /* Set the max len for RX as the min of buffer to store data and shared memory. */ hc_espi->rx_ctx->len_max = MIN(CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_SIZE, hc_espi->tx->len_max); hc_espi->state = ESPI_STATE_READY_TO_RECV; return 0; } static int ec_host_cmd_espi_send(const struct ec_host_cmd_backend *backend) { struct ec_host_cmd_espi_ctx *hc_espi = (struct ec_host_cmd_espi_ctx *)backend->ctx; struct ec_host_cmd_response_header *resp_hdr = hc_espi->tx->buf; uint32_t result = resp_hdr->result; int ret; /* Ignore in-progress on eSPI since interface is synchronous anyway */ if (result == EC_HOST_CMD_IN_PROGRESS) { return 0; } hc_espi->state = ESPI_STATE_SENDING; /* Data to transfer are already in the tx buffer (shared memory) */ ret = espi_write_lpc_request(hc_espi->espi_dev, ECUSTOM_HOST_CMD_SEND_RESULT, &result); hc_espi->state = ESPI_STATE_READY_TO_RECV; return ret; } static const struct ec_host_cmd_backend_api ec_host_cmd_api = { .init = &ec_host_cmd_espi_init, .send = &ec_host_cmd_espi_send, }; EC_HOST_CMD_ESPI_DEFINE(ec_host_cmd_espi); struct ec_host_cmd_backend *ec_host_cmd_backend_get_espi(const struct device *dev) { ((struct ec_host_cmd_espi_ctx *)(ec_host_cmd_espi.ctx))->espi_dev = dev; return &ec_host_cmd_espi; } #if DT_NODE_EXISTS(DT_CHOSEN(zephyr_host_cmd_espi_backend)) && \ defined(CONFIG_EC_HOST_CMD_INITIALIZE_AT_BOOT) static int host_cmd_init(void) { const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_host_cmd_espi_backend)); ec_host_cmd_init(ec_host_cmd_backend_get_espi(dev)); return 0; } SYS_INIT(host_cmd_init, POST_KERNEL, CONFIG_EC_HOST_CMD_INIT_PRIORITY); #endif