summaryrefslogtreecommitdiff
path: root/zephyr/shim/src/espi.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/shim/src/espi.c')
-rw-r--r--zephyr/shim/src/espi.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/zephyr/shim/src/espi.c b/zephyr/shim/src/espi.c
new file mode 100644
index 0000000000..ceeed83b79
--- /dev/null
+++ b/zephyr/shim/src/espi.c
@@ -0,0 +1,207 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <atomic.h>
+#include <device.h>
+#include <drivers/espi.h>
+#include <logging/log.h>
+#include <kernel.h>
+#include <stdint.h>
+#include <zephyr.h>
+
+#include "chipset.h"
+#include "espi.h"
+#include "port80.h"
+#include "zephyr_espi_shim.h"
+
+LOG_MODULE_REGISTER(espi_shim, CONFIG_ESPI_LOG_LEVEL);
+
+/*
+ * A mapping of platform/ec signals to Zephyr virtual wires.
+ *
+ * This should be a macro which takes a parameter M, and does a
+ * functional application of M to 2-tuples of (platform/ec signal,
+ * zephyr vwire).
+ */
+#define VW_SIGNAL_TRANSLATION_LIST(M) \
+ M(VW_SLP_S3_L, ESPI_VWIRE_SIGNAL_SLP_S3) \
+ M(VW_SLP_S4_L, ESPI_VWIRE_SIGNAL_SLP_S4) \
+ M(VW_SLP_S5_L, ESPI_VWIRE_SIGNAL_SLP_S5) \
+ M(VW_SUS_STAT_L, ESPI_VWIRE_SIGNAL_SUS_STAT) \
+ M(VW_PLTRST_L, ESPI_VWIRE_SIGNAL_PLTRST) \
+ M(VW_OOB_RST_WARN, ESPI_VWIRE_SIGNAL_OOB_RST_WARN) \
+ M(VW_OOB_RST_ACK, ESPI_VWIRE_SIGNAL_OOB_RST_ACK) \
+ M(VW_WAKE_L, ESPI_VWIRE_SIGNAL_WAKE) \
+ M(VW_PME_L, ESPI_VWIRE_SIGNAL_PME) \
+ M(VW_ERROR_FATAL, ESPI_VWIRE_SIGNAL_ERR_FATAL) \
+ M(VW_ERROR_NON_FATAL, ESPI_VWIRE_SIGNAL_ERR_NON_FATAL) \
+ M(VW_SLAVE_BTLD_STATUS_DONE, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE) \
+ M(VW_SCI_L, ESPI_VWIRE_SIGNAL_SCI) \
+ M(VW_SMI_L, ESPI_VWIRE_SIGNAL_SMI) \
+ M(VW_HOST_RST_ACK, ESPI_VWIRE_SIGNAL_HOST_RST_ACK) \
+ M(VW_HOST_RST_WARN, ESPI_VWIRE_SIGNAL_HOST_RST_WARN) \
+ M(VW_SUS_ACK, ESPI_VWIRE_SIGNAL_SUS_ACK) \
+ M(VW_SUS_WARN_L, ESPI_VWIRE_SIGNAL_SUS_WARN) \
+ M(VW_SUS_PWRDN_ACK_L, ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK) \
+ M(VW_SLP_A_L, ESPI_VWIRE_SIGNAL_SLP_A) \
+ M(VW_SLP_LAN, ESPI_VWIRE_SIGNAL_SLP_LAN) \
+ M(VW_SLP_WLAN, ESPI_VWIRE_SIGNAL_SLP_WLAN)
+
+/*
+ * These two macros are intended to be used as as the M parameter to
+ * the list above, generating case statements returning the
+ * translation for the first parameter to the second, and the second
+ * to the first, respectively.
+ */
+#define CASE_CROS_TO_ZEPHYR(A, B) \
+ case A: \
+ return B;
+#define CASE_ZEPHYR_TO_CROS(A, B) CASE_CROS_TO_ZEPHYR(B, A)
+
+/* Translate a platform/ec signal to a Zephyr signal */
+static enum espi_vwire_signal signal_to_zephyr_vwire(enum espi_vw_signal signal)
+{
+ switch (signal) {
+ VW_SIGNAL_TRANSLATION_LIST(CASE_CROS_TO_ZEPHYR);
+ default:
+ LOG_ERR("Invalid virtual wire signal (%d)", signal);
+ return -1;
+ }
+}
+
+/* Translate a Zephyr vwire to a platform/ec signal */
+static enum espi_vw_signal zephyr_vwire_to_signal(enum espi_vwire_signal vwire)
+{
+ switch (vwire) {
+ VW_SIGNAL_TRANSLATION_LIST(CASE_ZEPHYR_TO_CROS);
+ default:
+ LOG_ERR("Invalid zephyr vwire (%d)", vwire);
+ return -1;
+ }
+}
+
+/*
+ * Bit field for each signal which can have an interrupt enabled.
+ * Note the interrupt is always enabled, it just depends whether we
+ * route it to the power_signal_interrupt handler or not.
+ */
+static atomic_t signal_interrupt_enabled;
+
+/* To be used with VW_SIGNAL_TRASLATION_LIST */
+#define CASE_CROS_TO_BIT(A, _) CASE_CROS_TO_ZEPHYR(A, BIT(A - VW_SIGNAL_START))
+
+/* Convert from an EC signal to the corresponding interrupt enabled bit. */
+static uint32_t signal_to_interrupt_bit(enum espi_vw_signal signal)
+{
+ switch (signal) {
+ VW_SIGNAL_TRANSLATION_LIST(CASE_CROS_TO_BIT);
+ default:
+ return 0;
+ }
+}
+
+/* Callback for vwire received */
+static void espi_vwire_handler(const struct device *dev,
+ struct espi_callback *cb,
+ struct espi_event event)
+{
+ int ec_signal = zephyr_vwire_to_signal(event.evt_details);
+
+ if (IS_ENABLED(CONFIG_PLATFORM_EC_POWERseQ) &&
+ (signal_interrupt_enabled & signal_to_interrupt_bit(ec_signal))) {
+ power_signal_interrupt(ec_signal);
+ }
+}
+
+static void espi_peripheral_handler(const struct device *dev,
+ struct espi_callback *cb,
+ struct espi_event event)
+{
+ uint16_t event_type = event.evt_details;
+
+ if (IS_ENABLED(CONFIG_PLATFORM_EC_PORT80) &&
+ event_type == ESPI_PERIPHERAL_DEBUG_PORT80) {
+ port_80_write(event.evt_data);
+ }
+}
+
+#define ESPI_DEV DT_LABEL(DT_NODELABEL(espi0))
+static const struct device *espi_dev;
+
+int zephyr_shim_setup_espi(void)
+{
+ static struct {
+ struct espi_callback cb;
+ espi_callback_handler_t handler;
+ enum espi_bus_event event_type;
+ } callbacks[] = {
+ {
+ .handler = espi_vwire_handler,
+ .event_type = ESPI_BUS_EVENT_VWIRE_RECEIVED,
+ },
+ {
+ .handler = espi_peripheral_handler,
+ .event_type = ESPI_BUS_PERIPHERAL_NOTIFICATION,
+ },
+ };
+
+ struct espi_cfg cfg = {
+ .io_caps = ESPI_IO_MODE_SINGLE_LINE,
+ .channel_caps = ESPI_CHANNEL_VWIRE | ESPI_CHANNEL_PERIPHERAL |
+ ESPI_CHANNEL_OOB,
+ .max_freq = 20,
+ };
+
+ espi_dev = device_get_binding(ESPI_DEV);
+ if (!espi_dev) {
+ LOG_ERR("Failed to find device %s", ESPI_DEV);
+ return -1;
+ }
+
+ /* Configure eSPI */
+ if (espi_config(espi_dev, &cfg)) {
+ LOG_ERR("Failed to configure eSPI device");
+ return -1;
+ }
+
+ /* Setup callbacks */
+ for (size_t i = 0; i < ARRAY_SIZE(callbacks); i++) {
+ espi_init_callback(&callbacks[i].cb, callbacks[i].handler,
+ callbacks[i].event_type);
+ espi_add_callback(espi_dev, &callbacks[i].cb);
+ }
+
+ return 0;
+}
+
+int espi_vw_set_wire(enum espi_vw_signal signal, uint8_t level)
+{
+ return espi_send_vwire(espi_dev, signal_to_zephyr_vwire(signal), level);
+}
+
+int espi_vw_get_wire(enum espi_vw_signal signal)
+{
+ uint8_t level;
+
+ if (espi_receive_vwire(espi_dev, signal_to_zephyr_vwire(signal),
+ &level) < 0) {
+ LOG_ERR("Encountered error receiving virtual wire signal");
+ return 0;
+ }
+
+ return level;
+}
+
+int espi_vw_enable_wire_int(enum espi_vw_signal signal)
+{
+ atomic_or(signal_interrupt_enabled, signal_to_interrupt_bit(signal));
+ return 0;
+}
+
+int espi_vw_disable_wire_int(enum espi_vw_signal signal)
+{
+ atomic_and(signal_interrupt_enabled, ~signal_to_interrupt_bit(signal));
+ return 0;
+}