zephyr/drivers/espi/espi_emul.c
Tristan Honscheid 8fd1ce7579 emul: Only add enabled DT nodes to bus emulators
The eSPI, I2C, and SPI emulators use devicetree macros to build an array
of devices on the virtual bus. Currently, they will add device nodes that
are not status-okay. This leads to linker errors because the respective
device drivers would not have instantiated device structs for these
nodes --assuming the driver was even compiled. This can be frustrating
if nodes need to be disabled for debugging or configuration purposes.

Update the bus emulators to only consider status-okay nodes by changing
the macros used to iterate over bus devices.

Signed-off-by: Tristan Honscheid <honscheid@google.com>
2023-07-12 09:25:18 +02:00

246 lines
6.6 KiB
C

/*
* Copyright 2020 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*
* This driver creates fake eSPI buses which can contain emulated devices
* (mainly host), implemented by a separate emulation driver.
* The API between this driver/controller and device emulators attached
* to its bus is defined by struct emul_espi_device_api.
*/
#define DT_DRV_COMPAT zephyr_espi_emul_controller
#define LOG_LEVEL CONFIG_ESPI_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(espi_emul_ctlr);
#include <zephyr/device.h>
#include <zephyr/drivers/emul.h>
#include <zephyr/drivers/espi.h>
#include <zephyr/drivers/espi_emul.h>
#include "espi_utils.h"
/** Working data for the controller */
struct espi_emul_data {
/* List of struct espi_emul associated with the device */
sys_slist_t emuls;
/* eSPI host configuration */
struct espi_cfg cfg;
/** List of eSPI callbacks */
sys_slist_t callbacks;
};
static struct espi_emul *espi_emul_find(const struct device *dev, unsigned int chipsel)
{
struct espi_emul_data *data = dev->data;
sys_snode_t *node;
SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) {
struct espi_emul *emul;
emul = CONTAINER_OF(node, struct espi_emul, node);
if (emul->chipsel == chipsel) {
return emul;
}
}
return NULL;
}
static int espi_emul_config(const struct device *dev, struct espi_cfg *cfg)
{
struct espi_emul_data *data = dev->data;
__ASSERT_NO_MSG(cfg);
data->cfg = *cfg;
return 0;
}
static int emul_espi_trigger_event(const struct device *dev, struct espi_event *evt)
{
struct espi_emul_data *data = dev->data;
if (((evt->evt_type & ESPI_BUS_EVENT_VWIRE_RECEIVED) &&
!(data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) ||
((evt->evt_type & ESPI_BUS_EVENT_OOB_RECEIVED) &&
!(data->cfg.channel_caps & ESPI_CHANNEL_OOB)) ||
((evt->evt_type & ESPI_BUS_PERIPHERAL_NOTIFICATION) &&
!(data->cfg.channel_caps & ESPI_CHANNEL_PERIPHERAL))) {
return -EIO;
}
espi_send_callbacks(&data->callbacks, dev, *evt);
return 0;
}
static bool espi_emul_get_channel_status(const struct device *dev, enum espi_channel ch)
{
struct espi_emul_data *data = dev->data;
return (data->cfg.channel_caps & ch);
}
static int espi_emul_read_lpc_request(const struct device *dev, enum lpc_peripheral_opcode op,
uint32_t *data)
{
const struct emul_espi_device_api *api;
struct espi_emul *emul;
struct espi_emul_data *emul_data = dev->data;
ARG_UNUSED(data);
if (!(emul_data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) {
LOG_ERR("bad channel vwire");
return -EINVAL;
}
emul = espi_emul_find(dev, EMUL_ESPI_HOST_CHIPSEL);
if (!emul) {
LOG_ERR("espi_emul not found");
return -ENOTSUP;
}
__ASSERT_NO_MSG(emul->api);
api = emul->api;
switch (op) {
#ifdef CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION
case EACPI_GET_SHARED_MEMORY:
__ASSERT_NO_MSG(api->get_acpi_shm);
*data = (uint32_t)api->get_acpi_shm(emul->target);
break;
#endif
default:
return -EINVAL;
}
return 0;
}
static int espi_emul_write_lpc_request(const struct device *dev, enum lpc_peripheral_opcode op,
uint32_t *data)
{
ARG_UNUSED(dev);
ARG_UNUSED(op);
ARG_UNUSED(data);
return -EINVAL;
}
static int espi_emul_send_vwire(const struct device *dev, enum espi_vwire_signal vw, uint8_t level)
{
const struct emul_espi_device_api *api;
struct espi_emul *emul;
struct espi_emul_data *data = dev->data;
if (!(data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) {
return -EIO;
}
emul = espi_emul_find(dev, EMUL_ESPI_HOST_CHIPSEL);
if (!emul) {
LOG_DBG("espi_emul not found");
return -EIO;
}
__ASSERT_NO_MSG(emul->api);
__ASSERT_NO_MSG(emul->api->set_vw);
api = emul->api;
return api->set_vw(emul->target, vw, level);
}
static int espi_emul_receive_vwire(const struct device *dev, enum espi_vwire_signal vw,
uint8_t *level)
{
const struct emul_espi_device_api *api;
struct espi_emul *emul;
struct espi_emul_data *data = dev->data;
if (!(data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) {
return -EIO;
}
emul = espi_emul_find(dev, EMUL_ESPI_HOST_CHIPSEL);
if (!emul) {
LOG_INF("espi_emul not found");
return -EIO;
}
__ASSERT_NO_MSG(emul->api);
__ASSERT_NO_MSG(emul->api->get_vw);
api = emul->api;
return api->get_vw(emul->target, vw, level);
}
static int espi_emul_manage_callback(const struct device *dev, struct espi_callback *callback,
bool set)
{
struct espi_emul_data *data = dev->data;
return espi_manage_callback(&data->callbacks, callback, set);
}
/**
* Set up a new emulator and add it to the list
*
* @param dev eSPI emulation controller device
*/
static int espi_emul_init(const struct device *dev)
{
struct espi_emul_data *data = dev->data;
sys_slist_init(&data->emuls);
return emul_init_for_bus(dev);
}
int espi_emul_register(const struct device *dev, struct espi_emul *emul)
{
struct espi_emul_data *data = dev->data;
const char *name = emul->target->dev->name;
sys_slist_append(&data->emuls, &emul->node);
LOG_INF("Register emulator '%s' at cs %u\n", name, emul->chipsel);
return 0;
}
/* Device instantiation */
static struct emul_espi_driver_api emul_espi_driver_api = {
.espi_api = {
.config = espi_emul_config,
.get_channel_status = espi_emul_get_channel_status,
.read_lpc_request = espi_emul_read_lpc_request,
.write_lpc_request = espi_emul_write_lpc_request,
.send_vwire = espi_emul_send_vwire,
.receive_vwire = espi_emul_receive_vwire,
.manage_callback = espi_emul_manage_callback
},
.trigger_event = emul_espi_trigger_event,
.find_emul = espi_emul_find,
};
#define EMUL_LINK_AND_COMMA(node_id) \
{ \
.dev = DEVICE_DT_GET(node_id), \
},
#define ESPI_EMUL_INIT(n) \
static const struct emul_link_for_bus emuls_##n[] = { \
DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(n), EMUL_LINK_AND_COMMA)}; \
static struct emul_list_for_bus espi_emul_cfg_##n = { \
.children = emuls_##n, \
.num_children = ARRAY_SIZE(emuls_##n), \
}; \
static struct espi_emul_data espi_emul_data_##n; \
DEVICE_DT_INST_DEFINE(n, &espi_emul_init, NULL, &espi_emul_data_##n, &espi_emul_cfg_##n, \
POST_KERNEL, CONFIG_ESPI_INIT_PRIORITY, &emul_espi_driver_api);
DT_INST_FOREACH_STATUS_OKAY(ESPI_EMUL_INIT)