samples: drivers: espi: Showcase OOB Rx asynchronous handling

Add example to handle OOB host responses asynchronously.

Signed-off-by: Jose Alberto Meza <jose.a.meza.arellano@intel.com>
This commit is contained in:
Jose Alberto Meza 2020-11-24 13:59:34 -08:00 committed by Anas Nashif
commit d38d913452
5 changed files with 296 additions and 80 deletions

View file

@ -531,7 +531,6 @@ static int espi_xec_receive_oob(const struct device *dev,
{
uint8_t err_mask = MCHP_ESPI_OOB_RX_STS_IBERR |
MCHP_ESPI_OOB_RX_STS_OVRUN;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
if (ESPI_OOB_REGS->TX_STS & err_mask) {
return -EIO;
@ -539,7 +538,7 @@ static int espi_xec_receive_oob(const struct device *dev,
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
int ret;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->driver_data);
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
/* Wait until ISR or timeout */
ret = k_sem_take(&data->rx_lock, K_MSEC(MAX_OOB_TIMEOUT));

View file

@ -7,3 +7,5 @@ CONFIG_LOG_PROCESS_THREAD_SLEEP_MS=100
CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE=n
# Sample code doesn't handle ACPI host communication
CONFIG_ESPI_PERIPHERAL_HOST_IO=n
# This only makes sense for system using this board
CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC=y

View file

@ -0,0 +1,214 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <soc.h>
#include <drivers/gpio.h>
#include <drivers/espi.h>
#include <logging/log_ctrl.h>
#include <logging/log.h>
#include "espi_oob_handler.h"
LOG_MODULE_DECLARE(espi, CONFIG_ESPI_LOG_LEVEL);
struct oob_header {
uint8_t dest_slave_addr;
uint8_t oob_cmd_code;
uint8_t byte_cnt;
uint8_t src_slave_addr;
};
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
#define OOB_THREAD_STACK_SIZE 512ul
#define OOB_THREAD_PRIORITY K_PRIO_COOP(5)
#define OOB_THREAD_WAIT -1
/* Thread to process asynchronous callbacks */
void espihub_thread(void *p1, void *p2, void *p3);
void temperature_timer(struct k_timer *timer_id);
K_TIMER_DEFINE(temp_timer, temperature_timer, NULL);
K_THREAD_DEFINE(espihub_thrd_id, OOB_THREAD_STACK_SIZE, espihub_thread,
NULL, NULL, NULL,
OOB_THREAD_PRIORITY, K_INHERIT_PERMS, OOB_THREAD_WAIT);
K_MSGQ_DEFINE(from_host, sizeof(uint8_t), 8, 4);
struct thread_context {
const struct device *espi_dev;
int cycles;
};
static struct thread_context context;
#endif
static struct espi_oob_packet resp_pckt;
static uint8_t buf[MAX_ESPI_BUF_LEN];
static int request_temp(const struct device *dev)
{
int ret;
struct oob_header oob_hdr;
struct espi_oob_packet req_pckt;
LOG_WRN("%s", __func__);
oob_hdr.dest_slave_addr = PCH_DEST_SLV_ADDR;
oob_hdr.oob_cmd_code = OOB_CMDCODE;
oob_hdr.byte_cnt = 1;
oob_hdr.src_slave_addr = SRC_SLV_ADDR;
/* Packetize OOB request */
req_pckt.buf = (uint8_t *)&oob_hdr;
req_pckt.len = sizeof(struct oob_header);
ret = espi_send_oob(dev, &req_pckt);
if (ret) {
LOG_ERR("OOB Tx failed %d", ret);
return ret;
}
return 0;
}
static int retrieve_packet(const struct device *dev, uint8_t *sender)
{
int ret;
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
/* Note that no data is in the item */
uint8_t response_len;
if (k_msgq_num_used_get(&from_host) == 0U) {
return -EINVAL;
}
k_msgq_get(&from_host, &response_len, K_FOREVER);
#endif
resp_pckt.buf = (uint8_t *)&buf;
resp_pckt.len = MAX_ESPI_BUF_LEN;
ret = espi_receive_oob(dev, &resp_pckt);
if (ret) {
LOG_ERR("OOB Rx failed %d", ret);
return ret;
}
LOG_INF("OOB transaction completed rcvd: %d bytes", resp_pckt.len);
for (int i = 0; i < resp_pckt.len; i++) {
LOG_INF("%x ", buf[i]);
}
if (sender) {
*sender = buf[OOB_RESPONSE_SENDER_INDEX];
}
return 0;
}
int get_pch_temp_sync(const struct device *dev)
{
int ret;
for (int i = 0; i < MIN_GET_TEMP_CYCLES; i++) {
ret = request_temp(dev);
if (ret) {
LOG_ERR("OOB req failed %d", ret);
return ret;
}
ret = retrieve_packet(dev, NULL);
if (ret) {
LOG_ERR("OOB retrieve failed %d", ret);
return ret;
}
}
return 0;
}
int get_pch_temp_async(const struct device *dev)
{
#if !defined(CONFIG_ESPI_OOB_CHANNEL) || \
!defined(CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC)
return -ENOTSUP;
#else
context.espi_dev = dev;
context.cycles = MIN_GET_TEMP_CYCLES;
k_thread_start(espihub_thrd_id);
k_thread_join(espihub_thrd_id, K_FOREVER);
return 0;
#endif
}
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
void oob_rx_handler(const struct device *dev, struct espi_callback *cb,
struct espi_event event)
{
uint8_t last_resp_len = event.evt_details;
LOG_WRN("%s", __func__);
/* Post for post-processing in a thread
* Should not attempt to retrieve in callback context
*/
k_msgq_put(&from_host, &last_resp_len, K_NO_WAIT);
}
bool need_temp;
void temperature_timer(struct k_timer *timer_id)
{
LOG_WRN("%s", __func__);
need_temp = true;
}
void espihub_thread(void *p1, void *p2, void *p3)
{
int ret;
uint8_t temp;
uint8_t sender;
LOG_DBG("%s", __func__);
k_timer_start(&temp_timer, K_MSEC(100), K_MSEC(100));
while (context.cycles > 0) {
k_msleep(50);
ret = retrieve_packet(context.espi_dev, &sender);
if (!ret) {
switch (sender) {
case PCH_DEST_SLV_ADDR:
LOG_INF("PCH response");
/* Any other checks */
if (resp_pckt.len == OOB_RESPONSE_LEN) {
temp = buf[OOB_RESPONSE_DATA_INDEX];
LOG_INF("Temp %d", temp);
} else {
LOG_ERR("Incorrect size response");
}
context.cycles--;
break;
default:
LOG_INF("Other host sender %x", sender);
}
}
if (need_temp) {
request_temp(context.espi_dev);
need_temp = false;
}
}
k_timer_stop(&temp_timer);
}
#endif /* CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC */

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ESPI_OOB_HANDLER_H__
#define __ESPI_OOB_HANDLER_H__
/* eSPI host entity address */
#define PCH_DEST_SLV_ADDR 0x02u
#define SRC_SLV_ADDR 0x21u
#define OOB_RESPONSE_SENDER_INDEX 0x02u
#define OOB_RESPONSE_DATA_INDEX 0x04u
/* Temperature command opcode */
#define OOB_CMDCODE 0x01u
#define OOB_RESPONSE_LEN 0x05u
/* Maximum bytes for OOB transactions */
#define MAX_ESPI_BUF_LEN 80u
#define MIN_GET_TEMP_CYCLES 5u
/* 100ms */
#define MAX_OOB_TIMEOUT 100ul
void oob_rx_handler(const struct device *dev, struct espi_callback *cb,
struct espi_event event);
/**
* @brief Retrieve PCH temperature over OOB channel.
* Assumes OOB Tx and Rx as synchronous operation.
*
* @param dev eSPI driver handle.
*
* @retval 0 If successful.
* @retval -ENOTSUP returned when OOB channel is not supported.
* @retval -EINVAL is returned when OOB parameters are invalid.
*
*/
int get_pch_temp_sync(const struct device *dev);
/**
* @brief Retrieve PCH temperature over OOB channel.
* Assumes OOB Tx and Rx as synchronous operation.
*
* @param dev eSPI driver handle.
*
* @retval 0 If successful.
* @retval -ENOTSUP returned when OOB channel is not supported.
* @retval -ETIMEOUT OOB operations could not be started.
*
*/
int get_pch_temp_async(const struct device *dev);
#endif /* __ESPI_OOB_HANDLER_H__ */

View file

@ -12,21 +12,11 @@
#include <drivers/espi.h>
#include <logging/log_ctrl.h>
#include <logging/log.h>
#ifdef CONFIG_ESPI_OOB_CHANNEL
#include "espi_oob_handler.h"
#endif
LOG_MODULE_DECLARE(espi, CONFIG_ESPI_LOG_LEVEL);
/* eSPI host entity address */
#define DEST_SLV_ADDR 0x02u
#define SRC_SLV_ADDR 0x21u
/* Temperature command opcode */
#define OOB_CMDCODE 0x01u
#define OOB_RESPONSE_LEN 0x05u
#define OOB_RESPONSE_INDEX 0x03u
/* Maximum bytes for OOB transactions */
#define MAX_RESP_SIZE 20u
#define MIN_GET_TEMP_CYCLES 5u
/* eSPI flash parameters */
#define MAX_TEST_BUF_SIZE 1024u
#define MAX_FLASH_REQUEST 64u
@ -45,13 +35,6 @@ LOG_MODULE_DECLARE(espi, CONFIG_ESPI_LOG_LEVEL);
#define EVENT_TYPE(x) (x & EVENT_MASK)
#define EVENT_DETAILS(x) ((x & EVENT_DETAILS_MASK) >> EVENT_DETAILS_POS)
struct oob_header {
uint8_t dest_slave_addr;
uint8_t oob_cmd_code;
uint8_t byte_cnt;
uint8_t src_slave_addr;
};
#define PWR_SEQ_TIMEOUT 3000u
/* The devicetree node identifier for the board power rails pins. */
@ -74,8 +57,10 @@ static struct espi_callback espi_bus_cb;
static struct espi_callback vw_rdy_cb;
static struct espi_callback vw_cb;
static struct espi_callback p80_cb;
static uint8_t espi_rst_sts;
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
static struct espi_callback oob_cb;
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
static uint8_t flash_write_buf[MAX_TEST_BUF_SIZE];
@ -226,6 +211,10 @@ int espi_init(void)
ESPI_BUS_EVENT_VWIRE_RECEIVED);
espi_init_callback(&p80_cb, periph_handler,
ESPI_BUS_PERIPHERAL_NOTIFICATION);
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
espi_init_callback(&oob_cb, oob_rx_handler,
ESPI_BUS_EVENT_OOB_RECEIVED);
#endif
LOG_INF("complete");
LOG_INF("eSPI test - callbacks registration... ");
@ -233,6 +222,9 @@ int espi_init(void)
espi_add_callback(espi_dev, &vw_rdy_cb);
espi_add_callback(espi_dev, &vw_cb);
espi_add_callback(espi_dev, &p80_cb);
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
espi_add_callback(espi_dev, &oob_cb);
#endif
LOG_INF("complete");
return ret;
@ -479,53 +471,6 @@ static int espi_flash_test(uint32_t start_flash_addr, uint8_t blocks)
}
#endif /* CONFIG_ESPI_FLASH_CHANNEL */
int get_pch_temp(const struct device *dev, int *temp)
{
struct espi_oob_packet req_pckt;
struct espi_oob_packet resp_pckt;
struct oob_header oob_hdr;
uint8_t buf[MAX_RESP_SIZE];
int ret;
LOG_INF("%s", __func__);
oob_hdr.dest_slave_addr = DEST_SLV_ADDR;
oob_hdr.oob_cmd_code = OOB_CMDCODE;
oob_hdr.byte_cnt = 1;
oob_hdr.src_slave_addr = SRC_SLV_ADDR;
/* Packetize OOB request */
req_pckt.buf = (uint8_t *)&oob_hdr;
req_pckt.len = sizeof(struct oob_header);
resp_pckt.buf = (uint8_t *)&buf;
resp_pckt.len = MAX_RESP_SIZE;
ret = espi_send_oob(dev, &req_pckt);
if (ret) {
LOG_ERR("OOB Tx failed %d", ret);
return ret;
}
ret = espi_receive_oob(dev, &resp_pckt);
if (ret) {
LOG_ERR("OOB Rx failed %d", ret);
return ret;
}
LOG_INF("OOB transaction completed rcvd: %d bytes", resp_pckt.len);
for (int i = 0; i < resp_pckt.len; i++) {
LOG_INF("%x ", buf[i]);
}
if (resp_pckt.len == OOB_RESPONSE_LEN) {
*temp = buf[OOB_RESPONSE_INDEX];
} else {
LOG_ERR("Incorrect size response");
}
return 0;
}
#ifndef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE
static void send_slave_bootdone(void)
{
@ -674,16 +619,13 @@ int espi_test(void)
/* Attempt to use OOB channel to read temperature, regardless of
* if is enabled or not.
*/
for (int i = 0; i < MIN_GET_TEMP_CYCLES; i++) {
int temp;
ret = get_pch_temp(espi_dev, &temp);
if (ret) {
LOG_ERR("eSPI OOB transaction failed %d", ret);
} else {
LOG_INF("Temp: %d ", temp);
}
}
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
/* System without host-initiated OOB Rx traffic */
get_pch_temp_sync(espi_dev);
#else
/* System with host-initiated OOB Rx traffic */
get_pch_temp_async(espi_dev);
#endif
/* Cleanup */
k_sleep(K_SECONDS(1));