diff options
Diffstat (limited to 'zephyr/shim/src/espi.c')
-rw-r--r-- | zephyr/shim/src/espi.c | 563 |
1 files changed, 0 insertions, 563 deletions
diff --git a/zephyr/shim/src/espi.c b/zephyr/shim/src/espi.c deleted file mode 100644 index c064bd6157..0000000000 --- a/zephyr/shim/src/espi.c +++ /dev/null @@ -1,563 +0,0 @@ -/* 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 "acpi.h" -#include "chipset.h" -#include "common.h" -#include "espi.h" -#include "gpio.h" -#include "hooks.h" -#include "i8042_protocol.h" -#include "keyboard_protocol.h" -#include "lpc.h" -#include "port80.h" -#include "power.h" -#include "task.h" -#include "timer.h" -#include "zephyr_espi_shim.h" - -#define VWIRE_PULSE_TRIGGER_TIME 65 - -LOG_MODULE_REGISTER(espi_shim, CONFIG_ESPI_LOG_LEVEL); - -/* host command packet handler structure */ -static struct host_packet lpc_packet; -/* - * For the eSPI host command, request & response use the same share memory. - * This is for input request temp buffer. - */ -static uint8_t params_copy[EC_LPC_HOST_PACKET_SIZE] __aligned(4); -static bool init_done; - -/* - * 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_PERIPHERAL_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); - } -} - -#ifdef CONFIG_PLATFORM_EC_CHIPSET_RESET_HOOK -static void espi_chipset_reset(void) -{ - hook_notify(HOOK_CHIPSET_RESET); -} -DECLARE_DEFERRED(espi_chipset_reset); - -/* Callback for reset */ -static void espi_reset_handler(const struct device *dev, - struct espi_callback *cb, - struct espi_event event) -{ - hook_call_deferred(&espi_chipset_reset_data, MSEC); - -} -#endif /* CONFIG_PLATFORM_EC_CHIPSET_RESET_HOOK */ - -#define ESPI_NODE DT_NODELABEL(espi0) -static const struct device *espi_dev; - - -int espi_vw_set_wire(enum espi_vw_signal signal, uint8_t level) -{ - int ret = espi_send_vwire(espi_dev, signal_to_zephyr_vwire(signal), - level); - - if (ret != 0) - LOG_ERR("Encountered error sending virtual wire signal"); - - return ret; -} - -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; -} - -uint8_t *lpc_get_memmap_range(void) -{ - uint32_t lpc_memmap = 0; - int result = espi_read_lpc_request(espi_dev, EACPI_GET_SHARED_MEMORY, - &lpc_memmap); - - if (result != EC_SUCCESS) - LOG_ERR("Get lpc_memmap failed (%d)!\n", result); - - return (uint8_t *)lpc_memmap; -} - -/** - * Update the level-sensitive wake signal to the AP. - * - * @param wake_events Currently asserted wake events - */ -static void lpc_update_wake(host_event_t wake_events) -{ - /* - * Mask off power button event, since the AP gets that through a - * separate dedicated GPIO. - */ - wake_events &= ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON); - - /* Signal is asserted low when wake events is non-zero */ - gpio_set_level(GPIO_EC_PCH_WAKE_ODL, !wake_events); -} - -static void lpc_generate_smi(void) -{ - /* Enforce signal-high for long enough to debounce high */ - espi_vw_set_wire(VW_SMI_L, 1); - udelay(VWIRE_PULSE_TRIGGER_TIME); - espi_vw_set_wire(VW_SMI_L, 0); - udelay(VWIRE_PULSE_TRIGGER_TIME); - espi_vw_set_wire(VW_SMI_L, 1); -} - -static void lpc_generate_sci(void) -{ - /* Enforce signal-high for long enough to debounce high */ - espi_vw_set_wire(VW_SCI_L, 1); - udelay(VWIRE_PULSE_TRIGGER_TIME); - espi_vw_set_wire(VW_SCI_L, 0); - udelay(VWIRE_PULSE_TRIGGER_TIME); - espi_vw_set_wire(VW_SCI_L, 1); -} - -void lpc_update_host_event_status(void) -{ - uint32_t enable; - uint32_t status; - int need_sci = 0; - int need_smi = 0; - - if (!init_done) - return; - - /* Disable PMC1 interrupt while updating status register */ - enable = 0; - espi_write_lpc_request(espi_dev, ECUSTOM_HOST_SUBS_INTERRUPT_EN, - &enable); - - espi_read_lpc_request(espi_dev, EACPI_READ_STS, &status); - if (lpc_get_host_events_by_type(LPC_HOST_EVENT_SMI)) { - /* Only generate SMI for first event */ - if (!(status & EC_LPC_STATUS_SMI_PENDING)) - need_smi = 1; - - status |= EC_LPC_STATUS_SMI_PENDING; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); - } else { - status &= ~EC_LPC_STATUS_SMI_PENDING; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); - } - - espi_read_lpc_request(espi_dev, EACPI_READ_STS, &status); - if (lpc_get_host_events_by_type(LPC_HOST_EVENT_SCI)) { - /* Generate SCI for every event */ - need_sci = 1; - - status |= EC_LPC_STATUS_SCI_PENDING; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); - } else { - status &= ~EC_LPC_STATUS_SCI_PENDING; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); - } - - *(host_event_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = - lpc_get_host_events(); - - enable = 1; - espi_write_lpc_request(espi_dev, ECUSTOM_HOST_SUBS_INTERRUPT_EN, - &enable); - - /* Process the wake events. */ - lpc_update_wake(lpc_get_host_events_by_type(LPC_HOST_EVENT_WAKE)); - - /* Send pulse on SMI signal if needed */ - if (need_smi) - lpc_generate_smi(); - - /* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */ - if (need_sci) - lpc_generate_sci(); -} - -static void host_command_init(void) -{ - /* We support LPC args and version 3 protocol */ - *(lpc_get_memmap_range() + EC_MEMMAP_HOST_CMD_FLAGS) = - EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED | - EC_HOST_CMD_FLAG_VERSION_3; - - /* Sufficiently initialized */ - init_done = 1; - - lpc_update_host_event_status(); -} - -DECLARE_HOOK(HOOK_INIT, host_command_init, HOOK_PRIO_INIT_LPC); - -static void handle_acpi_write(uint32_t data) -{ - uint8_t value, result; - uint8_t is_cmd = is_acpi_command(data); - uint32_t status; - - value = get_acpi_value(data); - - /* Handle whatever this was. */ - if (acpi_ap_to_ec(is_cmd, value, &result)) { - data = result; - espi_write_lpc_request(espi_dev, EACPI_WRITE_CHAR, &data); - } - - /* Clear processing flag */ - espi_read_lpc_request(espi_dev, EACPI_READ_STS, &status); - status &= ~EC_LPC_STATUS_PROCESSING; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); - - /* - * ACPI 5.0-12.6.1: Generate SCI for Input Buffer Empty / Output Buffer - * Full condition on the kernel channel. - */ - lpc_generate_sci(); -} - -static void lpc_send_response_packet(struct host_packet *pkt) -{ - uint32_t data; - - /* TODO(b/176523211): check whether add EC_RES_IN_PROGRESS handle */ - - /* Write result to the data byte. This sets the TOH status bit. */ - data = pkt->driver_result; - espi_write_lpc_request(espi_dev, ECUSTOM_HOST_CMD_SEND_RESULT, &data); -} - -static void handle_host_write(uint32_t data) -{ - uint32_t shm_mem_host_cmd; - - if (EC_COMMAND_PROTOCOL_3 != (data & 0xff)) { - LOG_ERR("Don't support this version of the host command"); - /* TODO:(b/175217186): error response for other versions */ - return; - } - - espi_read_lpc_request(espi_dev, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY, - &shm_mem_host_cmd); - - lpc_packet.send_response = lpc_send_response_packet; - - lpc_packet.request = (const void *)shm_mem_host_cmd; - lpc_packet.request_temp = params_copy; - lpc_packet.request_max = sizeof(params_copy); - /* Don't know the request size so pass in the entire buffer */ - lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; - - lpc_packet.response = (void *)shm_mem_host_cmd; - lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; - lpc_packet.response_size = 0; - - lpc_packet.driver_result = EC_RES_SUCCESS; - - host_packet_receive(&lpc_packet); - return; -} - -void lpc_set_acpi_status_mask(uint8_t mask) -{ - uint32_t status; - espi_read_lpc_request(espi_dev, EACPI_READ_STS, &status); - status |= mask; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); -} - -void lpc_clear_acpi_status_mask(uint8_t mask) -{ - uint32_t status; - espi_read_lpc_request(espi_dev, EACPI_READ_STS, &status); - status &= ~mask; - espi_write_lpc_request(espi_dev, EACPI_WRITE_STS, &status); -} - -/* Get protocol information */ -static enum ec_status lpc_get_protocol_info(struct host_cmd_handler_args *args) -{ - struct ec_response_get_protocol_info *r = args->response; - - memset(r, 0, sizeof(*r)); - r->protocol_versions = BIT(3); - r->max_request_packet_size = EC_LPC_HOST_PACKET_SIZE; - r->max_response_packet_size = EC_LPC_HOST_PACKET_SIZE; - r->flags = 0; - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, lpc_get_protocol_info, - EC_VER_MASK(0)); - -/* - * This function is needed only for the obsolete platform which uses the GPIO - * for KBC's IRQ. - */ -void lpc_keyboard_resume_irq(void) {} - -void lpc_keyboard_clear_buffer(void) -{ - /* Clear OBF flag in host STATUS and HIKMST regs */ - espi_write_lpc_request(espi_dev, E8042_CLEAR_OBF, 0); -} -int lpc_keyboard_has_char(void) -{ - uint32_t status; - - /* if OBF bit is '1', that mean still have a data in DBBOUT */ - espi_read_lpc_request(espi_dev, E8042_OBF_HAS_CHAR, &status); - return status; -} - -void lpc_keyboard_put_char(uint8_t chr, int send_irq) -{ - uint32_t kb_char = chr; - - espi_write_lpc_request(espi_dev, E8042_WRITE_KB_CHAR, &kb_char); - LOG_INF("KB put %02x", kb_char); -} - -/* Put an aux char to host buffer by HIMDO and assert status bit 5. */ -void lpc_aux_put_char(uint8_t chr, int send_irq) -{ - uint32_t kb_char = chr; - uint32_t status = I8042_AUX_DATA; - - espi_write_lpc_request(espi_dev, E8042_SET_FLAG, &status); - espi_write_lpc_request(espi_dev, E8042_WRITE_KB_CHAR, &kb_char); - LOG_INF("AUX put %02x", kb_char); -} - -static void kbc_ibf_obe_handler(uint32_t data) -{ -#ifdef HAS_TASK_KEYPROTO - uint8_t is_ibf = is_8042_ibf(data); - uint32_t status = I8042_AUX_DATA; - - if (is_ibf) { - keyboard_host_write(get_8042_data(data), - get_8042_type(data)); - } else if (IS_ENABLED(CONFIG_8042_AUX)) { - espi_write_lpc_request(espi_dev, E8042_CLEAR_FLAG, &status); - } - task_wake(TASK_ID_KEYPROTO); -#endif -} - -int lpc_keyboard_input_pending(void) -{ - uint32_t status; - - /* if IBF bit is '1', that mean still have a data in DBBIN */ - espi_read_lpc_request(espi_dev, E8042_IBF_HAS_CHAR, &status); - return status; -} - -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); - } - - if (IS_ENABLED(CONFIG_PLATFORM_EC_ACPI) && - event_type == ESPI_PERIPHERAL_HOST_IO) { - handle_acpi_write(event.evt_data); - } - - if (IS_ENABLED(CONFIG_PLATFORM_EC_HOSTCMD) && - event_type == ESPI_PERIPHERAL_EC_HOST_CMD) { - handle_host_write(event.evt_data); - } - - if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC) && - IS_ENABLED(HAS_TASK_KEYPROTO) && - event_type == ESPI_PERIPHERAL_8042_KBC) { - kbc_ibf_obe_handler(event.evt_data); - } -} - -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, - }, -#ifdef CONFIG_PLATFORM_EC_CHIPSET_RESET_HOOK - { - .handler = espi_reset_handler, - .event_type = ESPI_BUS_RESET, - }, -#endif - }; - - 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_DT_GET(ESPI_NODE); - if (!device_is_ready(espi_dev)) { - LOG_ERR("Error: device %s is not ready", espi_dev->name); - 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; -} |