| /* |
| * Copyright (c) 2019 Intel Corporation |
| * Copyright (c) 2021 Microchip Technology Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT microchip_xec_espi_v2 |
| |
| #include <zephyr/kernel.h> |
| #include <soc.h> |
| #include <errno.h> |
| #include <zephyr/drivers/espi.h> |
| #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h> |
| #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h> |
| #include <zephyr/dt-bindings/interrupt-controller/mchp-xec-ecia.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys/sys_io.h> |
| #include <zephyr/sys/util.h> |
| #include "espi_utils.h" |
| #include "espi_mchp_xec_v2.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 |
| |
| /* Espi peripheral has 3 uart ports */ |
| #define ESPI_PERIPHERAL_UART_PORT0 0 |
| #define ESPI_PERIPHERAL_UART_PORT1 1 |
| |
| #define UART_DEFAULT_IRQ_POS 2u |
| #define UART_DEFAULT_IRQ BIT(UART_DEFAULT_IRQ_POS) |
| |
| LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL); |
| |
| #define ESPI_XEC_REG_BASE(dev) \ |
| ((struct espi_iom_regs *)ESPI_XEC_CONFIG(dev)->base_addr) |
| |
| #define ESPI_XEC_MSVW_REG_BASE(dev) \ |
| ((struct espi_msvw_ar_regs *)(ESPI_XEC_CONFIG(dev)->vw_base_addr)) |
| |
| #define ESPI_XEC_SMVW_REG_OFS 0x200 |
| |
| #define ESPI_XEC_SMVW_REG_BASE(dev) \ |
| ((struct espi_smvw_ar_regs *) \ |
| (ESPI_XEC_CONFIG(dev)->vw_base_addr + ESPI_XEC_SMVW_REG_OFS)) |
| |
| /* PCR */ |
| #define XEC_PCR_REG_BASE \ |
| ((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)))) |
| |
| /* 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 | SLV_BOOT_STS | ERR_NONFATAL | ERR_FATAL | SLV_BT_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 | |
| */ |
| |
| static const struct xec_signal vw_tbl[] = { |
| /* MSVW00 */ |
| [ESPI_VWIRE_SIGNAL_SLP_S3] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID0, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_SLP_S4] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID1, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_SLP_S5] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID2, |
| ESPI_MASTER_TO_SLAVE}, |
| /* MSVW01 */ |
| [ESPI_VWIRE_SIGNAL_SUS_STAT] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID0, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_PLTRST] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID1, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_OOB_RST_WARN] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID2, |
| ESPI_MASTER_TO_SLAVE}, |
| /* SMVW00 */ |
| [ESPI_VWIRE_SIGNAL_OOB_RST_ACK] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID0, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_WAKE] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID2, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_PME] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID3, |
| ESPI_SLAVE_TO_MASTER}, |
| /* SMVW01 */ |
| [ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID0, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_ERR_FATAL] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID1, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_ERR_NON_FATAL] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID2, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_SLV_BOOT_STS] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID3, |
| ESPI_SLAVE_TO_MASTER}, |
| /* SMVW02 */ |
| [ESPI_VWIRE_SIGNAL_SCI] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID0, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_SMI] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID1, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_RST_CPU_INIT] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID2, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_HOST_RST_ACK] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID3, |
| ESPI_SLAVE_TO_MASTER}, |
| /* MSVW02 */ |
| [ESPI_VWIRE_SIGNAL_HOST_RST_WARN] = {MCHP_MSVW02, ESPI_VWIRE_SRC_ID0, |
| ESPI_MASTER_TO_SLAVE}, |
| /* SMVW03 */ |
| [ESPI_VWIRE_SIGNAL_SUS_ACK] = {MCHP_SMVW03, ESPI_VWIRE_SRC_ID0, |
| ESPI_SLAVE_TO_MASTER}, |
| [ESPI_VWIRE_SIGNAL_DNX_ACK] = {MCHP_SMVW03, ESPI_VWIRE_SRC_ID1, |
| ESPI_SLAVE_TO_MASTER}, |
| /* MSVW03 */ |
| [ESPI_VWIRE_SIGNAL_SUS_WARN] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID0, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID1, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_SLP_A] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID3, |
| ESPI_MASTER_TO_SLAVE}, |
| /* MSVW04 */ |
| [ESPI_VWIRE_SIGNAL_SLP_LAN] = {MCHP_MSVW04, ESPI_VWIRE_SRC_ID0, |
| ESPI_MASTER_TO_SLAVE}, |
| [ESPI_VWIRE_SIGNAL_SLP_WLAN] = {MCHP_MSVW04, ESPI_VWIRE_SRC_ID1, |
| ESPI_MASTER_TO_SLAVE}, |
| /* MSVW07 */ |
| [ESPI_VWIRE_SIGNAL_HOST_C10] = {MCHP_MSVW07, ESPI_VWIRE_SRC_ID0, |
| ESPI_MASTER_TO_SLAVE}, |
| /* MSVW08 */ |
| [ESPI_VWIRE_SIGNAL_DNX_WARN] = {MCHP_MSVW08, ESPI_VWIRE_SRC_ID1, |
| ESPI_MASTER_TO_SLAVE}, |
| }; |
| |
| /* 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 inline uintptr_t xec_msvw_addr(const struct device *dev, |
| uint8_t vw_index) |
| { |
| uintptr_t vwbase = ESPI_XEC_CONFIG(dev)->vw_base_addr; |
| |
| return vwbase + vw_index * sizeof(struct espi_msvw_reg); |
| } |
| |
| static inline uintptr_t xec_smvw_addr(const struct device *dev, |
| uint8_t vw_index) |
| { |
| uintptr_t vwbase = ESPI_XEC_CONFIG(dev)->vw_base_addr; |
| |
| vwbase += ESPI_XEC_SMVW_REG_OFS; |
| return vwbase + vw_index * sizeof(struct espi_smvw_reg); |
| } |
| |
| static int espi_xec_configure(const struct device *dev, struct espi_cfg *cfg) |
| { |
| struct espi_iom_regs *iom_regs = ESPI_XEC_REG_BASE(dev); |
| uint8_t iomode = 0; |
| uint8_t cap0 = iom_regs->CAP0; |
| uint8_t cap1 = iom_regs->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; |
| } |
| } |
| |
| iom_regs->CAP0 = cap0; |
| iom_regs->CAP1 = cap1; |
| |
| /* Activate the eSPI block *. |
| * Need to guarantee that this register is configured before RSMRST# |
| * de-assertion and after pinmux |
| */ |
| iom_regs->ACTV = 1; |
| LOG_DBG("eSPI block activated successfully"); |
| |
| return 0; |
| } |
| |
| static bool espi_xec_channel_ready(const struct device *dev, |
| enum espi_channel ch) |
| { |
| struct espi_iom_regs *iom_regs = ESPI_XEC_REG_BASE(dev); |
| bool sts; |
| |
| switch (ch) { |
| case ESPI_CHANNEL_PERIPHERAL: |
| sts = iom_regs->PCRDY & MCHP_ESPI_PC_READY; |
| break; |
| case ESPI_CHANNEL_VWIRE: |
| sts = iom_regs->VWRDY & MCHP_ESPI_VW_READY; |
| break; |
| case ESPI_CHANNEL_OOB: |
| sts = iom_regs->OOBRDY & MCHP_ESPI_OOB_READY; |
| break; |
| case ESPI_CHANNEL_FLASH: |
| sts = iom_regs->FCRDY & MCHP_ESPI_FC_READY; |
| break; |
| default: |
| sts = false; |
| break; |
| } |
| |
| return sts; |
| } |
| |
| 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; |
| uintptr_t regaddr; |
| |
| if ((src_id >= ESPI_VWIRE_SRC_ID_MAX) || |
| (xec_id >= ESPI_MSVW_IDX_MAX)) { |
| return -EINVAL; |
| } |
| |
| if (signal_info.dir == ESPI_MASTER_TO_SLAVE) { |
| regaddr = xec_msvw_addr(dev, xec_id); |
| |
| sys_write8(level, regaddr + MSVW_BI_SRC0 + src_id); |
| } |
| |
| if (signal_info.dir == ESPI_SLAVE_TO_MASTER) { |
| regaddr = xec_smvw_addr(dev, xec_id); |
| |
| sys_write8(level, regaddr + SMVW_BI_SRC0 + src_id); |
| |
| /* 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 (sys_read8(regaddr + SMVW_BI_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; |
| uintptr_t regaddr; |
| |
| if ((src_id >= ESPI_VWIRE_SRC_ID_MAX) || |
| (xec_id >= ESPI_SMVW_IDX_MAX) || (level == NULL)) { |
| return -EINVAL; |
| } |
| |
| if (signal_info.dir == ESPI_MASTER_TO_SLAVE) { |
| regaddr = xec_msvw_addr(dev, xec_id); |
| *level = sys_read8(regaddr + MSVW_BI_SRC0 + src_id) & BIT(0); |
| } |
| |
| if (signal_info.dir == ESPI_SLAVE_TO_MASTER) { |
| regaddr = xec_smvw_addr(dev, xec_id); |
| *level = sys_read8(regaddr + SMVW_BI_SRC0 + src_id) & BIT(0); |
| } |
| |
| 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_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| 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 (!(regs->OOBTXSTS & MCHP_ESPI_OOB_TX_STS_CHEN)) { |
| LOG_ERR("OOB channel is disabled"); |
| return -EIO; |
| } |
| |
| if (regs->OOBTXSTS & 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); |
| |
| regs->OOBTXL = pckt->len; |
| regs->OOBTXC = MCHP_ESPI_OOB_TX_CTRL_START; |
| LOG_DBG("%s %d", __func__, regs->OOBTXL); |
| |
| /* Wait until ISR or timeout */ |
| ret = k_sem_take(&data->tx_lock, K_MSEC(MAX_OOB_TIMEOUT)); |
| if (ret == -EAGAIN) { |
| return -ETIMEDOUT; |
| } |
| |
| if (regs->OOBTXSTS & err_mask) { |
| LOG_ERR("Tx failed %x", regs->OOBTXSTS); |
| regs->OOBTXSTS = err_mask; |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int espi_xec_receive_oob(const struct device *dev, |
| struct espi_oob_packet *pckt) |
| { |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| uint8_t err_mask = MCHP_ESPI_OOB_RX_STS_IBERR | |
| MCHP_ESPI_OOB_RX_STS_OVRUN; |
| |
| if (regs->OOBRXSTS & 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 = regs->OOBRXL & 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 |
| */ |
| regs->OOBRXC |= 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_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| 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 (!(regs->FCSTS & 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; |
| } |
| |
| regs->FCFA[1] = 0; |
| regs->FCFA[0] = pckt->flash_addr; |
| regs->FCBA[1] = 0; |
| regs->FCBA[0] = (uint32_t)&target_mem[0]; |
| regs->FCLEN = pckt->len; |
| regs->FCCTL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_RD0); |
| regs->FCCTL |= 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 (regs->FCSTS & err_mask) { |
| LOG_ERR("%s error %x", __func__, err_mask); |
| regs->FCSTS = 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; |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| 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 (!(regs->FCSTS & MCHP_ESPI_FC_STS_CHAN_EN)) { |
| LOG_ERR("Flash channel is disabled"); |
| return -EIO; |
| } |
| |
| if ((regs->FCCFG & MCHP_ESPI_FC_CFG_BUSY)) { |
| LOG_ERR("Flash channel is busy"); |
| return -EBUSY; |
| } |
| |
| memcpy(target_mem, pckt->buf, pckt->len); |
| |
| regs->FCFA[1] = 0; |
| regs->FCFA[0] = pckt->flash_addr; |
| regs->FCBA[1] = 0; |
| regs->FCBA[0] = (uint32_t)&target_mem[0]; |
| regs->FCLEN = pckt->len; |
| regs->FCCTL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_WR0); |
| regs->FCCTL |= 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 (regs->FCSTS & err_mask) { |
| LOG_ERR("%s err: %x", __func__, err_mask); |
| regs->FCSTS = 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_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *data = (struct espi_xec_data *)(dev->data); |
| |
| LOG_DBG("%s", __func__); |
| |
| if (!(regs->FCSTS & MCHP_ESPI_FC_STS_CHAN_EN)) { |
| LOG_ERR("Flash channel is disabled"); |
| return -EIO; |
| } |
| |
| if ((regs->FCCFG & MCHP_ESPI_FC_CFG_BUSY)) { |
| LOG_ERR("Flash channel is busy"); |
| return -EBUSY; |
| } |
| |
| /* Clear status register */ |
| status = regs->FCSTS; |
| regs->FCSTS = status; |
| |
| regs->FCFA[1] = 0; |
| regs->FCFA[0] = pckt->flash_addr; |
| regs->FCLEN = ESPI_FLASH_ERASE_DUMMY; |
| regs->FCCTL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_ERS0); |
| regs->FCCTL |= 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 (regs->FCSTS & err_mask) { |
| LOG_ERR("%s err: %x", __func__, err_mask); |
| regs->FCSTS = 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 *const data = ESPI_XEC_DATA(dev); |
| |
| return espi_manage_callback(&data->callbacks, callback, set); |
| } |
| |
| #ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
| static void send_slave_bootdone(const struct device *dev) |
| { |
| int ret; |
| uint8_t boot_done; |
| |
| ret = espi_xec_receive_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, |
| &boot_done); |
| if (!ret && !boot_done) { |
| /* SLAVE_BOOT_DONE & SLAVE_LOAD_STS have to be sent together */ |
| espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_STS, 1); |
| espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, 1); |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_ESPI_OOB_CHANNEL |
| static void espi_init_oob(const struct device *dev) |
| { |
| struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| |
| /* Enable OOB Tx/Rx interrupts */ |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[oob_up_girq_idx].gid, |
| cfg->irq_info_list[oob_up_girq_idx].gpos); |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[oob_dn_girq_idx].gid, |
| cfg->irq_info_list[oob_dn_girq_idx].gpos); |
| |
| regs->OOBTXA[1] = 0; |
| regs->OOBRXA[1] = 0; |
| regs->OOBTXA[0] = (uint32_t)&target_tx_mem[0]; |
| regs->OOBRXA[0] = (uint32_t)&target_rx_mem[0]; |
| regs->OOBRXL = 0x00FF0000; |
| |
| /* Enable OOB Tx channel enable change status interrupt */ |
| regs->OOBTXIEN |= 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 |
| */ |
| regs->OOBRXIEN |= MCHP_ESPI_OOB_RX_IEN; |
| regs->OOBRXC |= 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 *const cfg = ESPI_XEC_CONFIG(dev); |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| |
| LOG_DBG("%s", __func__); |
| |
| /* Need to clear status done when ROM boots in MAF */ |
| LOG_DBG("%s ESPI_FC_REGS->CFG %X", __func__, regs->FCCFG); |
| regs->FCSTS = MCHP_ESPI_FC_STS_DONE; |
| |
| /* Enable interrupts */ |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[fc_girq_idx].gid, |
| cfg->irq_info_list[fc_girq_idx].gpos); |
| regs->FCIEN |= MCHP_ESPI_FC_IEN_CHG_EN; |
| regs->FCIEN |= MCHP_ESPI_FC_IEN_DONE; |
| } |
| #endif |
| |
| static void espi_bus_init(const struct device *dev) |
| { |
| struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); |
| |
| /* Enable bus interrupts */ |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[pc_girq_idx].gid, |
| cfg->irq_info_list[pc_girq_idx].gpos); |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[rst_girq_idx].gid, |
| cfg->irq_info_list[rst_girq_idx].gpos); |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[vw_ch_en_girq_idx].gid, |
| cfg->irq_info_list[vw_ch_en_girq_idx].gpos); |
| } |
| |
| /* Clear specified eSPI bus GIRQ status */ |
| static int xec_espi_bus_intr_clr(const struct device *dev, |
| enum xec_espi_girq_idx idx) |
| { |
| struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); |
| |
| if (idx >= max_girq_idx) { |
| return -EINVAL; |
| } |
| |
| mchp_xec_ecia_girq_src_clr(cfg->irq_info_list[idx].gid, |
| cfg->irq_info_list[idx].gpos); |
| |
| return 0; |
| } |
| |
| /* Enable/disable specified eSPI bus GIRQ */ |
| static int xec_espi_bus_intr_ctl(const struct device *dev, |
| enum xec_espi_girq_idx idx, |
| uint8_t enable) |
| { |
| struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); |
| |
| if (idx >= max_girq_idx) { |
| return -EINVAL; |
| } |
| |
| if (enable) { |
| mchp_xec_ecia_girq_src_en(cfg->irq_info_list[idx].gid, |
| cfg->irq_info_list[idx].gpos); |
| } else { |
| mchp_xec_ecia_girq_src_dis(cfg->irq_info_list[idx].gid, |
| cfg->irq_info_list[idx].gpos); |
| } |
| |
| return 0; |
| } |
| |
| static void espi_rst_isr(const struct device *dev) |
| { |
| uint8_t rst_sts; |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| struct espi_event evt = { ESPI_BUS_RESET, 0, 0 }; |
| |
| #ifdef ESPI_XEC_V2_DEBUG |
| data->espi_rst_count++; |
| #endif |
| |
| rst_sts = regs->ERIS; |
| |
| /* eSPI reset status register is clear on write register */ |
| regs->ERIS = MCHP_ESPI_RST_ISTS; |
| /* clear GIRQ latched status */ |
| xec_espi_bus_intr_clr(dev, rst_girq_idx); |
| |
| if (rst_sts & MCHP_ESPI_RST_ISTS) { |
| if (rst_sts & MCHP_ESPI_RST_ISTS_PIN_RO_HI) { |
| data->espi_rst_asserted = 1; |
| } else { |
| data->espi_rst_asserted = 0; |
| } |
| |
| evt.evt_data = data->espi_rst_asserted; |
| 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) |
| { |
| xec_host_dev_init(dev); |
| } |
| |
| static void configure_sirq(const struct device *dev) |
| { |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| |
| #ifdef CONFIG_ESPI_PERIPHERAL_UART |
| switch (CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING) { |
| case ESPI_PERIPHERAL_UART_PORT0: |
| regs->SIRQ[SIRQ_UART0] = UART_DEFAULT_IRQ; |
| break; |
| case ESPI_PERIPHERAL_UART_PORT1: |
| regs->SIRQ[SIRQ_UART1] = UART_DEFAULT_IRQ; |
| break; |
| } |
| #endif |
| #ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
| regs->SIRQ[SIRQ_KBC_KIRQ] = 1; |
| regs->SIRQ[SIRQ_KBC_MIRQ] = 12; |
| #endif |
| } |
| |
| static void setup_espi_io_config(const struct device *dev, |
| uint16_t host_address) |
| { |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| |
| regs->IOHBAR[IOB_IOC] = (host_address << 16) | |
| MCHP_ESPI_IO_BAR_HOST_VALID; |
| |
| config_sub_devices(dev); |
| configure_sirq(dev); |
| |
| regs->PCSTS = MCHP_ESPI_PC_STS_EN_CHG | |
| MCHP_ESPI_PC_STS_BM_EN_CHG_POS; |
| regs->PCIEN |= MCHP_ESPI_PC_IEN_EN_CHG; |
| regs->PCRDY = 1; |
| } |
| |
| /* |
| * Write the interrupt select field of the specified MSVW source. |
| * Each MSVW controls 4 virtual wires. |
| */ |
| static int xec_espi_vw_intr_ctrl(const struct device *dev, uint8_t msvw_idx, |
| uint8_t src_id, uint8_t intr_mode) |
| { |
| struct espi_msvw_ar_regs *regs = ESPI_XEC_MSVW_REG_BASE(dev); |
| |
| |
| if ((msvw_idx >= ESPI_NUM_MSVW) || (src_id > 3)) { |
| return -EINVAL; |
| } |
| |
| uintptr_t msvw_addr = (uintptr_t)®s->MSVW[msvw_idx]; |
| |
| sys_write8(intr_mode, msvw_addr + MSVW_BI_IRQ_SEL0 + src_id); |
| |
| return 0; |
| } |
| |
| static void espi_pc_isr(const struct device *dev) |
| { |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| uint32_t status = regs->PCSTS; |
| struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
| .evt_details = ESPI_CHANNEL_PERIPHERAL, |
| .evt_data = 0 }; |
| struct espi_xec_data *data = (struct espi_xec_data *)(dev->data); |
| |
| LOG_DBG("%s %x", __func__, status); |
| if (status & MCHP_ESPI_PC_STS_BUS_ERR) { |
| LOG_ERR("%s bus error", __func__); |
| regs->PCSTS = MCHP_ESPI_PC_STS_BUS_ERR; |
| } |
| |
| 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); |
| } |
| |
| regs->PCSTS = MCHP_ESPI_PC_STS_EN_CHG; |
| } |
| |
| if (status & MCHP_ESPI_PC_STS_BM_EN_CHG) { |
| if (status & MCHP_ESPI_PC_STS_BM_EN) { |
| evt.evt_data = ESPI_PC_EVT_BUS_MASTER_ENABLE; |
| LOG_WRN("%s BM change %x", __func__, status); |
| espi_send_callbacks(&data->callbacks, dev, evt); |
| } |
| |
| regs->PCSTS = MCHP_ESPI_PC_STS_BM_EN_CHG; |
| } |
| |
| xec_espi_bus_intr_clr(dev, pc_girq_idx); |
| } |
| |
| static void espi_vw_chan_en_isr(const struct device *dev) |
| { |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
| .evt_details = ESPI_CHANNEL_VWIRE, |
| .evt_data = 0 }; |
| uint32_t status = regs->VWSTS; |
| |
| if (status & MCHP_ESPI_VW_EN_STS_RO) { |
| regs->VWRDY = 1; |
| evt.evt_data = 1; |
| /* VW channel interrupt can disabled at this point */ |
| xec_espi_bus_intr_ctl(dev, vw_ch_en_girq_idx, 0); |
| |
| #ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
| send_slave_bootdone(dev); |
| #endif |
| } |
| |
| espi_send_callbacks(&data->callbacks, dev, evt); |
| |
| xec_espi_bus_intr_clr(dev, vw_ch_en_girq_idx); |
| } |
| |
| #ifdef CONFIG_ESPI_OOB_CHANNEL |
| static void espi_oob_down_isr(const struct device *dev) |
| { |
| uint32_t status; |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| #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 = regs->OOBRXSTS; |
| |
| 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 */ |
| regs->OOBRXSTS = MCHP_ESPI_OOB_RX_STS_DONE; |
| |
| #ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
| k_sem_give(&data->rx_lock); |
| #else |
| evt.evt_details = regs->OOBRXL & |
| MCHP_ESPI_OOB_RX_LEN_MASK; |
| espi_send_callbacks(&data->callbacks, dev, evt); |
| #endif |
| } |
| |
| xec_espi_bus_intr_clr(dev, oob_dn_girq_idx); |
| } |
| |
| static void espi_oob_up_isr(const struct device *dev) |
| { |
| uint32_t status; |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
| .evt_details = ESPI_CHANNEL_OOB, |
| .evt_data = 0 |
| }; |
| |
| status = regs->OOBTXSTS; |
| 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 */ |
| status = regs->OOBTXSTS = 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 */ |
| regs->OOBRDY = 1; |
| evt.evt_data = 1; |
| } |
| |
| status = regs->OOBTXSTS = MCHP_ESPI_OOB_TX_STS_CHG_EN; |
| espi_send_callbacks(&data->callbacks, dev, evt); |
| } |
| |
| xec_espi_bus_intr_clr(dev, oob_up_girq_idx); |
| } |
| #endif |
| |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| static void espi_flash_isr(const struct device *dev) |
| { |
| uint32_t status; |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
| .evt_details = ESPI_CHANNEL_FLASH, |
| .evt_data = 0, |
| }; |
| |
| status = regs->FCSTS; |
| LOG_DBG("%s %x", __func__, status); |
| |
| if (status & MCHP_ESPI_FC_STS_DONE) { |
| /* Ensure to clear only relevant bit */ |
| regs->FCSTS = 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 */ |
| regs->FCSTS = 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 master */ |
| regs->FCRDY = MCHP_ESPI_FC_READY; |
| evt.evt_data = 1; |
| } |
| |
| espi_send_callbacks(&data->callbacks, dev, evt); |
| } |
| |
| xec_espi_bus_intr_clr(dev, fc_girq_idx); |
| } |
| #endif |
| |
| /* 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 *const data = ESPI_XEC_DATA(dev); |
| struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0 }; |
| uint8_t status = 0; |
| |
| espi_xec_receive_vwire(dev, signal, &status); |
| if (!status) { |
| data->sx_state = signal; |
| } |
| |
| 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 *const data = ESPI_XEC_DATA(dev); |
| 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; |
| } |
| } |
| } |
| |
| /* |
| * VW handlers must have signature |
| * typedef void (*mchp_xec_ecia_callback_t) (int girq_id, int src, void *user) |
| * where parameter user is a pointer to const struct device |
| * These handlers are registered to their respective GIRQ child device of the |
| * ECIA driver. |
| */ |
| |
| static void vw_slp3_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S3); |
| } |
| |
| static void vw_slp4_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S4); |
| } |
| |
| static void vw_slp5_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S5); |
| } |
| |
| static void vw_host_rst_warn_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_host_warning(dev, ESPI_VWIRE_SIGNAL_HOST_RST_WARN); |
| } |
| |
| static void vw_sus_warn_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_host_warning(dev, ESPI_VWIRE_SIGNAL_SUS_WARN); |
| } |
| |
| static void vw_oob_rst_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_host_warning(dev, ESPI_VWIRE_SIGNAL_OOB_RST_WARN); |
| } |
| |
| static void vw_sus_pwrdn_ack_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_system_state(dev, ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK); |
| } |
| |
| static void vw_sus_slp_a_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_A); |
| } |
| |
| static void vw_sus_dnx_warn_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| |
| notify_host_warning(dev, ESPI_VWIRE_SIGNAL_DNX_WARN); |
| } |
| |
| static void vw_pltrst_handler(int girq_id, int src, void *user) |
| { |
| const struct device *dev = (const struct device *)user; |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| 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); |
| } |
| |
| /* PLT_RST will be received several times */ |
| if (status != data->plt_rst_asserted) { |
| data->plt_rst_asserted = status; |
| evt.evt_data = status; |
| espi_send_callbacks(&data->callbacks, dev, evt); |
| } |
| } |
| |
| const struct espi_vw_isr m2s_vwires_isr[] = { |
| {ESPI_VWIRE_SIGNAL_SLP_S3, MCHP_MSVW00_GIRQ, MCHP_MSVW00_SRC0_GIRQ_POS, |
| vw_slp3_handler}, |
| {ESPI_VWIRE_SIGNAL_SLP_S4, MCHP_MSVW00_GIRQ, MCHP_MSVW00_SRC1_GIRQ_POS, |
| vw_slp4_handler}, |
| {ESPI_VWIRE_SIGNAL_SLP_S5, MCHP_MSVW00_GIRQ, MCHP_MSVW00_SRC2_GIRQ_POS, |
| vw_slp5_handler}, |
| {ESPI_VWIRE_SIGNAL_PLTRST, MCHP_MSVW01_GIRQ, MCHP_MSVW01_SRC1_GIRQ_POS, |
| vw_pltrst_handler}, |
| {ESPI_VWIRE_SIGNAL_OOB_RST_WARN, MCHP_MSVW01_GIRQ, |
| MCHP_MSVW01_SRC2_GIRQ_POS, vw_oob_rst_handler}, |
| {ESPI_VWIRE_SIGNAL_HOST_RST_WARN, MCHP_MSVW02_GIRQ, |
| MCHP_MSVW02_SRC0_GIRQ_POS, vw_host_rst_warn_handler}, |
| {ESPI_VWIRE_SIGNAL_SUS_WARN, MCHP_MSVW03_GIRQ, |
| MCHP_MSVW03_SRC0_GIRQ_POS, vw_sus_warn_handler}, |
| {ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK, MCHP_MSVW03_GIRQ, |
| MCHP_MSVW03_SRC1_GIRQ_POS, vw_sus_pwrdn_ack_handler}, |
| {ESPI_VWIRE_SIGNAL_SLP_A, MCHP_MSVW03_GIRQ, |
| MCHP_MSVW03_SRC3_GIRQ_POS, vw_sus_slp_a_handler}, |
| {ESPI_VWIRE_SIGNAL_DNX_WARN, MCHP_MSVW08_GIRQ, |
| MCHP_MSVW08_SRC1_GIRQ_POS, vw_sus_dnx_warn_handler} |
| }; |
| |
| 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_var; |
| |
| /* n = node-id, p = property, i = index */ |
| #define XEC_IRQ_INFO(n, p, i) \ |
| { \ |
| .gid = MCHP_XEC_ECIA_GIRQ(DT_PROP_BY_IDX(n, p, i)), \ |
| .gpos = MCHP_XEC_ECIA_GIRQ_POS(DT_PROP_BY_IDX(n, p, i)), \ |
| .anid = MCHP_XEC_ECIA_NVIC_AGGR(DT_PROP_BY_IDX(n, p, i)), \ |
| .dnid = MCHP_XEC_ECIA_NVIC_DIRECT(DT_PROP_BY_IDX(n, p, i)), \ |
| }, |
| |
| static const struct espi_xec_irq_info espi_xec_irq_info_0[] = { |
| DT_FOREACH_PROP_ELEM(DT_NODELABEL(espi0), girqs, XEC_IRQ_INFO) |
| }; |
| |
| /* 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), |
| .vw_base_addr = DT_INST_REG_ADDR_BY_NAME(0, vw), |
| .pcr_idx = DT_INST_PROP_BY_IDX(0, pcrs, 0), |
| .pcr_bitpos = DT_INST_PROP_BY_IDX(0, pcrs, 1), |
| .irq_info_size = ARRAY_SIZE(espi_xec_irq_info_0), |
| .irq_info_list = espi_xec_irq_info_0, |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), |
| }; |
| |
| DEVICE_DT_INST_DEFINE(0, &espi_xec_init, NULL, |
| &espi_xec_data_var, &espi_xec_config, |
| PRE_KERNEL_2, CONFIG_ESPI_INIT_PRIORITY, |
| &espi_xec_driver_api); |
| |
| /* |
| * Connect ESPI bus interrupt handlers: ESPI_RESET and channels. |
| * MEC172x hardware fixed SAF interrupt routing bug. SAF driver |
| * will connect its direct mode interrupt handler(s) on this GIRQ. |
| */ |
| static void espi_xec_connect_irqs(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| /* eSPI Reset */ |
| IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 7, irq), |
| DT_INST_IRQ_BY_IDX(0, 7, priority), |
| espi_rst_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQ_BY_IDX(0, 7, irq)); |
| |
| /* eSPI Virtual wire channel enable change ISR */ |
| IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 8, irq), |
| DT_INST_IRQ_BY_IDX(0, 8, priority), |
| espi_vw_chan_en_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQ_BY_IDX(0, 8, irq)); |
| |
| /* eSPI Peripheral Channel */ |
| IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 0, irq), |
| DT_INST_IRQ_BY_IDX(0, 0, priority), |
| espi_pc_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQ_BY_IDX(0, 0, irq)); |
| |
| #ifdef CONFIG_ESPI_OOB_CHANNEL |
| /* eSPI OOB Upstream direction */ |
| IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 4, irq), |
| DT_INST_IRQ_BY_IDX(0, 4, priority), |
| espi_oob_up_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQ_BY_IDX(0, 4, irq)); |
| |
| /* eSPI OOB Channel Downstream direction */ |
| IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 5, irq), |
| DT_INST_IRQ_BY_IDX(0, 5, priority), |
| espi_oob_down_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQ_BY_IDX(0, 5, irq)); |
| #endif |
| |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 6, irq), |
| DT_INST_IRQ_BY_IDX(0, 6, priority), |
| espi_flash_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQ_BY_IDX(0, 6, irq)); |
| #endif |
| } |
| |
| /* |
| * Initialize eSPI hardware and associated peripherals blocks using eSPI |
| * as their host interface. |
| * We change VW capabilities reported to match the number of VWires the |
| * driver is supporting. |
| * A VW packet on the bus contains VW count followed by the VW groups. |
| * The VW count is a zero based 6-bit value: (0 - 63) specifying the number of |
| * groups in the packet. |
| * A VW group consists of two bytes: VW host index and VW data. Each group |
| * contains the state of 4 virtual wires. |
| * The total supported virtual wires is 64 * 4 = 256. |
| * MEC172x supports 11 MSVW groups and 11 SMVW groups. |
| * NOTE: While ESPI_nRESET is active most of the eSPI hardware is held |
| * in reset state. |
| */ |
| static int espi_xec_init(const struct device *dev) |
| { |
| struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); |
| struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); |
| struct espi_xec_data *const data = ESPI_XEC_DATA(dev); |
| struct pcr_regs *pcr = XEC_PCR_REG_BASE; |
| int ret; |
| |
| ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); |
| if (ret != 0) { |
| LOG_ERR("XEC eSPI V2 pinctrl setup failed (%d)", ret); |
| return ret; |
| } |
| |
| data->plt_rst_asserted = 0; |
| #ifdef ESPI_XEC_V2_DEBUG |
| data->espi_rst_count = 0; |
| #endif |
| /* clear eSPI PCR sleep enable */ |
| z_mchp_xec_pcr_periph_sleep(cfg->pcr_idx, cfg->pcr_bitpos, 0); |
| |
| /* Configure eSPI_PLTRST# to cause nSIO_RESET reset |
| * NOTE: this is also clearing bit 0(PWR_INV) causing the internal |
| * RESET_VCC to de-assert. Host facing peripherals will no longer |
| * be held in reset. |
| */ |
| pcr->PWR_RST_CTRL = MCHP_PCR_PR_CTRL_USE_ESPI_PLTRST; |
| regs->PLTSRC = MCHP_ESPI_PLTRST_SRC_IS_VW; |
| |
| /* Configure the channels and its capabilities based on build config */ |
| regs->CAP0 |= MCHP_ESPI_GBL_CAP0_VW_SUPP | MCHP_ESPI_GBL_CAP0_PC_SUPP; |
| |
| regs->CAPVW = MAX(ESPI_NUM_MSVW, ESPI_NUM_SMVW); |
| regs->CAPPC |= MCHP_ESPI_PC_CAP_MAX_PLD_SZ_64; |
| |
| #ifdef CONFIG_ESPI_OOB_CHANNEL |
| regs->CAP0 |= MCHP_ESPI_GBL_CAP0_OOB_SUPP; |
| regs->CAPOOB |= 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 |
| regs->CAP0 &= ~MCHP_ESPI_GBL_CAP0_OOB_SUPP; |
| #endif |
| |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| regs->CAP0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP | |
| MCHP_ESPI_FC_CAP_MAX_PLD_SZ_64; |
| regs->CAPFC |= MCHP_ESPI_FC_CAP_SHARE_MAF_SAF | |
| MCHP_ESPI_FC_CAP_MAX_RD_SZ_64; |
| |
| k_sem_init(&data->flash_lock, 0, 1); |
| #else |
| regs->CAP0 &= ~MCHP_ESPI_GBL_CAP0_FC_SUPP; |
| #endif |
| |
| /* Clear reset interrupt status and enable interrupts */ |
| regs->ERIS = MCHP_ESPI_RST_ISTS; |
| regs->ERIE |= MCHP_ESPI_RST_IEN; |
| regs->PCSTS = MCHP_ESPI_PC_STS_EN_CHG; |
| regs->PCIEN |= MCHP_ESPI_PC_IEN_EN_CHG; |
| |
| /* register VWire handlers with their aggregated GIRQs |
| * in the ECIA driver |
| */ |
| for (int i = 0; i < ARRAY_SIZE(m2s_vwires_isr); i++) { |
| const struct espi_vw_isr *vwi = &m2s_vwires_isr[i]; |
| struct xec_signal signal_info = vw_tbl[vwi->signal]; |
| uint8_t xec_id = signal_info.xec_reg_idx; |
| |
| /* enables interrupt in eSPI MSVWn register */ |
| xec_espi_vw_intr_ctrl(dev, xec_id, signal_info.bit, |
| MSVW_IRQ_SEL_EDGE_BOTH); |
| |
| /* register handler */ |
| ret = mchp_xec_ecia_set_callback(vwi->girq_id, vwi->girq_pos, |
| vwi->the_isr, (void *)dev); |
| __ASSERT_NO_MSG(ret == 0); |
| |
| mchp_xec_ecia_girq_src_en(vwi->girq_id, vwi->girq_pos); |
| } |
| |
| /* Enable interrupts for each logical channel enable assertion */ |
| xec_espi_bus_intr_ctl(dev, pc_girq_idx, 1); |
| xec_espi_bus_intr_ctl(dev, vw_ch_en_girq_idx, 1); |
| xec_espi_bus_intr_ctl(dev, rst_girq_idx, 1); |
| |
| #ifdef CONFIG_ESPI_OOB_CHANNEL |
| espi_init_oob(dev); |
| #endif |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| espi_init_flash(dev); |
| #endif |
| |
| espi_xec_connect_irqs(dev); |
| |
| ret = xec_host_dev_connect_irqs(dev); |
| |
| return ret; |
| } |