zephyr/drivers/espi/espi_mchp_xec.c
Jose Alberto Meza 30eda2058b treewide: drivers: espi: Adjust terms per eSPI specification 1.5
1) Replace master/slave in API for new terms in eSPI spec 1.5
2) Reflect eSPI VW change and macro changes across eSPI drivers
3) Update terms in eSPI driver sample and eSPI test driver

Signed-off-by: Jose Alberto Meza <jose.a.meza.arellano@intel.com>
2024-05-02 13:56:46 -04:00

1602 lines
46 KiB
C

/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_espi
#include <zephyr/kernel.h>
#include <soc.h>
#include <errno.h>
#include <zephyr/drivers/espi.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
#include "espi_utils.h"
/* Minimum delay before acknowledging a virtual wire */
#define ESPI_XEC_VWIRE_ACK_DELAY 10ul
/* Maximum timeout to transmit a virtual wire packet.
* 10 ms expressed in multiples of 100us
*/
#define ESPI_XEC_VWIRE_SEND_TIMEOUT 100ul
#define VW_MAX_GIRQS 2ul
/* 200ms */
#define MAX_OOB_TIMEOUT 200ul
/* 1s */
#define MAX_FLASH_TIMEOUT 1000ul
/* While issuing flash erase command, it should be ensured that the transfer
* length specified is non-zero.
*/
#define ESPI_FLASH_ERASE_DUMMY 0x01ul
/* OOB maximum address configuration */
#define ESPI_XEC_OOB_ADDR_MSW 0x1FFFul
#define ESPI_XEC_OOB_ADDR_LSW 0xFFFFul
/* OOB Rx length */
#define ESPI_XEC_OOB_RX_LEN 0x7F00ul
/* BARs as defined in LPC spec chapter 11 */
#define ESPI_XEC_KBC_BAR_ADDRESS 0x00600000
#define ESPI_XEC_UART0_BAR_ADDRESS 0x03F80000
#define ESPI_XEC_MBOX_BAR_ADDRESS 0x03600000
#define ESPI_XEC_PORT80_BAR_ADDRESS 0x00800000
#define ESPI_XEC_PORT81_BAR_ADDRESS 0x00810000
/* Espi peripheral has 3 uart ports */
#define ESPI_PERIPHERAL_UART_PORT0 0
#define ESPI_PERIPHERAL_UART_PORT1 1
#define ESPI_PERIPHERAL_UART_PORT2 2
#define UART_DEFAULT_IRQ_POS 2u
#define UART_DEFAULT_IRQ BIT(UART_DEFAULT_IRQ_POS)
/* VM index 0x50 for OCB */
#define ESPI_OCB_VW_INDEX 0x50u
LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL);
struct espi_isr {
uint32_t girq_bit;
void (*the_isr)(const struct device *dev);
};
struct espi_xec_config {
uint32_t base_addr;
uint8_t bus_girq_id;
uint8_t vw_girq_ids[VW_MAX_GIRQS];
uint8_t pc_girq_id;
const struct pinctrl_dev_config *pcfg;
};
struct espi_xec_data {
sys_slist_t callbacks;
struct k_sem tx_lock;
struct k_sem rx_lock;
struct k_sem flash_lock;
};
struct xec_signal {
uint8_t xec_reg_idx;
uint8_t bit;
uint8_t dir;
};
enum mchp_msvw_regs {
MCHP_MSVW00,
MCHP_MSVW01,
MCHP_MSVW02,
MCHP_MSVW03,
MCHP_MSVW04,
MCHP_MSVW05,
MCHP_MSVW06,
MCHP_MSVW07,
MCHP_MSVW08,
};
enum mchp_smvw_regs {
MCHP_SMVW00,
MCHP_SMVW01,
MCHP_SMVW02,
MCHP_SMVW03,
MCHP_SMVW04,
MCHP_SMVW05,
MCHP_SMVW06,
MCHP_SMVW07,
MCHP_SMVW08,
};
/* Microchip canonical virtual wire mapping
* --------------------------------------------------------------------------------|
* VW Idx | VW reg | SRC_ID3 | SRC_ID2 | SRC_ID1 | SRC_ID0 |
* --------------------------------------------------------------------------------|
* System Event Virtual Wires
* --------------------------------------------------------------------------------|
* 2h | MSVW00 | res | SLP_S5# | SLP_S4# | SLP_S3# |
* 3h | MSVW01 | res | OOB_RST_WARN | PLTRST# | SUS_STAT# |
* 4h | SMVW00 | PME# | WAKE# | res | OOB_RST_ACK |
* 5h | SMVW01 | TARGET_BOOT_STS | ERR_NONFATAL | ERR_FATAL | TARGET_BOOT_DONE |
* 6h | SMVW02 | HOST_RST_ACK | RCIN# | SMI# | SCI# |
* 7h | MSVW02 | res | res | res | HOS_RST_WARN |
* --------------------------------------------------------------------------------|
* Platform specific virtual wires
* --------------------------------------------------------------------------------|
* 40h | SMVW03 | res | res | DNX_ACK | SUS_ACK# |
* 41h | MSVW03 | SLP_A# | res | SUS_PDNACK| SUS_WARN# |
* 42h | MSVW04 | res | res | SLP_WLAN# | SLP_LAN# |
* 43h | MSVW05 | generic | generic | generic | generic |
* 44h | MSVW06 | generic | generic | generic | generic |
* 45h | SMVW04 | generic | generic | generic | generic |
* 46h | SMVW05 | generic | generic | generic | generic |
* 47h | MSVW07 | res | res | res | HOST_C10 |
* 4Ah | MSVW08 | res | res | DNX_WARN | res |
* 50h | SMVW06 | ESPI_OCB_3 | ESPI_OCB_2 | ESPI_OCB_1| ESPI_OCB_0 |
*/
static const struct xec_signal vw_tbl[] = {
/* MSVW00 */
[ESPI_VWIRE_SIGNAL_SLP_S3] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID0,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_SLP_S4] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID1,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_SLP_S5] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID2,
ESPI_CONTROLLER_TO_TARGET},
/* MSVW01 */
[ESPI_VWIRE_SIGNAL_SUS_STAT] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID0,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_PLTRST] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID1,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_OOB_RST_WARN] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID2,
ESPI_CONTROLLER_TO_TARGET},
/* SMVW00 */
[ESPI_VWIRE_SIGNAL_OOB_RST_ACK] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID0,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_WAKE] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID2,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_PME] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID3,
ESPI_TARGET_TO_CONTROLLER},
/* SMVW01 */
[ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID0,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_ERR_FATAL] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID1,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_ERR_NON_FATAL] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID2,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_TARGET_BOOT_STS] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID3,
ESPI_TARGET_TO_CONTROLLER},
/* SMVW02 */
[ESPI_VWIRE_SIGNAL_SCI] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID0,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_SMI] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID1,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_RST_CPU_INIT] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID2,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_HOST_RST_ACK] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID3,
ESPI_TARGET_TO_CONTROLLER},
/* MSVW02 */
[ESPI_VWIRE_SIGNAL_HOST_RST_WARN] = {MCHP_MSVW02, ESPI_VWIRE_SRC_ID0,
ESPI_CONTROLLER_TO_TARGET},
/* SMVW03 */
[ESPI_VWIRE_SIGNAL_SUS_ACK] = {MCHP_SMVW03, ESPI_VWIRE_SRC_ID0,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_DNX_ACK] = {MCHP_SMVW03, ESPI_VWIRE_SRC_ID1,
ESPI_TARGET_TO_CONTROLLER},
/* MSVW03 */
[ESPI_VWIRE_SIGNAL_SUS_WARN] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID0,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID1,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_SLP_A] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID3,
ESPI_CONTROLLER_TO_TARGET},
/* MSVW04 */
[ESPI_VWIRE_SIGNAL_SLP_LAN] = {MCHP_MSVW04, ESPI_VWIRE_SRC_ID0,
ESPI_CONTROLLER_TO_TARGET},
[ESPI_VWIRE_SIGNAL_SLP_WLAN] = {MCHP_MSVW04, ESPI_VWIRE_SRC_ID1,
ESPI_CONTROLLER_TO_TARGET},
/* MSVW07 */
[ESPI_VWIRE_SIGNAL_HOST_C10] = {MCHP_MSVW07, ESPI_VWIRE_SRC_ID0,
ESPI_CONTROLLER_TO_TARGET},
/* MSVW08 */
[ESPI_VWIRE_SIGNAL_DNX_WARN] = {MCHP_MSVW08, ESPI_VWIRE_SRC_ID1,
ESPI_CONTROLLER_TO_TARGET},
/* SMVW06 */
[ESPI_VWIRE_SIGNAL_OCB_0] = {MCHP_SMVW06, ESPI_VWIRE_SRC_ID0,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_OCB_1] = {MCHP_SMVW06, ESPI_VWIRE_SRC_ID1,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_OCB_2] = {MCHP_SMVW06, ESPI_VWIRE_SRC_ID2,
ESPI_TARGET_TO_CONTROLLER},
[ESPI_VWIRE_SIGNAL_OCB_3] = {MCHP_SMVW06, ESPI_VWIRE_SRC_ID3,
ESPI_TARGET_TO_CONTROLLER},
};
/* Buffer size are expressed in bytes */
#ifdef CONFIG_ESPI_OOB_CHANNEL
static uint32_t target_rx_mem[CONFIG_ESPI_OOB_BUFFER_SIZE >> 2];
static uint32_t target_tx_mem[CONFIG_ESPI_OOB_BUFFER_SIZE >> 2];
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
static uint32_t target_mem[CONFIG_ESPI_FLASH_BUFFER_SIZE >> 2];
#endif
static int espi_xec_configure(const struct device *dev, struct espi_cfg *cfg)
{
uint8_t iomode = 0;
uint8_t cap0 = ESPI_CAP_REGS->GLB_CAP0;
uint8_t cap1 = ESPI_CAP_REGS->GLB_CAP1;
uint8_t cur_iomode = (cap1 & MCHP_ESPI_GBL_CAP1_IO_MODE_MASK) >>
MCHP_ESPI_GBL_CAP1_IO_MODE_POS;
/* Set frequency */
cap1 &= ~MCHP_ESPI_GBL_CAP1_MAX_FREQ_MASK;
switch (cfg->max_freq) {
case 20:
cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_20M;
break;
case 25:
cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_25M;
break;
case 33:
cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_33M;
break;
case 50:
cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_50M;
break;
case 66:
cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_66M;
break;
default:
return -EINVAL;
}
/* Set IO mode */
iomode = (cfg->io_caps >> 1);
if (iomode > 3) {
return -EINVAL;
}
if (iomode != cur_iomode) {
cap1 &= ~(MCHP_ESPI_GBL_CAP1_IO_MODE_MASK0 <<
MCHP_ESPI_GBL_CAP1_IO_MODE_POS);
cap1 |= (iomode << MCHP_ESPI_GBL_CAP1_IO_MODE_POS);
}
/* Validate and translate eSPI API channels to MEC capabilities */
cap0 &= ~MCHP_ESPI_GBL_CAP0_MASK;
if (cfg->channel_caps & ESPI_CHANNEL_PERIPHERAL) {
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_CHANNEL)) {
cap0 |= MCHP_ESPI_GBL_CAP0_PC_SUPP;
} else {
return -EINVAL;
}
}
if (cfg->channel_caps & ESPI_CHANNEL_VWIRE) {
if (IS_ENABLED(CONFIG_ESPI_VWIRE_CHANNEL)) {
cap0 |= MCHP_ESPI_GBL_CAP0_VW_SUPP;
} else {
return -EINVAL;
}
}
if (cfg->channel_caps & ESPI_CHANNEL_OOB) {
if (IS_ENABLED(CONFIG_ESPI_OOB_CHANNEL)) {
cap0 |= MCHP_ESPI_GBL_CAP0_OOB_SUPP;
} else {
return -EINVAL;
}
}
if (cfg->channel_caps & ESPI_CHANNEL_FLASH) {
if (IS_ENABLED(CONFIG_ESPI_FLASH_CHANNEL)) {
cap0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP;
} else {
LOG_ERR("Flash channel not supported");
return -EINVAL;
}
}
ESPI_CAP_REGS->GLB_CAP0 = cap0;
ESPI_CAP_REGS->GLB_CAP1 = cap1;
/* Activate the eSPI block *.
* Need to guarantee that this register is configured before RSMRST#
* de-assertion and after pinmux
*/
ESPI_EIO_BAR_REGS->IO_ACTV = 1;
LOG_DBG("eSPI block activated successfully");
return 0;
}
static bool espi_xec_channel_ready(const struct device *dev,
enum espi_channel ch)
{
bool sts;
switch (ch) {
case ESPI_CHANNEL_PERIPHERAL:
sts = ESPI_CAP_REGS->PC_RDY & MCHP_ESPI_PC_READY;
break;
case ESPI_CHANNEL_VWIRE:
sts = ESPI_CAP_REGS->VW_RDY & MCHP_ESPI_VW_READY;
break;
case ESPI_CHANNEL_OOB:
sts = ESPI_CAP_REGS->OOB_RDY & MCHP_ESPI_OOB_READY;
break;
case ESPI_CHANNEL_FLASH:
sts = ESPI_CAP_REGS->FC_RDY & MCHP_ESPI_FC_READY;
break;
default:
sts = false;
break;
}
return sts;
}
static int espi_xec_read_lpc_request(const struct device *dev,
enum lpc_peripheral_opcode op,
uint32_t *data)
{
ARG_UNUSED(dev);
if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) {
/* Make sure kbc 8042 is on */
if (!(KBC_REGS->KBC_CTRL & MCHP_KBC_CTRL_OBFEN)) {
return -ENOTSUP;
}
switch (op) {
case E8042_OBF_HAS_CHAR:
/* EC has written data back to host. OBF is
* automatically cleared after host reads
* the data
*/
*data = KBC_REGS->EC_KBC_STS & MCHP_KBC_STS_OBF ? 1 : 0;
break;
case E8042_IBF_HAS_CHAR:
*data = KBC_REGS->EC_KBC_STS & MCHP_KBC_STS_IBF ? 1 : 0;
break;
case E8042_READ_KB_STS:
*data = KBC_REGS->EC_KBC_STS;
break;
default:
return -EINVAL;
}
} else {
return -ENOTSUP;
}
return 0;
}
static int espi_xec_write_lpc_request(const struct device *dev,
enum lpc_peripheral_opcode op,
uint32_t *data)
{
struct espi_xec_config *config =
(struct espi_xec_config *) (dev->config);
volatile uint32_t __attribute__((unused)) dummy;
if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) {
/* Make sure kbc 8042 is on */
if (!(KBC_REGS->KBC_CTRL & MCHP_KBC_CTRL_OBFEN)) {
return -ENOTSUP;
}
switch (op) {
case E8042_WRITE_KB_CHAR:
KBC_REGS->EC_DATA = *data & 0xff;
break;
case E8042_WRITE_MB_CHAR:
KBC_REGS->EC_AUX_DATA = *data & 0xff;
break;
case E8042_RESUME_IRQ:
MCHP_GIRQ_SRC(config->pc_girq_id) = MCHP_KBC_IBF_GIRQ;
MCHP_GIRQ_ENSET(config->pc_girq_id) = MCHP_KBC_IBF_GIRQ;
break;
case E8042_PAUSE_IRQ:
MCHP_GIRQ_ENCLR(config->pc_girq_id) = MCHP_KBC_IBF_GIRQ;
break;
case E8042_CLEAR_OBF:
dummy = KBC_REGS->HOST_AUX_DATA;
break;
case E8042_SET_FLAG:
/* FW shouldn't modify these flags directly */
*data &= ~(MCHP_KBC_STS_OBF | MCHP_KBC_STS_IBF |
MCHP_KBC_STS_AUXOBF);
KBC_REGS->EC_KBC_STS |= *data;
break;
case E8042_CLEAR_FLAG:
/* FW shouldn't modify these flags directly */
*data |= (MCHP_KBC_STS_OBF | MCHP_KBC_STS_IBF |
MCHP_KBC_STS_AUXOBF);
KBC_REGS->EC_KBC_STS &= ~(*data);
break;
default:
return -EINVAL;
}
} else {
return -ENOTSUP;
}
return 0;
}
static int espi_xec_send_vwire(const struct device *dev,
enum espi_vwire_signal signal, uint8_t level)
{
struct xec_signal signal_info = vw_tbl[signal];
uint8_t xec_id = signal_info.xec_reg_idx;
uint8_t src_id = signal_info.bit;
if ((src_id >= ESPI_VWIRE_SRC_ID_MAX) ||
(xec_id >= ESPI_MSVW_IDX_MAX)) {
return -EINVAL;
}
if (signal_info.dir == ESPI_CONTROLLER_TO_TARGET) {
ESPI_MSVW_REG *reg = &(ESPI_M2S_VW_REGS->MSVW00) + xec_id;
uint8_t *p8 = (uint8_t *)&reg->SRC;
*(p8 + (uintptr_t) src_id) = level;
}
if (signal_info.dir == ESPI_TARGET_TO_CONTROLLER) {
ESPI_SMVW_REG *reg = &(ESPI_S2M_VW_REGS->SMVW00) + xec_id;
uint8_t *p8 = (uint8_t *)&reg->SRC;
*(p8 + (uintptr_t) src_id) = level;
/* Ensure eSPI virtual wire packet is transmitted
* There is no interrupt, so need to poll register
*/
uint8_t rd_cnt = ESPI_XEC_VWIRE_SEND_TIMEOUT;
while (reg->SRC_CHG && rd_cnt--) {
k_busy_wait(100);
}
}
return 0;
}
static int espi_xec_receive_vwire(const struct device *dev,
enum espi_vwire_signal signal, uint8_t *level)
{
struct xec_signal signal_info = vw_tbl[signal];
uint8_t xec_id = signal_info.xec_reg_idx;
uint8_t src_id = signal_info.bit;
if ((src_id >= ESPI_VWIRE_SRC_ID_MAX) ||
(xec_id >= ESPI_SMVW_IDX_MAX) || (level == NULL)) {
return -EINVAL;
}
if (signal_info.dir == ESPI_CONTROLLER_TO_TARGET) {
ESPI_MSVW_REG *reg = &(ESPI_M2S_VW_REGS->MSVW00) + xec_id;
*level = ((reg->SRC >> (src_id << 3)) & 0x01ul);
}
if (signal_info.dir == ESPI_TARGET_TO_CONTROLLER) {
ESPI_SMVW_REG *reg = &(ESPI_S2M_VW_REGS->SMVW00) + xec_id;
*level = ((reg->SRC >> (src_id << 3)) & 0x01ul);
}
return 0;
}
#ifdef CONFIG_ESPI_OOB_CHANNEL
static int espi_xec_send_oob(const struct device *dev,
struct espi_oob_packet *pckt)
{
int ret;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
uint8_t err_mask = MCHP_ESPI_OOB_TX_STS_IBERR |
MCHP_ESPI_OOB_TX_STS_OVRUN |
MCHP_ESPI_OOB_TX_STS_BADREQ;
LOG_DBG("%s", __func__);
if (!(ESPI_OOB_REGS->TX_STS & MCHP_ESPI_OOB_TX_STS_CHEN)) {
LOG_ERR("OOB channel is disabled");
return -EIO;
}
if (ESPI_OOB_REGS->TX_STS & MCHP_ESPI_OOB_TX_STS_BUSY) {
LOG_ERR("OOB channel is busy");
return -EBUSY;
}
if (pckt->len > CONFIG_ESPI_OOB_BUFFER_SIZE) {
LOG_ERR("insufficient space");
return -EINVAL;
}
memcpy(target_tx_mem, pckt->buf, pckt->len);
ESPI_OOB_REGS->TX_LEN = pckt->len;
ESPI_OOB_REGS->TX_CTRL = MCHP_ESPI_OOB_TX_CTRL_START;
LOG_DBG("%s %d", __func__, ESPI_OOB_REGS->TX_LEN);
/* Wait until ISR or timeout */
ret = k_sem_take(&data->tx_lock, K_MSEC(MAX_OOB_TIMEOUT));
if (ret == -EAGAIN) {
return -ETIMEDOUT;
}
if (ESPI_OOB_REGS->TX_STS & err_mask) {
LOG_ERR("Tx failed %x", ESPI_OOB_REGS->TX_STS);
ESPI_OOB_REGS->TX_STS = err_mask;
return -EIO;
}
return 0;
}
static int espi_xec_receive_oob(const struct device *dev,
struct espi_oob_packet *pckt)
{
uint8_t err_mask = MCHP_ESPI_OOB_RX_STS_IBERR |
MCHP_ESPI_OOB_RX_STS_OVRUN;
if (ESPI_OOB_REGS->TX_STS & err_mask) {
return -EIO;
}
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
int ret;
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));
if (ret == -EAGAIN) {
return -ETIMEDOUT;
}
#endif
/* Check if buffer passed to driver can fit the received buffer */
uint32_t rcvd_len = ESPI_OOB_REGS->RX_LEN & MCHP_ESPI_OOB_RX_LEN_MASK;
if (rcvd_len > pckt->len) {
LOG_ERR("space rcvd %d vs %d", rcvd_len, pckt->len);
return -EIO;
}
pckt->len = rcvd_len;
memcpy(pckt->buf, target_rx_mem, pckt->len);
memset(target_rx_mem, 0, pckt->len);
/* Only after data has been copied from SRAM, indicate channel
* is available for next packet
*/
ESPI_OOB_REGS->RX_CTRL |= MCHP_ESPI_OOB_RX_CTRL_AVAIL;
return 0;
}
#endif /* CONFIG_ESPI_OOB_CHANNEL */
#ifdef CONFIG_ESPI_FLASH_CHANNEL
static int espi_xec_flash_read(const struct device *dev,
struct espi_flash_packet *pckt)
{
int ret;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
uint32_t err_mask = MCHP_ESPI_FC_STS_IBERR |
MCHP_ESPI_FC_STS_FAIL |
MCHP_ESPI_FC_STS_OVFL |
MCHP_ESPI_FC_STS_BADREQ;
LOG_DBG("%s", __func__);
if (!(ESPI_FC_REGS->STS & MCHP_ESPI_FC_STS_CHAN_EN)) {
LOG_ERR("Flash channel is disabled");
return -EIO;
}
if (pckt->len > CONFIG_ESPI_FLASH_BUFFER_SIZE) {
LOG_ERR("Invalid size request");
return -EINVAL;
}
ESPI_FC_REGS->FL_ADDR_MSW = 0;
ESPI_FC_REGS->FL_ADDR_LSW = pckt->flash_addr;
ESPI_FC_REGS->MEM_ADDR_MSW = 0;
ESPI_FC_REGS->MEM_ADDR_LSW = (uint32_t)&target_mem[0];
ESPI_FC_REGS->XFR_LEN = pckt->len;
ESPI_FC_REGS->CTRL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_RD0);
ESPI_FC_REGS->CTRL |= MCHP_ESPI_FC_CTRL_START;
/* Wait until ISR or timeout */
ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT));
if (ret == -EAGAIN) {
LOG_ERR("%s timeout", __func__);
return -ETIMEDOUT;
}
if (ESPI_FC_REGS->STS & err_mask) {
LOG_ERR("%s error %x", __func__, err_mask);
ESPI_FC_REGS->STS = err_mask;
return -EIO;
}
memcpy(pckt->buf, target_mem, pckt->len);
return 0;
}
static int espi_xec_flash_write(const struct device *dev,
struct espi_flash_packet *pckt)
{
int ret;
uint32_t err_mask = MCHP_ESPI_FC_STS_IBERR |
MCHP_ESPI_FC_STS_OVRUN |
MCHP_ESPI_FC_STS_FAIL |
MCHP_ESPI_FC_STS_BADREQ;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
LOG_DBG("%s", __func__);
if (sizeof(target_mem) < pckt->len) {
LOG_ERR("Packet length is too big");
return -ENOMEM;
}
if (!(ESPI_FC_REGS->STS & MCHP_ESPI_FC_STS_CHAN_EN)) {
LOG_ERR("Flash channel is disabled");
return -EIO;
}
if ((ESPI_FC_REGS->CFG & MCHP_ESPI_FC_CFG_BUSY)) {
LOG_ERR("Flash channel is busy");
return -EBUSY;
}
memcpy(target_mem, pckt->buf, pckt->len);
ESPI_FC_REGS->FL_ADDR_MSW = 0;
ESPI_FC_REGS->FL_ADDR_LSW = pckt->flash_addr;
ESPI_FC_REGS->MEM_ADDR_MSW = 0;
ESPI_FC_REGS->MEM_ADDR_LSW = (uint32_t)&target_mem[0];
ESPI_FC_REGS->XFR_LEN = pckt->len;
ESPI_FC_REGS->CTRL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_WR0);
ESPI_FC_REGS->CTRL |= MCHP_ESPI_FC_CTRL_START;
/* Wait until ISR or timeout */
ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT));
if (ret == -EAGAIN) {
LOG_ERR("%s timeout", __func__);
return -ETIMEDOUT;
}
if (ESPI_FC_REGS->STS & err_mask) {
LOG_ERR("%s err: %x", __func__, err_mask);
ESPI_FC_REGS->STS = err_mask;
return -EIO;
}
return 0;
}
static int espi_xec_flash_erase(const struct device *dev,
struct espi_flash_packet *pckt)
{
int ret;
uint32_t status;
uint32_t err_mask = MCHP_ESPI_FC_STS_IBERR |
MCHP_ESPI_FC_STS_OVRUN |
MCHP_ESPI_FC_STS_FAIL |
MCHP_ESPI_FC_STS_BADREQ;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
LOG_DBG("%s", __func__);
if (!(ESPI_FC_REGS->STS & MCHP_ESPI_FC_STS_CHAN_EN)) {
LOG_ERR("Flash channel is disabled");
return -EIO;
}
if ((ESPI_FC_REGS->CFG & MCHP_ESPI_FC_CFG_BUSY)) {
LOG_ERR("Flash channel is busy");
return -EBUSY;
}
/* Clear status register */
status = ESPI_FC_REGS->STS;
ESPI_FC_REGS->STS = status;
ESPI_FC_REGS->FL_ADDR_MSW = 0;
ESPI_FC_REGS->FL_ADDR_LSW = pckt->flash_addr;
ESPI_FC_REGS->XFR_LEN = ESPI_FLASH_ERASE_DUMMY;
ESPI_FC_REGS->CTRL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_ERS0);
ESPI_FC_REGS->CTRL |= MCHP_ESPI_FC_CTRL_START;
/* Wait until ISR or timeout */
ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT));
if (ret == -EAGAIN) {
LOG_ERR("%s timeout", __func__);
return -ETIMEDOUT;
}
if (ESPI_FC_REGS->STS & err_mask) {
LOG_ERR("%s err: %x", __func__, err_mask);
ESPI_FC_REGS->STS = err_mask;
return -EIO;
}
return 0;
}
#endif /* CONFIG_ESPI_FLASH_CHANNEL */
static int espi_xec_manage_callback(const struct device *dev,
struct espi_callback *callback, bool set)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
return espi_manage_callback(&data->callbacks, callback, set);
}
#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE
static void send_target_bootdone(const struct device *dev)
{
int ret;
uint8_t boot_done;
ret = espi_xec_receive_vwire(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE,
&boot_done);
if (!ret && !boot_done) {
/* TARGET_BOOT_DONE & TARGET_LOAD_STS have to be sent together */
espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_STS, 1);
espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE, 1);
}
}
#endif
#ifdef CONFIG_ESPI_OOB_CHANNEL
static void espi_init_oob(const struct device *dev)
{
struct espi_xec_config *config =
(struct espi_xec_config *) (dev->config);
/* Enable OOB Tx/Rx interrupts */
MCHP_GIRQ_ENSET(config->bus_girq_id) = (MCHP_ESPI_OOB_UP_GIRQ_VAL |
MCHP_ESPI_OOB_DN_GIRQ_VAL);
ESPI_OOB_REGS->TX_ADDR_MSW = 0;
ESPI_OOB_REGS->RX_ADDR_MSW = 0;
ESPI_OOB_REGS->TX_ADDR_LSW = (uint32_t)&target_tx_mem[0];
ESPI_OOB_REGS->RX_ADDR_LSW = (uint32_t)&target_rx_mem[0];
ESPI_OOB_REGS->RX_LEN = 0x00FF0000;
/* Enable OOB Tx channel enable change status interrupt */
ESPI_OOB_REGS->TX_IEN |= MCHP_ESPI_OOB_TX_IEN_CHG_EN |
MCHP_ESPI_OOB_TX_IEN_DONE;
/* Enable Rx channel to receive data any time
* there are case where OOB is not initiated by a previous OOB Tx
*/
ESPI_OOB_REGS->RX_IEN |= MCHP_ESPI_OOB_RX_IEN;
ESPI_OOB_REGS->RX_CTRL |= MCHP_ESPI_OOB_RX_CTRL_AVAIL;
}
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
static void espi_init_flash(const struct device *dev)
{
struct espi_xec_config *config =
(struct espi_xec_config *)(dev->config);
LOG_DBG("%s", __func__);
/* Need to clear status done when ROM boots in MAF */
LOG_DBG("%s ESPI_FC_REGS->CFG %X", __func__, ESPI_FC_REGS->CFG);
ESPI_FC_REGS->STS = MCHP_ESPI_FC_STS_DONE;
/* Enable interrupts */
MCHP_GIRQ_ENSET(config->bus_girq_id) = BIT(MCHP_ESPI_FC_GIRQ_POS);
ESPI_FC_REGS->IEN |= MCHP_ESPI_FC_IEN_CHG_EN;
ESPI_FC_REGS->IEN |= MCHP_ESPI_FC_IEN_DONE;
}
#endif
static void espi_bus_init(const struct device *dev)
{
const struct espi_xec_config *config = dev->config;
/* Enable bus interrupts */
MCHP_GIRQ_ENSET(config->bus_girq_id) = MCHP_ESPI_ESPI_RST_GIRQ_VAL |
MCHP_ESPI_VW_EN_GIRQ_VAL | MCHP_ESPI_PC_GIRQ_VAL;
}
void espi_config_vw_ocb(void)
{
ESPI_SMVW_REG *reg = &(ESPI_S2M_VW_REGS->SMVW06);
/* Keep index bits [7:0] in initial 0h value (disabled state) */
mec_espi_smvw_index_set(reg, 0);
/* Set 01b (eSPI_RESET# domain) into bits [9:8] which frees the
* register from all except chip level resets and set initial state
* of VW wires as 1111b in bits [15:12].
*/
mec_espi_msvw_stom_set(reg, VW_RST_SRC_ESPI_RESET, 0x1);
/* Set 4 SMVW SRC bits in bit positions [0], [8], [16] and [24] to
* initial value '1'.
*/
mec_espi_smvw_set_all_bitmap(reg, 0xF);
/* Set 00b (eSPI_RESET# domain) into bits [9:8] while preserving
* the values in bits [15:12].
*/
mec_espi_msvw_stom_set(reg, VW_RST_SRC_ESPI_RESET, 0x0);
/* Set INDEX field with OCB VW index */
mec_espi_smvw_index_set(reg, ESPI_OCB_VW_INDEX);
}
static void espi_rst_isr(const struct device *dev)
{
uint8_t rst_sts;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { ESPI_BUS_RESET, 0, 0 };
rst_sts = ESPI_CAP_REGS->ERST_STS;
/* eSPI reset status register is clear on write register */
ESPI_CAP_REGS->ERST_STS = MCHP_ESPI_RST_ISTS;
if (rst_sts & MCHP_ESPI_RST_ISTS) {
if (rst_sts & MCHP_ESPI_RST_ISTS_PIN_RO_HI) {
evt.evt_data = 1;
} else {
evt.evt_data = 0;
}
espi_send_callbacks(&data->callbacks, dev, evt);
#ifdef CONFIG_ESPI_OOB_CHANNEL
espi_init_oob(dev);
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
espi_init_flash(dev);
#endif
espi_bus_init(dev);
}
}
/* Configure sub devices BAR address if not using default I/O based address
* then make its BAR valid.
* Refer to microchip eSPI I/O base addresses for default values
*/
static void config_sub_devices(const struct device *dev)
{
#ifdef CONFIG_ESPI_PERIPHERAL_UART
/* eSPI logical UART is tied to corresponding physical UART
* Not all boards use same UART port for debug, hence needs to set
* eSPI host logical UART0 bar address based on configuration.
*/
switch (CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING) {
case 0:
ESPI_EIO_BAR_REGS->EC_BAR_UART_0 = ESPI_XEC_UART0_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
break;
case 1:
ESPI_EIO_BAR_REGS->EC_BAR_UART_1 = ESPI_XEC_UART0_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
break;
case 2:
ESPI_EIO_BAR_REGS->EC_BAR_UART_2 = ESPI_XEC_UART0_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
break;
}
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC
KBC_REGS->KBC_CTRL |= MCHP_KBC_CTRL_AUXH;
KBC_REGS->KBC_CTRL |= MCHP_KBC_CTRL_OBFEN;
/* This is the activate register, but the HAL has a funny name */
KBC_REGS->KBC_PORT92_EN = MCHP_KBC_PORT92_EN;
ESPI_EIO_BAR_REGS->EC_BAR_KBC = ESPI_XEC_KBC_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO
ESPI_EIO_BAR_REGS->EC_BAR_ACPI_EC_0 |= MCHP_ESPI_IO_BAR_HOST_VALID;
ESPI_EIO_BAR_REGS->EC_BAR_MBOX = ESPI_XEC_MBOX_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT
ESPI_EIO_BAR_REGS->EC_BAR_ACPI_EC_1 =
CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT_PORT_NUM |
MCHP_ESPI_IO_BAR_HOST_VALID;
ESPI_EIO_BAR_REGS->EC_BAR_MBOX = ESPI_XEC_MBOX_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80
ESPI_EIO_BAR_REGS->EC_BAR_P80CAP_0 = ESPI_XEC_PORT80_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
PORT80_CAP0_REGS->ACTV = 1;
ESPI_EIO_BAR_REGS->EC_BAR_P80CAP_1 = ESPI_XEC_PORT81_BAR_ADDRESS |
MCHP_ESPI_IO_BAR_HOST_VALID;
PORT80_CAP1_REGS->ACTV = 1;
#endif
}
static void configure_sirq(void)
{
#ifdef CONFIG_ESPI_PERIPHERAL_UART
switch (CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING) {
case ESPI_PERIPHERAL_UART_PORT0:
ESPI_SIRQ_REGS->UART_0_SIRQ = UART_DEFAULT_IRQ;
break;
case ESPI_PERIPHERAL_UART_PORT1:
ESPI_SIRQ_REGS->UART_1_SIRQ = UART_DEFAULT_IRQ;
break;
case ESPI_PERIPHERAL_UART_PORT2:
ESPI_SIRQ_REGS->UART_2_SIRQ = UART_DEFAULT_IRQ;
break;
}
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC
ESPI_SIRQ_REGS->KBC_SIRQ_0 = 0x01;
ESPI_SIRQ_REGS->KBC_SIRQ_1 = 0x0C;
#endif
}
static void setup_espi_io_config(const struct device *dev,
uint16_t host_address)
{
ESPI_EIO_BAR_REGS->EC_BAR_IOC = (host_address << 16) |
MCHP_ESPI_IO_BAR_HOST_VALID;
config_sub_devices(dev);
configure_sirq();
ESPI_PC_REGS->PC_STATUS = (MCHP_ESPI_PC_STS_EN_CHG |
MCHP_ESPI_PC_STS_BM_EN_CHG_POS);
ESPI_PC_REGS->PC_IEN |= MCHP_ESPI_PC_IEN_EN_CHG;
ESPI_CAP_REGS->PC_RDY = 1;
}
static void espi_pc_isr(const struct device *dev)
{
uint32_t status = ESPI_PC_REGS->PC_STATUS;
if (status & MCHP_ESPI_PC_STS_EN_CHG) {
if (status & MCHP_ESPI_PC_STS_EN) {
setup_espi_io_config(dev, MCHP_ESPI_IOBAR_INIT_DFLT);
}
ESPI_PC_REGS->PC_STATUS = MCHP_ESPI_PC_STS_EN_CHG;
}
}
static void espi_vwire_chanel_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
const struct espi_xec_config *config = dev->config;
struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY,
.evt_details = ESPI_CHANNEL_VWIRE,
.evt_data = 0 };
uint32_t status;
status = ESPI_IO_VW_REGS->VW_EN_STS;
if (status & MCHP_ESPI_VW_EN_STS_RO) {
ESPI_IO_VW_REGS->VW_RDY = 1;
evt.evt_data = 1;
/* VW channel interrupt can disabled at this point */
MCHP_GIRQ_ENCLR(config->bus_girq_id) = MCHP_ESPI_VW_EN_GIRQ_VAL;
#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE
send_target_bootdone(dev);
#endif
}
espi_send_callbacks(&data->callbacks, dev, evt);
}
#ifdef CONFIG_ESPI_OOB_CHANNEL
static void espi_oob_down_isr(const struct device *dev)
{
uint32_t status;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_OOB_RECEIVED,
.evt_details = 0,
.evt_data = 0 };
#endif
status = ESPI_OOB_REGS->RX_STS;
LOG_DBG("%s %x", __func__, status);
if (status & MCHP_ESPI_OOB_RX_STS_DONE) {
/* Register is write-on-clear, ensure only 1 bit is affected */
ESPI_OOB_REGS->RX_STS = MCHP_ESPI_OOB_RX_STS_DONE;
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
k_sem_give(&data->rx_lock);
#else
evt.evt_details = ESPI_OOB_REGS->RX_LEN & MCHP_ESPI_OOB_RX_LEN_MASK;
espi_send_callbacks(&data->callbacks, dev, evt);
#endif
}
}
static void espi_oob_up_isr(const struct device *dev)
{
uint32_t status;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY,
.evt_details = ESPI_CHANNEL_OOB,
.evt_data = 0
};
status = ESPI_OOB_REGS->TX_STS;
LOG_DBG("%s sts:%x", __func__, status);
if (status & MCHP_ESPI_OOB_TX_STS_DONE) {
/* Register is write-on-clear, ensure only 1 bit is affected */
ESPI_OOB_REGS->TX_STS = MCHP_ESPI_OOB_TX_STS_DONE;
k_sem_give(&data->tx_lock);
}
if (status & MCHP_ESPI_OOB_TX_STS_CHG_EN) {
if (status & MCHP_ESPI_OOB_TX_STS_CHEN) {
espi_init_oob(dev);
/* Indicate OOB channel is ready to eSPI host */
ESPI_CAP_REGS->OOB_RDY = 1;
evt.evt_data = 1;
}
ESPI_OOB_REGS->TX_STS = MCHP_ESPI_OOB_TX_STS_CHG_EN;
espi_send_callbacks(&data->callbacks, dev, evt);
}
}
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
static void espi_flash_isr(const struct device *dev)
{
uint32_t status;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY,
.evt_details = ESPI_CHANNEL_FLASH,
.evt_data = 0,
};
status = ESPI_FC_REGS->STS;
LOG_DBG("%s %x", __func__, status);
if (status & MCHP_ESPI_FC_STS_DONE) {
/* Ensure to clear only relevant bit */
ESPI_FC_REGS->STS = MCHP_ESPI_FC_STS_DONE;
k_sem_give(&data->flash_lock);
}
if (status & MCHP_ESPI_FC_STS_CHAN_EN_CHG) {
/* Ensure to clear only relevant bit */
ESPI_FC_REGS->STS = MCHP_ESPI_FC_STS_CHAN_EN_CHG;
if (status & MCHP_ESPI_FC_STS_CHAN_EN) {
espi_init_flash(dev);
/* Indicate flash channel is ready to eSPI controller */
ESPI_CAP_REGS->FC_RDY = MCHP_ESPI_FC_READY;
evt.evt_data = 1;
}
espi_send_callbacks(&data->callbacks, dev, evt);
}
}
#endif
static void vw_pltrst_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED,
ESPI_VWIRE_SIGNAL_PLTRST, 0
};
uint8_t status = 0;
espi_xec_receive_vwire(dev, ESPI_VWIRE_SIGNAL_PLTRST, &status);
if (status) {
setup_espi_io_config(dev, MCHP_ESPI_IOBAR_INIT_DFLT);
}
evt.evt_data = status;
espi_send_callbacks(&data->callbacks, dev, evt);
}
/* Send callbacks if enabled and track eSPI host system state */
static void notify_system_state(const struct device *dev,
enum espi_vwire_signal signal)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0 };
uint8_t status = 0;
espi_xec_receive_vwire(dev, signal, &status);
evt.evt_details = signal;
evt.evt_data = status;
espi_send_callbacks(&data->callbacks, dev, evt);
}
static void notify_host_warning(const struct device *dev,
enum espi_vwire_signal signal)
{
uint8_t status;
espi_xec_receive_vwire(dev, signal, &status);
if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) {
struct espi_xec_data *data =
(struct espi_xec_data *)(dev->data);
struct espi_event evt = {ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0 };
evt.evt_details = signal;
evt.evt_data = status;
espi_send_callbacks(&data->callbacks, dev, evt);
} else {
k_busy_wait(ESPI_XEC_VWIRE_ACK_DELAY);
/* Some flows are dependent on awareness of client's driver
* about these warnings in such cases these automatic response
* should not be enabled.
*/
switch (signal) {
case ESPI_VWIRE_SIGNAL_HOST_RST_WARN:
espi_xec_send_vwire(dev,
ESPI_VWIRE_SIGNAL_HOST_RST_ACK,
status);
break;
case ESPI_VWIRE_SIGNAL_SUS_WARN:
espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_SUS_ACK,
status);
break;
case ESPI_VWIRE_SIGNAL_OOB_RST_WARN:
espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_OOB_RST_ACK,
status);
break;
case ESPI_VWIRE_SIGNAL_DNX_WARN:
espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_DNX_ACK,
status);
break;
default:
break;
}
}
}
static void vw_slp3_isr(const struct device *dev)
{
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S3);
}
static void vw_slp4_isr(const struct device *dev)
{
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S4);
}
static void vw_slp5_isr(const struct device *dev)
{
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S5);
}
static void vw_host_rst_warn_isr(const struct device *dev)
{
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_HOST_RST_WARN);
}
static void vw_sus_warn_isr(const struct device *dev)
{
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_SUS_WARN);
/* Configure spare VW register SMVW06 to VW index 50h. As per
* per microchip recommendation, spare VW register should be
* configured between TARGET_BOOT_LOAD_DONE = 1 VW event and
* point where SUS_ACK=1 VW is sent to SOC.
*/
espi_config_vw_ocb();
}
static void vw_oob_rst_isr(const struct device *dev)
{
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_OOB_RST_WARN);
}
static void vw_sus_pwrdn_ack_isr(const struct device *dev)
{
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK);
}
static void vw_sus_slp_a_isr(const struct device *dev)
{
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_A);
}
static void ibf_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION,
ESPI_PERIPHERAL_HOST_IO, ESPI_PERIPHERAL_NODATA
};
espi_send_callbacks(&data->callbacks, dev, evt);
}
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT
static void ibf_pvt_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = {
.evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION,
.evt_details = ESPI_PERIPHERAL_HOST_IO_PVT,
.evt_data = ESPI_PERIPHERAL_NODATA
};
espi_send_callbacks(&data->callbacks, dev, evt);
}
#endif
static void ibf_kbc_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
/* The high byte contains information from the host,
* and the lower byte specifies if the host sent
* a command or data. 1 = Command.
*/
uint32_t isr_data = ((KBC_REGS->EC_DATA & 0xFF) << E8042_ISR_DATA_POS) |
((KBC_REGS->EC_KBC_STS & MCHP_KBC_STS_CD) <<
E8042_ISR_CMD_DATA_POS);
struct espi_event evt = {
.evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION,
.evt_details = ESPI_PERIPHERAL_8042_KBC,
.evt_data = isr_data
};
espi_send_callbacks(&data->callbacks, dev, evt);
}
static void port80_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION,
(ESPI_PERIPHERAL_INDEX_0 << 16) | ESPI_PERIPHERAL_DEBUG_PORT80,
ESPI_PERIPHERAL_NODATA
};
evt.evt_data = PORT80_CAP0_REGS->EC_DATA;
espi_send_callbacks(&data->callbacks, dev, evt);
}
static void port81_isr(const struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION,
(ESPI_PERIPHERAL_INDEX_1 << 16) | ESPI_PERIPHERAL_DEBUG_PORT80,
ESPI_PERIPHERAL_NODATA
};
evt.evt_data = PORT80_CAP1_REGS->EC_DATA;
espi_send_callbacks(&data->callbacks, dev, evt);
}
const struct espi_isr espi_bus_isr[] = {
{MCHP_ESPI_PC_GIRQ_VAL, espi_pc_isr},
#ifdef CONFIG_ESPI_OOB_CHANNEL
{MCHP_ESPI_OOB_UP_GIRQ_VAL, espi_oob_up_isr},
{MCHP_ESPI_OOB_DN_GIRQ_VAL, espi_oob_down_isr},
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
{MCHP_ESPI_FC_GIRQ_VAL, espi_flash_isr},
#endif
{MCHP_ESPI_ESPI_RST_GIRQ_VAL, espi_rst_isr},
{MCHP_ESPI_VW_EN_GIRQ_VAL, espi_vwire_chanel_isr},
};
uint8_t vw_wires_int_en[] = {
ESPI_VWIRE_SIGNAL_SLP_S3,
ESPI_VWIRE_SIGNAL_SLP_S4,
ESPI_VWIRE_SIGNAL_SLP_S5,
ESPI_VWIRE_SIGNAL_PLTRST,
ESPI_VWIRE_SIGNAL_OOB_RST_WARN,
ESPI_VWIRE_SIGNAL_HOST_RST_WARN,
ESPI_VWIRE_SIGNAL_SUS_WARN,
ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK,
ESPI_VWIRE_SIGNAL_DNX_WARN,
};
const struct espi_isr m2s_vwires_isr[] = {
{MEC_ESPI_MSVW00_SRC0_VAL, vw_slp3_isr},
{MEC_ESPI_MSVW00_SRC1_VAL, vw_slp4_isr},
{MEC_ESPI_MSVW00_SRC2_VAL, vw_slp5_isr},
{MEC_ESPI_MSVW01_SRC1_VAL, vw_pltrst_isr},
{MEC_ESPI_MSVW01_SRC2_VAL, vw_oob_rst_isr},
{MEC_ESPI_MSVW02_SRC0_VAL, vw_host_rst_warn_isr},
{MEC_ESPI_MSVW03_SRC0_VAL, vw_sus_warn_isr},
{MEC_ESPI_MSVW03_SRC1_VAL, vw_sus_pwrdn_ack_isr},
{MEC_ESPI_MSVW03_SRC3_VAL, vw_sus_slp_a_isr},
};
const struct espi_isr peripherals_isr[] = {
{MCHP_ACPI_EC_0_IBF_GIRQ, ibf_isr},
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT
{MCHP_ACPI_EC_1_IBF_GIRQ, ibf_pvt_isr},
#endif
{MCHP_KBC_IBF_GIRQ, ibf_kbc_isr},
{MCHP_PORT80_DEBUG0_GIRQ_VAL, port80_isr},
{MCHP_PORT80_DEBUG1_GIRQ_VAL, port81_isr},
};
static uint8_t bus_isr_cnt = sizeof(espi_bus_isr) / sizeof(struct espi_isr);
static uint8_t m2s_vwires_isr_cnt =
sizeof(m2s_vwires_isr) / sizeof(struct espi_isr);
static uint8_t periph_isr_cnt = sizeof(peripherals_isr) / sizeof(struct espi_isr);
static void espi_xec_bus_isr(const struct device *dev)
{
const struct espi_xec_config *config = dev->config;
uint32_t girq_result;
girq_result = MCHP_GIRQ_RESULT(config->bus_girq_id);
for (int i = 0; i < bus_isr_cnt; i++) {
struct espi_isr entry = espi_bus_isr[i];
if (girq_result & entry.girq_bit) {
if (entry.the_isr != NULL) {
entry.the_isr(dev);
}
}
}
REG32(MCHP_GIRQ_SRC_ADDR(config->bus_girq_id)) = girq_result;
}
static void espi_xec_vw_isr(const struct device *dev)
{
const struct espi_xec_config *config = dev->config;
uint32_t girq_result;
girq_result = MCHP_GIRQ_RESULT(config->vw_girq_ids[0]);
MCHP_GIRQ_SRC(config->vw_girq_ids[0]) = girq_result;
for (int i = 0; i < m2s_vwires_isr_cnt; i++) {
struct espi_isr entry = m2s_vwires_isr[i];
if (girq_result & entry.girq_bit) {
if (entry.the_isr != NULL) {
entry.the_isr(dev);
}
}
}
}
#if DT_INST_PROP_HAS_IDX(0, vw_girqs, 1)
static void vw_sus_dnx_warn_isr(const struct device *dev)
{
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_DNX_WARN);
}
const struct espi_isr m2s_vwires_ext_isr[] = {
{MEC_ESPI_MSVW08_SRC1_VAL, vw_sus_dnx_warn_isr}
};
static void espi_xec_vw_ext_isr(const struct device *dev)
{
const struct espi_xec_config *config = dev->config;
uint32_t girq_result;
girq_result = MCHP_GIRQ_RESULT(config->vw_girq_ids[1]);
MCHP_GIRQ_SRC(config->vw_girq_ids[1]) = girq_result;
for (int i = 0; i < ARRAY_SIZE(m2s_vwires_ext_isr); i++) {
struct espi_isr entry = m2s_vwires_ext_isr[i];
if (girq_result & entry.girq_bit) {
if (entry.the_isr != NULL) {
entry.the_isr(dev);
}
}
}
}
#endif
static void espi_xec_periph_isr(const struct device *dev)
{
const struct espi_xec_config *config = dev->config;
uint32_t girq_result;
girq_result = MCHP_GIRQ_RESULT(config->pc_girq_id);
for (int i = 0; i < periph_isr_cnt; i++) {
struct espi_isr entry = peripherals_isr[i];
if (girq_result & entry.girq_bit) {
if (entry.the_isr != NULL) {
entry.the_isr(dev);
}
}
}
REG32(MCHP_GIRQ_SRC_ADDR(config->pc_girq_id)) = girq_result;
}
static int espi_xec_init(const struct device *dev);
static const struct espi_driver_api espi_xec_driver_api = {
.config = espi_xec_configure,
.get_channel_status = espi_xec_channel_ready,
.send_vwire = espi_xec_send_vwire,
.receive_vwire = espi_xec_receive_vwire,
#ifdef CONFIG_ESPI_OOB_CHANNEL
.send_oob = espi_xec_send_oob,
.receive_oob = espi_xec_receive_oob,
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
.flash_read = espi_xec_flash_read,
.flash_write = espi_xec_flash_write,
.flash_erase = espi_xec_flash_erase,
#endif
.manage_callback = espi_xec_manage_callback,
.read_lpc_request = espi_xec_read_lpc_request,
.write_lpc_request = espi_xec_write_lpc_request,
};
static struct espi_xec_data espi_xec_data;
/* pin control structure(s) */
PINCTRL_DT_INST_DEFINE(0);
static const struct espi_xec_config espi_xec_config = {
.base_addr = DT_INST_REG_ADDR(0),
.bus_girq_id = DT_INST_PROP(0, io_girq),
.vw_girq_ids[0] = DT_INST_PROP_BY_IDX(0, vw_girqs, 0),
.vw_girq_ids[1] = DT_INST_PROP_BY_IDX(0, vw_girqs, 1),
.pc_girq_id = DT_INST_PROP(0, pc_girq),
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
};
DEVICE_DT_INST_DEFINE(0, &espi_xec_init, NULL,
&espi_xec_data, &espi_xec_config,
PRE_KERNEL_2, CONFIG_ESPI_INIT_PRIORITY,
&espi_xec_driver_api);
static int espi_xec_init(const struct device *dev)
{
const struct espi_xec_config *config = dev->config;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->data);
int ret;
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret != 0) {
LOG_ERR("XEC eSPI pinctrl setup failed (%d)", ret);
return ret;
}
/* Configure eSPI_PLTRST# to cause nSIO_RESET reset */
PCR_REGS->PWR_RST_CTRL = MCHP_PCR_PR_CTRL_USE_ESPI_PLTRST;
ESPI_CAP_REGS->PLTRST_SRC = MCHP_ESPI_PLTRST_SRC_IS_VW;
/* Configure the channels and its capabilities based on build config */
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_GBL_CAP0_VW_SUPP;
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_GBL_CAP0_PC_SUPP;
/* Max VW count is 12 pairs */
ESPI_CAP_REGS->VW_CAP = ESPI_NUM_SMVW;
ESPI_CAP_REGS->PC_CAP |= MCHP_ESPI_PC_CAP_MAX_PLD_SZ_64;
#ifdef CONFIG_ESPI_OOB_CHANNEL
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_GBL_CAP0_OOB_SUPP;
ESPI_CAP_REGS->OOB_CAP |= MCHP_ESPI_OOB_CAP_MAX_PLD_SZ_73;
k_sem_init(&data->tx_lock, 0, 1);
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
k_sem_init(&data->rx_lock, 0, 1);
#endif /* CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC */
#else
ESPI_CAP_REGS->GLB_CAP0 &= ~MCHP_ESPI_GBL_CAP0_OOB_SUPP;
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP;
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_FC_CAP_MAX_PLD_SZ_64;
ESPI_CAP_REGS->FC_CAP |= MCHP_ESPI_FC_CAP_SHARE_MAF_SAF;
ESPI_CAP_REGS->FC_CAP |= MCHP_ESPI_FC_CAP_MAX_RD_SZ_64;
k_sem_init(&data->flash_lock, 0, 1);
#else
ESPI_CAP_REGS->GLB_CAP0 &= ~MCHP_ESPI_GBL_CAP0_FC_SUPP;
#endif
/* Clear reset interrupt status and enable interrupts */
ESPI_CAP_REGS->ERST_STS = MCHP_ESPI_RST_ISTS;
ESPI_CAP_REGS->ERST_IEN |= MCHP_ESPI_RST_IEN;
ESPI_PC_REGS->PC_STATUS = MCHP_ESPI_PC_STS_EN_CHG;
ESPI_PC_REGS->PC_IEN |= MCHP_ESPI_PC_IEN_EN_CHG;
/* Enable VWires interrupts */
for (int i = 0; i < sizeof(vw_wires_int_en); i++) {
uint8_t signal = vw_wires_int_en[i];
struct xec_signal signal_info = vw_tbl[signal];
uint8_t xec_id = signal_info.xec_reg_idx;
ESPI_MSVW_REG *reg = &(ESPI_M2S_VW_REGS->MSVW00) + xec_id;
mec_espi_msvw_irq_sel_set(reg, signal_info.bit,
MSVW_IRQ_SEL_EDGE_BOTH);
}
/* Enable interrupts for each logical channel enable assertion */
MCHP_GIRQ_ENSET(config->bus_girq_id) = MCHP_ESPI_ESPI_RST_GIRQ_VAL |
MCHP_ESPI_VW_EN_GIRQ_VAL | MCHP_ESPI_PC_GIRQ_VAL;
#ifdef CONFIG_ESPI_OOB_CHANNEL
espi_init_oob(dev);
#endif
#ifdef CONFIG_ESPI_FLASH_CHANNEL
espi_init_flash(dev);
#endif
/* Enable aggregated block interrupts for VWires */
MCHP_GIRQ_ENSET(config->vw_girq_ids[0]) = MEC_ESPI_MSVW00_SRC0_VAL |
MEC_ESPI_MSVW00_SRC1_VAL | MEC_ESPI_MSVW00_SRC2_VAL |
MEC_ESPI_MSVW01_SRC1_VAL | MEC_ESPI_MSVW01_SRC2_VAL |
MEC_ESPI_MSVW02_SRC0_VAL | MEC_ESPI_MSVW03_SRC0_VAL;
/* Enable aggregated block interrupts for peripherals supported */
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC
MCHP_GIRQ_ENSET(config->pc_girq_id) = MCHP_KBC_IBF_GIRQ;
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO
MCHP_GIRQ_ENSET(config->pc_girq_id) = MCHP_ACPI_EC_0_IBF_GIRQ;
MCHP_GIRQ_ENSET(config->pc_girq_id) = MCHP_ACPI_EC_2_IBF_GIRQ;
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT
MCHP_GIRQ_ENSET(config->pc_girq_id) = MCHP_ACPI_EC_1_IBF_GIRQ;
#endif
#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80
MCHP_GIRQ_ENSET(config->pc_girq_id) = MCHP_PORT80_DEBUG0_GIRQ_VAL |
MCHP_PORT80_DEBUG1_GIRQ_VAL;
#endif
/* Enable aggregated interrupt block for eSPI bus events */
MCHP_GIRQ_BLK_SETEN(config->bus_girq_id);
IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
espi_xec_bus_isr,
DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQN(0));
/* Enable aggregated interrupt block for eSPI VWire events */
MCHP_GIRQ_BLK_SETEN(config->vw_girq_ids[0]);
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 1, irq),
DT_INST_IRQ_BY_IDX(0, 1, priority),
espi_xec_vw_isr,
DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQ_BY_IDX(0, 1, irq));
/* Enable aggregated interrupt block for eSPI peripheral channel */
MCHP_GIRQ_BLK_SETEN(config->pc_girq_id);
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 2, irq),
DT_INST_IRQ_BY_IDX(0, 2, priority),
espi_xec_periph_isr,
DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQ_BY_IDX(0, 2, irq));
#if DT_INST_PROP_HAS_IDX(0, vw_girqs, 1)
MCHP_GIRQ_ENSET(config->vw_girq_ids[1]) = MEC_ESPI_MSVW08_SRC1_VAL;
MCHP_GIRQ_BLK_SETEN(config->vw_girq_ids[1]);
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 3, irq),
DT_INST_IRQ_BY_IDX(0, 3, priority),
espi_xec_vw_ext_isr,
DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQ_BY_IDX(0, 3, irq));
#endif
return 0;
}