| /* |
| * Copyright (c) 2019 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <soc.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/espi.h> |
| #include <zephyr/drivers/espi_saf.h> |
| #include <zephyr/drivers/spi.h> |
| #include <zephyr/logging/log_ctrl.h> |
| #include <zephyr/logging/log.h> |
| /* OOB operations will be attempted regardless of channel enabled or not */ |
| #include "espi_oob_handler.h" |
| LOG_MODULE_DECLARE(espi, CONFIG_ESPI_LOG_LEVEL); |
| |
| /* eSPI flash parameters */ |
| #define MAX_TEST_BUF_SIZE 1024u |
| #define MAX_FLASH_REQUEST 64u |
| #define TARGET_FLASH_REGION 0x72000ul |
| |
| #define ESPI_FREQ_20MHZ 20u |
| #define ESPI_FREQ_25MHZ 25u |
| #define ESPI_FREQ_66MHZ 66u |
| |
| #define K_WAIT_DELAY 100u |
| |
| /* eSPI event */ |
| #define EVENT_MASK 0x0000FFFFu |
| #define EVENT_DETAILS_MASK 0xFFFF0000u |
| #define EVENT_DETAILS_POS 16u |
| #define EVENT_TYPE(x) (x & EVENT_MASK) |
| #define EVENT_DETAILS(x) ((x & EVENT_DETAILS_MASK) >> EVENT_DETAILS_POS) |
| |
| #define PWR_SEQ_TIMEOUT 3000u |
| |
| /* The devicetree node identifier for the board power rails pins. */ |
| #define BRD_PWR_NODE DT_NODELABEL(board_power) |
| |
| #if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay) |
| static const struct gpio_dt_spec pwrgd_gpio = GPIO_DT_SPEC_GET(BRD_PWR_NODE, pwrg_gpios); |
| static const struct gpio_dt_spec rsm_gpio = GPIO_DT_SPEC_GET(BRD_PWR_NODE, rsm_gpios); |
| #endif |
| |
| static const struct device *const espi_dev = DEVICE_DT_GET(DT_NODELABEL(espi0)); |
| static struct espi_callback espi_bus_cb; |
| static struct espi_callback vw_rdy_cb; |
| static struct espi_callback vw_cb; |
| static struct espi_callback p80_cb; |
| static uint8_t espi_rst_sts; |
| #ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
| static struct espi_callback oob_cb; |
| #endif |
| |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| static uint8_t flash_write_buf[MAX_TEST_BUF_SIZE]; |
| static uint8_t flash_read_buf[MAX_TEST_BUF_SIZE]; |
| #endif |
| |
| #ifdef CONFIG_ESPI_SAF |
| #define SAF_BASE_ADDR DT_REG_ADDR(DT_NODELABEL(espi_saf0)) |
| |
| #define SAF_TEST_FREQ_HZ 24000000U |
| #define SAF_TEST_BUF_SIZE 4096U |
| |
| /* SPI address of 4KB sector modified by test */ |
| #define SAF_SPI_TEST_ADDRESS 0x1000U |
| |
| #define SPI_WRITE_STATUS1 0x01U |
| #define SPI_WRITE_STATUS2 0x31U |
| #define SPI_WRITE_DISABLE 0x04U |
| #define SPI_READ_STATUS1 0x05U |
| #define SPI_WRITE_ENABLE 0x06U |
| #define SPI_READ_STATUS2 0x35U |
| #define SPI_WRITE_ENABLE_VS 0x50U |
| #define SPI_READ_JEDEC_ID 0x9FU |
| |
| #define SPI_STATUS1_BUSY 0x80U |
| #define SPI_STATUS2_QE 0x02U |
| |
| #define W25Q128_JEDEC_ID 0x001840efU |
| |
| enum saf_erase_size { |
| SAF_ERASE_4K = 0, |
| SAF_ERASE_32K = 1, |
| SAF_ERASE_64K = 2, |
| SAF_ERASE_MAX |
| }; |
| |
| struct saf_addr_info { |
| uintptr_t saf_struct_addr; |
| uintptr_t saf_exp_addr; |
| }; |
| static const struct device *const qspi_dev = |
| DEVICE_DT_GET(DT_NODELABEL(spi0)); |
| static const struct device *const espi_saf_dev = |
| DEVICE_DT_GET(DT_NODELABEL(espi_saf0)); |
| static uint8_t safbuf[SAF_TEST_BUF_SIZE] __aligned(4); |
| static uint8_t safbuf2[SAF_TEST_BUF_SIZE] __aligned(4); |
| |
| /* |
| * W25Q128 SPI flash SAF configuration. |
| * Size is 16Mbytes, it requires no continuous mode prefix, or |
| * other special SAF configuration. |
| */ |
| static const struct espi_saf_flash_cfg flash_w25q128 = { |
| .flashsz = 0x1000000U, |
| .opa = MCHP_SAF_OPCODE_REG_VAL(0x06U, 0x75U, 0x7aU, 0x05U), |
| .opb = MCHP_SAF_OPCODE_REG_VAL(0x20U, 0x52U, 0xd8U, 0x02U), |
| .opc = MCHP_SAF_OPCODE_REG_VAL(0xebU, 0xffU, 0xa5U, 0x35U), |
| .poll2_mask = MCHP_W25Q128_POLL2_MASK, |
| .cont_prefix = 0U, |
| .cs_cfg_descr_ids = MCHP_CS0_CFG_DESCR_IDX_REG_VAL, |
| .flags = 0, |
| .descr = { |
| MCHP_W25Q128_CM_RD_D0, |
| MCHP_W25Q128_CM_RD_D1, |
| MCHP_W25Q128_CM_RD_D2, |
| MCHP_W25Q128_ENTER_CM_D0, |
| MCHP_W25Q128_ENTER_CM_D1, |
| MCHP_W25Q128_ENTER_CM_D2 |
| } |
| }; |
| |
| /* |
| * SAF driver configuration. |
| * One SPI flash device. |
| * Use QMSPI frequency, chip select timing, and signal sampling configured |
| * by QMSPI driver. |
| * Use SAF hardware default TAG map. |
| */ |
| #ifdef CONFIG_ESPI_SAF_XEC_V2 |
| static const struct espi_saf_cfg saf_cfg1 = { |
| .nflash_devices = 1U, |
| .hwcfg = { |
| .version = 2U, /* TODO */ |
| .flags = 0U, /* TODO */ |
| .qmspi_cpha = 0U, /* TODO */ |
| .qmspi_cs_timing = 0U, /* TODO */ |
| .flash_pd_timeout = 0U, /* TODO */ |
| .flash_pd_min_interval = 0U, /* TODO */ |
| .generic_descr = { |
| MCHP_SAF_EXIT_CM_DESCR12, MCHP_SAF_EXIT_CM_DESCR13, |
| MCHP_SAF_POLL_DESCR14, MCHP_SAF_POLL_DESCR15 |
| }, |
| .tag_map = { 0U, 0U, 0U } |
| }, |
| .flash_cfgs = (struct espi_saf_flash_cfg *)&flash_w25q128 |
| }; |
| #else |
| static const struct espi_saf_cfg saf_cfg1 = { |
| .nflash_devices = 1U, |
| .hwcfg = { |
| .qmspi_freq_hz = 0U, |
| .qmspi_cs_timing = 0U, |
| .qmspi_cpha = 0U, |
| .flags = 0U, |
| .generic_descr = { |
| MCHP_SAF_EXIT_CM_DESCR12, MCHP_SAF_EXIT_CM_DESCR13, |
| MCHP_SAF_POLL_DESCR14, MCHP_SAF_POLL_DESCR15 |
| }, |
| .tag_map = { 0U, 0U, 0U } |
| }, |
| .flash_cfgs = (struct espi_saf_flash_cfg *)&flash_w25q128 |
| }; |
| #endif |
| |
| /* |
| * Example for SAF driver set protection regions API. |
| */ |
| static const struct espi_saf_pr w25q128_protect_regions[2] = { |
| { |
| .start = 0xe00000U, |
| .size = 0x100000U, |
| .master_bm_we = (1U << MCHP_SAF_MSTR_HOST_PCH_ME), |
| .master_bm_rd = (1U << MCHP_SAF_MSTR_HOST_PCH_ME), |
| .pr_num = 1U, |
| .flags = MCHP_SAF_PR_FLAG_ENABLE |
| | MCHP_SAF_PR_FLAG_LOCK, |
| }, |
| { |
| .start = 0xf00000U, |
| .size = 0x100000U, |
| .master_bm_we = (1U << MCHP_SAF_MSTR_HOST_PCH_LAN), |
| .master_bm_rd = (1U << MCHP_SAF_MSTR_HOST_PCH_LAN), |
| .pr_num = 2U, |
| .flags = MCHP_SAF_PR_FLAG_ENABLE |
| | MCHP_SAF_PR_FLAG_LOCK, |
| }, |
| }; |
| |
| static const struct espi_saf_protection saf_pr_w25q128 = { |
| .nregions = 2U, |
| .pregions = w25q128_protect_regions |
| }; |
| |
| /* |
| * Initialize the local attached SPI flash. |
| * 1. Get SPI driver binding |
| * 2. Read JEDEC ID and verify its a W25Q128 |
| * 3. Read STATUS2 and check QE bit |
| * 4. If QE bit is not set |
| * Send volatile status write enable |
| * Set volatile QE bit |
| * Check STATUS1 BUSY, not expected to be set for volatile status write. |
| * Read STATUS2 and check QE |
| * Returns 0 if QE was already set or this routine successfully set volatile |
| * QE. Returns < 0 on SPI driver error or unexpected BUSY or STATUS values. |
| * NOTE: SPI driver transceive API will configure the SPI controller to the |
| * settings passed in the struct spi_config. We set the frequency to the |
| * frequency we will be using for SAF. |
| */ |
| int spi_saf_init(void) |
| { |
| struct spi_config spi_cfg; |
| struct spi_buf_set tx_bufs; |
| struct spi_buf_set rx_bufs; |
| struct spi_buf txb; |
| struct spi_buf rxb; |
| uint8_t spi_status1, spi_status2; |
| uint32_t jedec_id; |
| int ret; |
| |
| /* Read JEDEC ID command and fill read buffer */ |
| safbuf[0] = SPI_READ_JEDEC_ID; |
| memset(safbuf2, 0x55, 4U); |
| |
| spi_cfg.frequency = SAF_TEST_FREQ_HZ; |
| spi_cfg.operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB |
| | SPI_WORD_SET(8); |
| |
| /* |
| * Use SPI master mode and inform driver the SPI controller hardware |
| * controls chip select. |
| */ |
| jedec_id = 0U; |
| spi_cfg.slave = 0; |
| spi_cfg.cs.delay = 0; |
| spi_cfg.cs.gpio.pin = 0; |
| spi_cfg.cs.gpio.dt_flags = 0; |
| spi_cfg.cs.gpio.port = NULL; |
| |
| txb.buf = &safbuf; |
| txb.len = 1U; |
| |
| tx_bufs.buffers = (const struct spi_buf *)&txb; |
| tx_bufs.count = 1U; |
| |
| rxb.buf = &jedec_id; |
| rxb.len = 3U; |
| |
| rx_bufs.buffers = (const struct spi_buf *)&rxb; |
| rx_bufs.count = 1U; |
| |
| ret = spi_transceive(qspi_dev, (const struct spi_config *)&spi_cfg, |
| (const struct spi_buf_set *)&tx_bufs, |
| (const struct spi_buf_set *)&rx_bufs); |
| if (ret) { |
| LOG_ERR("Read JEDEC ID spi_transceive failure: error %d", ret); |
| return ret; |
| } |
| |
| if (jedec_id != W25Q128_JEDEC_ID) { |
| LOG_ERR("JEDIC ID does not match W25Q128 %0x", safbuf2[0]); |
| return -1; |
| } |
| |
| /* Read STATUS2 to get quad enable bit */ |
| safbuf[0] = SPI_READ_STATUS2; |
| memset(safbuf2, 0, 4U); |
| |
| txb.buf = &safbuf; |
| txb.len = 1U; |
| |
| tx_bufs.buffers = (const struct spi_buf *)&txb; |
| tx_bufs.count = 1U; |
| |
| rxb.buf = &safbuf2; |
| rxb.len = 1U; |
| |
| rx_bufs.buffers = (const struct spi_buf *)&rxb; |
| rx_bufs.count = 1U; |
| |
| ret = spi_transceive(qspi_dev, (const struct spi_config *)&spi_cfg, |
| (const struct spi_buf_set *)&tx_bufs, |
| (const struct spi_buf_set *)&rx_bufs); |
| if (ret) { |
| LOG_ERR("Read STATUS2 spi_transceive failure: error %d", ret); |
| return ret; |
| } |
| |
| spi_status2 = safbuf2[0]; |
| |
| /* |
| * If QE not set then write the volatile QE bit. |
| * SAF test requires SPI flash quad enabled so the WP#/HOLD# signals |
| * will act as IO2/IO3. We will write the volatile QE bit for less |
| * wear of the STATUS2 register |
| */ |
| if ((spi_status2 & SPI_STATUS2_QE) == 0U) { |
| safbuf[0] = SPI_WRITE_ENABLE_VS; |
| |
| txb.buf = &safbuf; |
| txb.len = 1U; |
| |
| tx_bufs.buffers = (const struct spi_buf *)&txb; |
| tx_bufs.count = 1U; |
| |
| rx_bufs.buffers = NULL; |
| rx_bufs.count = 0U; |
| |
| ret = spi_transceive(qspi_dev, |
| (const struct spi_config *)&spi_cfg, |
| (const struct spi_buf_set *)&tx_bufs, |
| (const struct spi_buf_set *)&rx_bufs); |
| if (ret) { |
| LOG_ERR("Send write enable volatile spi_transceive" |
| " failure: error %d", ret); |
| return ret; |
| } |
| |
| safbuf[0] = SPI_WRITE_STATUS2; |
| safbuf[1] = spi_status2 | SPI_STATUS2_QE; |
| |
| txb.buf = &safbuf; |
| txb.len = 2U; |
| |
| tx_bufs.buffers = (const struct spi_buf *)&txb; |
| tx_bufs.count = 1U; |
| |
| rx_bufs.buffers = NULL; |
| rx_bufs.count = 0U; |
| |
| ret = spi_transceive(qspi_dev, |
| (const struct spi_config *)&spi_cfg, |
| (const struct spi_buf_set *)&tx_bufs, |
| (const struct spi_buf_set *)&rx_bufs); |
| if (ret) { |
| LOG_ERR("Write SPI STATUS2 QE=1 spi_transceive" |
| " failure: error %d", ret); |
| return ret; |
| } |
| |
| /* Write to volatile status is fast, expect BUSY to be clear */ |
| safbuf[0] = SPI_READ_STATUS1; |
| memset(safbuf2, 0, 4U); |
| |
| txb.buf = &safbuf; |
| txb.len = 1U; |
| |
| tx_bufs.buffers = (const struct spi_buf *)&txb; |
| tx_bufs.count = 1U; |
| |
| rxb.buf = &safbuf2; |
| /* read 2 bytes both will be STATUS1 */ |
| rxb.len = 2U; |
| |
| rx_bufs.buffers = (const struct spi_buf *)&rxb; |
| rx_bufs.count = 1U; |
| |
| ret = spi_transceive(qspi_dev, |
| (const struct spi_config *)&spi_cfg, |
| (const struct spi_buf_set *)&tx_bufs, |
| (const struct spi_buf_set *)&rx_bufs); |
| if (ret) { |
| LOG_ERR("Read SPI STATUS1 spi_transceive" |
| " failure: error %d", ret); |
| return ret; |
| } |
| |
| spi_status1 = safbuf2[0]; |
| if (spi_status1 & SPI_STATUS1_BUSY) { |
| LOG_ERR("SPI BUSY set after write to volatile STATUS2:" |
| " STATUS1=0x%02X", spi_status1); |
| return ret; |
| } |
| |
| /* Read STATUS2 to make sure QE is set */ |
| safbuf[0] = SPI_READ_STATUS2; |
| memset(safbuf2, 0, 4U); |
| |
| txb.buf = &safbuf; |
| txb.len = 1U; |
| |
| tx_bufs.buffers = (const struct spi_buf *)&txb; |
| tx_bufs.count = 1U; |
| |
| rxb.buf = &safbuf2; |
| /* read 2 bytes both will be STATUS2 */ |
| rxb.len = 2U; |
| |
| rx_bufs.buffers = (const struct spi_buf *)&rxb; |
| rx_bufs.count = 1U; |
| |
| ret = spi_transceive(qspi_dev, |
| (const struct spi_config *)&spi_cfg, |
| (const struct spi_buf_set *)&tx_bufs, |
| (const struct spi_buf_set *)&rx_bufs); |
| if (ret) { |
| LOG_ERR("Read 2 of SPI STATUS2 spi_transceive" |
| " failure: error %d", ret); |
| return ret; |
| } |
| |
| spi_status2 = safbuf2[0]; |
| if (!(spi_status2 & SPI_STATUS2_QE)) { |
| LOG_ERR("Read back of SPI STATUS2 after setting " |
| "volatile QE bit shows QE not set: 0x%02X", |
| spi_status2); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int espi_saf_init(void) |
| { |
| int ret; |
| |
| ret = espi_saf_config(espi_saf_dev, (struct espi_saf_cfg *)&saf_cfg1); |
| if (ret) { |
| LOG_ERR("Failed to configure eSPI SAF error %d", ret); |
| } else { |
| LOG_INF("eSPI SAF configured successfully!"); |
| } |
| |
| ret = espi_saf_set_protection_regions(espi_saf_dev, |
| &saf_pr_w25q128); |
| if (ret) { |
| LOG_ERR("Failed to set SAF protection region(s) %d", ret); |
| } else { |
| LOG_INF("eSPI SAF protection regions(s) configured!"); |
| } |
| |
| return ret; |
| } |
| |
| static int pr_check_range(struct mchp_espi_saf *regs, |
| const struct espi_saf_pr *pr) |
| { |
| uint32_t limit; |
| |
| limit = pr->start + pr->size - 1U; |
| |
| /* registers b[19:0] = bits[31:12] (align on 4KB) */ |
| if (regs->SAF_PROT_RG[pr->pr_num].START != (pr->start >> 12)) { |
| return -1; |
| } |
| |
| if (regs->SAF_PROT_RG[pr->pr_num].LIMIT != (limit >> 12)) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int pr_check_enable(struct mchp_espi_saf *regs, |
| const struct espi_saf_pr *pr) |
| { |
| if (pr->flags & MCHP_SAF_PR_FLAG_ENABLE) { |
| if (regs->SAF_PROT_RG[pr->pr_num].LIMIT > |
| regs->SAF_PROT_RG[pr->pr_num].START) { |
| return 0; |
| } |
| } else { |
| if (regs->SAF_PROT_RG[pr->pr_num].START > |
| regs->SAF_PROT_RG[pr->pr_num].LIMIT) { |
| return 0; |
| } |
| } |
| |
| return -2; |
| } |
| |
| static int pr_check_lock(struct mchp_espi_saf *regs, |
| const struct espi_saf_pr *pr) |
| { |
| if (pr->flags & MCHP_SAF_PR_FLAG_LOCK) { |
| if (regs->SAF_PROT_LOCK & BIT(pr->pr_num)) { |
| return 0; |
| } |
| } else { |
| if (!(regs->SAF_PROT_LOCK & BIT(pr->pr_num))) { |
| return 0; |
| } |
| } |
| |
| return -3; |
| } |
| |
| /* |
| * NOTE: bit[0] of bit map registers is read-only = 1 |
| */ |
| static int pr_check_master_bm(struct mchp_espi_saf *regs, |
| const struct espi_saf_pr *pr) |
| { |
| if (regs->SAF_PROT_RG[pr->pr_num].WEBM != |
| (pr->master_bm_we | BIT(0))) { |
| return -4; |
| } |
| |
| if (regs->SAF_PROT_RG[pr->pr_num].RDBM != |
| (pr->master_bm_rd | BIT(0))) { |
| return -4; |
| } |
| |
| return 0; |
| } |
| |
| static int espi_saf_test_pr1(const struct espi_saf_protection *spr) |
| { |
| struct mchp_espi_saf *saf_regs; |
| const struct espi_saf_pr *pr; |
| int rc; |
| |
| LOG_INF("espi_saf_test_pr1"); |
| |
| if (spr == NULL) { |
| return 0; |
| } |
| |
| saf_regs = (struct mchp_espi_saf *)(SAF_BASE_ADDR); |
| pr = spr->pregions; |
| |
| for (size_t n = 0U; n < spr->nregions; n++) { |
| rc = pr_check_range(saf_regs, pr); |
| if (rc) { |
| LOG_INF("SAF Protection region %u range fail", |
| pr->pr_num); |
| return rc; |
| } |
| |
| rc = pr_check_enable(saf_regs, pr); |
| if (rc) { |
| LOG_INF("SAF Protection region %u enable fail", |
| pr->pr_num); |
| return rc; |
| } |
| |
| rc = pr_check_lock(saf_regs, pr); |
| if (rc) { |
| LOG_INF("SAF Protection region %u lock check fail", |
| pr->pr_num); |
| return rc; |
| } |
| |
| rc = pr_check_master_bm(saf_regs, pr); |
| if (rc) { |
| LOG_INF("SAF Protection region %u Master select fail", |
| pr->pr_num); |
| return rc; |
| } |
| |
| pr++; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * SAF hardware limited to 1 to 64 byte read requests. |
| */ |
| static int saf_read(uint32_t spi_addr, uint8_t *dest, int len) |
| { |
| int rc, chunk_len, n; |
| struct espi_saf_packet saf_pkt = { 0 }; |
| |
| if ((dest == NULL) || (len < 0)) { |
| return -EINVAL; |
| } |
| |
| saf_pkt.flash_addr = spi_addr; |
| saf_pkt.buf = dest; |
| |
| n = len; |
| while (n) { |
| chunk_len = 64; |
| if (n < 64) { |
| chunk_len = n; |
| } |
| |
| saf_pkt.len = chunk_len; |
| |
| rc = espi_saf_flash_read(espi_saf_dev, &saf_pkt); |
| if (rc != 0) { |
| LOG_INF("%s: error = %d: chunk_len = %d " |
| "spi_addr = %x", __func__, rc, chunk_len, |
| spi_addr); |
| return rc; |
| } |
| |
| saf_pkt.flash_addr += chunk_len; |
| saf_pkt.buf += chunk_len; |
| n -= chunk_len; |
| } |
| |
| return len; |
| } |
| |
| /* |
| * SAF hardware limited to 4KB(mandatory), 32KB, and 64KB erase sizes. |
| * eSPI configuration has flags the Host can read specifying supported |
| * erase sizes. |
| */ |
| static int saf_erase_block(uint32_t spi_addr, enum saf_erase_size ersz) |
| { |
| int rc; |
| struct espi_saf_packet saf_pkt = { 0 }; |
| |
| switch (ersz) { |
| case SAF_ERASE_4K: |
| saf_pkt.len = 4096U; |
| spi_addr &= ~(4096U - 1U); |
| break; |
| case SAF_ERASE_32K: |
| saf_pkt.len = (32U * 1024U); |
| spi_addr &= ~((32U * 1024U) - 1U); |
| break; |
| case SAF_ERASE_64K: |
| saf_pkt.len = (64U * 1024U); |
| spi_addr &= ~((64U * 1024U) - 1U); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| saf_pkt.flash_addr = spi_addr; |
| |
| rc = espi_saf_flash_erase(espi_saf_dev, &saf_pkt); |
| if (rc != 0) { |
| LOG_INF("espi_saf_test1: erase fail = %d", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * SAF hardware limited to 1 to 64 byte programming within a 256 byte page. |
| */ |
| static int saf_page_prog(uint32_t spi_addr, const uint8_t *src, int progsz) |
| { |
| int rc, chunk_len, n; |
| struct espi_saf_packet saf_pkt = { 0 }; |
| |
| if ((src == NULL) || (progsz < 0) || (progsz > 256)) { |
| return -EINVAL; |
| } |
| |
| if (progsz == 0) { |
| return 0; |
| } |
| |
| saf_pkt.flash_addr = spi_addr; |
| saf_pkt.buf = (uint8_t *)src; |
| |
| n = progsz; |
| while (n) { |
| chunk_len = 64; |
| if (n < 64) { |
| chunk_len = n; |
| } |
| |
| saf_pkt.len = (uint32_t)chunk_len; |
| |
| rc = espi_saf_flash_write(espi_saf_dev, &saf_pkt); |
| if (rc != 0) { |
| LOG_INF("%s: error = %d: erase fail spi_addr = 0x%X", |
| __func__, rc, spi_addr); |
| return rc; |
| } |
| |
| saf_pkt.flash_addr += chunk_len; |
| saf_pkt.buf += chunk_len; |
| n -= chunk_len; |
| } |
| |
| return progsz; |
| } |
| |
| |
| int espi_saf_test1(uint32_t spi_addr) |
| { |
| int rc, retries; |
| bool erased; |
| uint32_t n, saddr, progsz, chunksz; |
| |
| rc = espi_saf_activate(espi_saf_dev); |
| LOG_INF("%s: activate = %d", __func__, rc); |
| |
| if (spi_addr & 0xfffU) { |
| LOG_INF("%s: SPI address 0x%08x not 4KB aligned", __func__, |
| spi_addr); |
| spi_addr &= ~(4096U-1U); |
| LOG_INF("%s: Aligned SPI address to 0x%08x", __func__, |
| spi_addr); |
| } |
| |
| memset(safbuf, 0x55, sizeof(safbuf)); |
| memset(safbuf2, 0, sizeof(safbuf2)); |
| |
| erased = false; |
| retries = 3; |
| while (!erased && (retries-- > 0)) { |
| /* read 4KB sector at 0 */ |
| rc = saf_read(spi_addr, safbuf, 4096); |
| if (rc != 4096) { |
| LOG_INF("%s: error=%d Read 4K sector at 0x%X failed", |
| __func__, rc, spi_addr); |
| return rc; |
| } |
| |
| rc = 0; |
| for (n = 0; n < 4096U; n++) { |
| if (safbuf[n] != 0xffUL) { |
| rc = -1; |
| break; |
| } |
| } |
| |
| if (rc == 0) { |
| LOG_INF("4KB sector at 0x%x is in erased state. " |
| "Continue tests", spi_addr); |
| erased = true; |
| } else { |
| LOG_INF("4KB sector at 0x%x not in erased state. " |
| "Send 4K erase.", spi_addr); |
| rc = saf_erase_block(spi_addr, SAF_ERASE_4K); |
| if (rc != 0) { |
| LOG_INF("SAF erase block at 0x%x returned " |
| "error %d", spi_addr, rc); |
| return rc; |
| } |
| } |
| } |
| |
| if (!erased) { |
| LOG_INF("%s: Could not erase 4KB sector at 0x%08x", |
| __func__, spi_addr); |
| return -1; |
| } |
| |
| /* |
| * Page program test pattern every 256 bytes = 0,1,...,255 |
| */ |
| for (n = 0; n < 4096U; n++) { |
| safbuf[n] = n % 256U; |
| } |
| |
| /* SPI flash sector erase size is 4KB, page program is 256 bytes */ |
| progsz = 4096U; |
| chunksz = 256U; |
| saddr = spi_addr; |
| n = 0; |
| const uint8_t *src = (const uint8_t *)safbuf; |
| |
| LOG_INF("%s: Program 4KB sector at 0x%X", __func__, saddr); |
| |
| while (n < progsz) { |
| rc = saf_page_prog(saddr, (const uint8_t *)src, |
| (int)chunksz); |
| if (rc != chunksz) { |
| LOG_INF("saf_page_prog error=%d at 0x%X", rc, |
| saddr); |
| break; |
| } |
| saddr += chunksz; |
| n += chunksz; |
| src += chunksz; |
| } |
| |
| /* read back and check */ |
| LOG_INF("%s: Read back 4K sector at 0x%X", __func__, spi_addr); |
| |
| rc = saf_read(spi_addr, safbuf2, progsz); |
| if (rc == progsz) { |
| rc = memcmp(safbuf, safbuf2, progsz); |
| if (rc == 0) { |
| LOG_INF("%s: Read back match: PASS", __func__); |
| } else { |
| LOG_INF("%s: Read back mismatch: FAIL", __func__); |
| } |
| } else { |
| LOG_INF("%s: Read back 4K error=%d", __func__, rc); |
| return rc; |
| } |
| |
| return rc; |
| } |
| #endif /* CONFIG_ESPI_SAF */ |
| |
| static void host_warn_handler(uint32_t signal, uint32_t status) |
| { |
| switch (signal) { |
| case ESPI_VWIRE_SIGNAL_HOST_RST_WARN: |
| LOG_INF("Host reset warning %d", status); |
| if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) { |
| LOG_INF("HOST RST ACK %d", status); |
| espi_send_vwire(espi_dev, |
| ESPI_VWIRE_SIGNAL_HOST_RST_ACK, |
| status); |
| } |
| break; |
| case ESPI_VWIRE_SIGNAL_SUS_WARN: |
| LOG_INF("Host suspend warning %d", status); |
| if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) { |
| LOG_INF("SUS ACK %d", status); |
| espi_send_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SUS_ACK, |
| status); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* eSPI bus event handler */ |
| static void espi_reset_handler(const struct device *dev, |
| struct espi_callback *cb, |
| struct espi_event event) |
| { |
| if (event.evt_type == ESPI_BUS_RESET) { |
| espi_rst_sts = event.evt_data; |
| LOG_INF("eSPI BUS reset %d", event.evt_data); |
| } |
| } |
| |
| /* eSPI logical channels enable/disable event handler */ |
| static void espi_ch_handler(const struct device *dev, |
| struct espi_callback *cb, |
| struct espi_event event) |
| { |
| if (event.evt_type == ESPI_BUS_EVENT_CHANNEL_READY) { |
| switch (event.evt_details) { |
| case ESPI_CHANNEL_VWIRE: |
| LOG_INF("VW channel event %x", event.evt_data); |
| break; |
| case ESPI_CHANNEL_FLASH: |
| LOG_INF("Flash channel event %d", event.evt_data); |
| break; |
| case ESPI_CHANNEL_OOB: |
| LOG_INF("OOB channel event %d", event.evt_data); |
| break; |
| default: |
| LOG_ERR("Unknown channel event"); |
| } |
| } |
| } |
| |
| /* eSPI vwire received event handler */ |
| static void vwire_handler(const struct device *dev, struct espi_callback *cb, |
| struct espi_event event) |
| { |
| if (event.evt_type == ESPI_BUS_EVENT_VWIRE_RECEIVED) { |
| switch (event.evt_details) { |
| case ESPI_VWIRE_SIGNAL_PLTRST: |
| LOG_INF("PLT_RST changed %d", event.evt_data); |
| break; |
| case ESPI_VWIRE_SIGNAL_SLP_S3: |
| case ESPI_VWIRE_SIGNAL_SLP_S4: |
| case ESPI_VWIRE_SIGNAL_SLP_S5: |
| LOG_INF("SLP signal changed %d", event.evt_data); |
| break; |
| case ESPI_VWIRE_SIGNAL_SUS_WARN: |
| case ESPI_VWIRE_SIGNAL_HOST_RST_WARN: |
| host_warn_handler(event.evt_details, |
| event.evt_data); |
| break; |
| } |
| } |
| } |
| |
| /* eSPI peripheral channel notifications handler */ |
| static void periph_handler(const struct device *dev, struct espi_callback *cb, |
| struct espi_event event) |
| { |
| uint8_t periph_type; |
| uint8_t periph_index; |
| |
| periph_type = EVENT_TYPE(event.evt_details); |
| periph_index = EVENT_DETAILS(event.evt_details); |
| |
| switch (periph_type) { |
| case ESPI_PERIPHERAL_DEBUG_PORT80: |
| LOG_INF("Postcode %x", event.evt_data); |
| break; |
| case ESPI_PERIPHERAL_HOST_IO: |
| LOG_INF("ACPI %x", event.evt_data); |
| espi_remove_callback(espi_dev, &p80_cb); |
| break; |
| default: |
| LOG_INF("%s periph 0x%x [%x]", __func__, periph_type, |
| event.evt_data); |
| } |
| } |
| |
| int espi_init(void) |
| { |
| int ret; |
| /* Indicate to eSPI master simplest configuration: Single line, |
| * 20MHz frequency and only logical channel 0 and 1 are supported |
| */ |
| struct espi_cfg cfg = { |
| .io_caps = ESPI_IO_MODE_SINGLE_LINE, |
| .channel_caps = ESPI_CHANNEL_VWIRE | ESPI_CHANNEL_PERIPHERAL, |
| .max_freq = ESPI_FREQ_20MHZ, |
| }; |
| |
| /* If eSPI driver supports additional capabilities use them */ |
| #ifdef CONFIG_ESPI_OOB_CHANNEL |
| cfg.channel_caps |= ESPI_CHANNEL_OOB; |
| #endif |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| cfg.channel_caps |= ESPI_CHANNEL_FLASH; |
| cfg.io_caps |= ESPI_IO_MODE_QUAD_LINES; |
| cfg.max_freq = ESPI_FREQ_25MHZ; |
| #endif |
| |
| ret = espi_config(espi_dev, &cfg); |
| if (ret) { |
| LOG_ERR("Failed to configure eSPI slave channels:%x err: %d", |
| cfg.channel_caps, ret); |
| return ret; |
| } else { |
| LOG_INF("eSPI slave configured successfully!"); |
| } |
| |
| LOG_INF("eSPI test - callbacks initialization... "); |
| espi_init_callback(&espi_bus_cb, espi_reset_handler, ESPI_BUS_RESET); |
| espi_init_callback(&vw_rdy_cb, espi_ch_handler, |
| ESPI_BUS_EVENT_CHANNEL_READY); |
| espi_init_callback(&vw_cb, vwire_handler, |
| ESPI_BUS_EVENT_VWIRE_RECEIVED); |
| espi_init_callback(&p80_cb, periph_handler, |
| ESPI_BUS_PERIPHERAL_NOTIFICATION); |
| #ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
| espi_init_callback(&oob_cb, oob_rx_handler, |
| ESPI_BUS_EVENT_OOB_RECEIVED); |
| #endif |
| LOG_INF("complete"); |
| |
| LOG_INF("eSPI test - callbacks registration... "); |
| espi_add_callback(espi_dev, &espi_bus_cb); |
| espi_add_callback(espi_dev, &vw_rdy_cb); |
| espi_add_callback(espi_dev, &vw_cb); |
| espi_add_callback(espi_dev, &p80_cb); |
| #ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
| espi_add_callback(espi_dev, &oob_cb); |
| #endif |
| LOG_INF("complete"); |
| |
| return ret; |
| } |
| |
| #if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay) |
| static int wait_for_pin(const struct gpio_dt_spec *gpio, uint16_t timeout, int exp_level) |
| { |
| uint16_t loop_cnt = timeout; |
| int level; |
| |
| do { |
| level = gpio_pin_get_dt(gpio); |
| if (level < 0) { |
| LOG_ERR("Failed to read %x %d", gpio->pin, level); |
| return -EIO; |
| } |
| |
| if (exp_level == level) { |
| LOG_DBG("PIN %x = %x", gpio->pin, exp_level); |
| break; |
| } |
| |
| k_usleep(K_WAIT_DELAY); |
| loop_cnt--; |
| } while (loop_cnt > 0); |
| |
| if (loop_cnt == 0) { |
| LOG_ERR("Timeout for %x %x", gpio->pin, level); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int wait_for_vwire(const struct device *espi_dev, |
| enum espi_vwire_signal signal, |
| uint16_t timeout, uint8_t exp_level) |
| { |
| int ret; |
| uint8_t level; |
| uint16_t loop_cnt = timeout; |
| |
| do { |
| ret = espi_receive_vwire(espi_dev, signal, &level); |
| if (ret) { |
| LOG_ERR("Failed to read %x %d", signal, ret); |
| return -EIO; |
| } |
| |
| if (exp_level == level) { |
| break; |
| } |
| |
| k_usleep(K_WAIT_DELAY); |
| loop_cnt--; |
| } while (loop_cnt > 0); |
| |
| if (loop_cnt == 0) { |
| LOG_ERR("VWIRE %d is %x", signal, level); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int wait_for_espi_reset(uint8_t exp_sts) |
| { |
| uint16_t loop_cnt = CONFIG_ESPI_VIRTUAL_WIRE_TIMEOUT; |
| |
| do { |
| if (exp_sts == espi_rst_sts) { |
| break; |
| } |
| k_usleep(K_WAIT_DELAY); |
| loop_cnt--; |
| } while (loop_cnt > 0); |
| |
| if (loop_cnt == 0) { |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| int espi_handshake(void) |
| { |
| int ret; |
| |
| LOG_INF("eSPI test - Handshake with eSPI master..."); |
| ret = wait_for_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SUS_WARN, |
| CONFIG_ESPI_VIRTUAL_WIRE_TIMEOUT, 1); |
| if (ret) { |
| LOG_ERR("SUS_WARN Timeout"); |
| return ret; |
| } |
| |
| LOG_INF("1st phase completed"); |
| ret = wait_for_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SLP_S5, |
| CONFIG_ESPI_VIRTUAL_WIRE_TIMEOUT, 1); |
| if (ret) { |
| LOG_ERR("SLP_S5 Timeout"); |
| return ret; |
| } |
| |
| ret = wait_for_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SLP_S4, |
| CONFIG_ESPI_VIRTUAL_WIRE_TIMEOUT, 1); |
| if (ret) { |
| LOG_ERR("SLP_S4 Timeout"); |
| return ret; |
| } |
| |
| ret = wait_for_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SLP_S3, |
| CONFIG_ESPI_VIRTUAL_WIRE_TIMEOUT, 1); |
| if (ret) { |
| LOG_ERR("SLP_S3 Timeout"); |
| return ret; |
| } |
| |
| LOG_INF("2nd phase completed"); |
| |
| ret = wait_for_vwire(espi_dev, ESPI_VWIRE_SIGNAL_PLTRST, |
| CONFIG_ESPI_VIRTUAL_WIRE_TIMEOUT, 1); |
| if (ret) { |
| LOG_ERR("PLT_RST Timeout"); |
| return ret; |
| } |
| |
| LOG_INF("3rd phase completed"); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| int read_test_block(uint8_t *buf, uint32_t start_flash_adr, uint16_t block_len) |
| { |
| uint8_t i = 0; |
| uint32_t flash_addr = start_flash_adr; |
| uint16_t transactions = block_len/MAX_FLASH_REQUEST; |
| int ret = 0; |
| struct espi_flash_packet pckt; |
| |
| for (i = 0; i < transactions; i++) { |
| pckt.buf = buf; |
| pckt.flash_addr = flash_addr; |
| pckt.len = MAX_FLASH_REQUEST; |
| |
| ret = espi_read_flash(espi_dev, &pckt); |
| if (ret) { |
| LOG_ERR("espi_read_flash failed: %d", ret); |
| return ret; |
| } |
| |
| buf += MAX_FLASH_REQUEST; |
| flash_addr += MAX_FLASH_REQUEST; |
| } |
| |
| LOG_INF("%d read flash transactions completed", transactions); |
| return 0; |
| } |
| |
| int write_test_block(uint8_t *buf, uint32_t start_flash_adr, uint16_t block_len) |
| { |
| uint8_t i = 0; |
| uint32_t flash_addr = start_flash_adr; |
| uint16_t transactions = block_len/MAX_FLASH_REQUEST; |
| int ret = 0; |
| struct espi_flash_packet pckt; |
| |
| /* Split operation in multiple MAX_FLASH_REQ transactions */ |
| for (i = 0; i < transactions; i++) { |
| pckt.buf = buf; |
| pckt.flash_addr = flash_addr; |
| pckt.len = MAX_FLASH_REQUEST; |
| |
| ret = espi_write_flash(espi_dev, &pckt); |
| if (ret) { |
| LOG_ERR("espi_write_flash failed: %d", ret); |
| return ret; |
| } |
| |
| buf += MAX_FLASH_REQUEST; |
| flash_addr += MAX_FLASH_REQUEST; |
| } |
| |
| LOG_INF("%d write flash transactions completed", transactions); |
| return 0; |
| } |
| |
| static int espi_flash_test(uint32_t start_flash_addr, uint8_t blocks) |
| { |
| uint8_t i; |
| uint8_t pattern; |
| uint32_t flash_addr; |
| int ret = 0; |
| |
| LOG_INF("Test eSPI write flash"); |
| flash_addr = start_flash_addr; |
| pattern = 0x99; |
| for (i = 0; i <= blocks; i++) { |
| memset(flash_write_buf, pattern++, sizeof(flash_write_buf)); |
| ret = write_test_block(flash_write_buf, flash_addr, |
| sizeof(flash_write_buf)); |
| if (ret) { |
| LOG_ERR("Failed to write to eSPI"); |
| return ret; |
| } |
| |
| flash_addr += sizeof(flash_write_buf); |
| } |
| |
| LOG_INF("Test eSPI read flash"); |
| flash_addr = start_flash_addr; |
| pattern = 0x99; |
| for (i = 0; i <= blocks; i++) { |
| /* Set expected content */ |
| memset(flash_write_buf, pattern, sizeof(flash_write_buf)); |
| /* Clear last read content */ |
| memset(flash_read_buf, 0, sizeof(flash_read_buf)); |
| ret = read_test_block(flash_read_buf, flash_addr, |
| sizeof(flash_read_buf)); |
| if (ret) { |
| LOG_ERR("Failed to read from eSPI"); |
| return ret; |
| } |
| |
| /* Compare buffers */ |
| int cmp = memcmp(flash_write_buf, flash_read_buf, |
| sizeof(flash_write_buf)); |
| |
| if (cmp != 0) { |
| LOG_ERR("eSPI read mismmatch at %d expected %x", |
| cmp, pattern); |
| } |
| |
| flash_addr += sizeof(flash_read_buf); |
| pattern++; |
| } |
| |
| return 0; |
| } |
| #endif /* CONFIG_ESPI_FLASH_CHANNEL */ |
| |
| #ifndef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
| static void send_slave_bootdone(void) |
| { |
| int ret; |
| uint8_t boot_done; |
| |
| ret = espi_receive_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, |
| &boot_done); |
| LOG_INF("%s boot_done: %d", __func__, boot_done); |
| if (ret) { |
| LOG_WRN("Fail to retrieve slave boot done"); |
| } else if (!boot_done) { |
| /* SLAVE_BOOT_DONE & SLAVE_LOAD_STS have to be sent together */ |
| espi_send_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_STS, 1); |
| espi_send_vwire(espi_dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, 1); |
| } |
| } |
| #endif |
| |
| int espi_test(void) |
| { |
| int ret; |
| |
| /* Account for the time serial port is detected so log messages can |
| * be seen |
| */ |
| k_sleep(K_SECONDS(1)); |
| |
| #if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay) |
| if (!device_is_ready(pwrgd_gpio.port)) { |
| LOG_ERR("%s: device not ready.", pwrgd_gpio.port->name); |
| return -ENODEV; |
| } |
| if (!device_is_ready(rsm_gpio.port)) { |
| LOG_ERR("%s: device not ready.", rsm_gpio.port->name); |
| return -ENODEV; |
| } |
| #endif |
| if (!device_is_ready(espi_dev)) { |
| LOG_ERR("%s: device not ready.", espi_dev->name); |
| return -ENODEV; |
| } |
| |
| #ifdef CONFIG_ESPI_SAF |
| if (!device_is_ready(qspi_dev)) { |
| LOG_ERR("%s: device not ready.", qspi_dev->name); |
| return -ENODEV; |
| } |
| |
| if (!device_is_ready(espi_saf_dev)) { |
| LOG_ERR("%s: device not ready.", espi_saf_dev->name); |
| return -ENODEV; |
| } |
| #endif |
| |
| LOG_INF("Hello eSPI test %s", CONFIG_BOARD); |
| |
| #if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay) |
| ret = gpio_pin_configure_dt(&pwrgd_gpio, GPIO_INPUT); |
| if (ret) { |
| LOG_ERR("Unable to configure %d:%d", pwrgd_gpio.pin, ret); |
| return ret; |
| } |
| |
| ret = gpio_pin_configure_dt(&rsm_gpio, GPIO_OUTPUT); |
| if (ret) { |
| LOG_ERR("Unable to config %d: %d", rsm_gpio.pin, ret); |
| return ret; |
| } |
| |
| ret = gpio_pin_set_dt(&rsm_gpio, 0); |
| if (ret) { |
| LOG_ERR("Unable to initialize %d", rsm_gpio.pin); |
| return -1; |
| } |
| #endif |
| |
| espi_init(); |
| |
| #ifdef CONFIG_ESPI_SAF |
| /* |
| * eSPI SAF configuration must be after eSPI configuration. |
| * eSPI SAF EC portal flash tests before EC releases RSMRST# and |
| * Host de-asserts ESPI_RESET#. |
| */ |
| ret = spi_saf_init(); |
| if (ret) { |
| LOG_ERR("Unable to configure %d:%s", ret, qspi_dev->name); |
| return ret; |
| } |
| |
| ret = espi_saf_init(); |
| if (ret) { |
| LOG_ERR("Unable to configure %d:%s", ret, espi_saf_dev->name); |
| return ret; |
| } |
| |
| |
| ret = espi_saf_test_pr1(&saf_pr_w25q128); |
| if (ret) { |
| LOG_INF("eSPI SAF test pr1 returned error %d", ret); |
| } |
| |
| ret = espi_saf_test1(SAF_SPI_TEST_ADDRESS); |
| if (ret) { |
| LOG_INF("eSPI SAF test1 returned error %d", ret); |
| } |
| #endif |
| |
| #if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay) |
| ret = wait_for_pin(&pwrgd_gpio, PWR_SEQ_TIMEOUT, 1); |
| if (ret) { |
| LOG_ERR("RSMRST_PWRGD timeout"); |
| return ret; |
| } |
| |
| ret = gpio_pin_set_dt(&rsm_gpio, 1); |
| if (ret) { |
| LOG_ERR("Failed to set rsm err: %d", ret); |
| return ret; |
| } |
| #endif |
| ret = wait_for_espi_reset(1); |
| if (ret) { |
| LOG_INF("ESPI_RESET timeout"); |
| return ret; |
| } |
| |
| #ifndef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
| /* When automatic acknowledge is disabled to perform lengthy operations |
| * in the eSPI slave, need to explicitly send slave boot |
| */ |
| bool vw_ch_sts; |
| |
| /* Simulate lengthy operation during boot */ |
| k_sleep(K_SECONDS(2)); |
| |
| do { |
| vw_ch_sts = espi_get_channel_status(espi_dev, |
| ESPI_CHANNEL_VWIRE); |
| k_busy_wait(100); |
| } while (!vw_ch_sts); |
| |
| |
| send_slave_bootdone(); |
| #endif |
| |
| |
| #ifdef CONFIG_ESPI_FLASH_CHANNEL |
| /* Flash operation need to be perform before VW handshake or |
| * after eSPI host completes full initialization. |
| * This sample code can't assume a full initialized eSPI host |
| * so flash operations are perform here. |
| */ |
| bool flash_sts; |
| |
| do { |
| flash_sts = espi_get_channel_status(espi_dev, |
| ESPI_CHANNEL_FLASH); |
| k_busy_wait(100); |
| } while (!flash_sts); |
| |
| /* eSPI flash test can fail and rest of operation can continue */ |
| ret = espi_flash_test(TARGET_FLASH_REGION, 1); |
| if (ret) { |
| LOG_INF("eSPI flash test failed %d", ret); |
| } |
| #endif |
| |
| /* Showcase VW channel by exchanging virtual wires with eSPI host */ |
| ret = espi_handshake(); |
| if (ret) { |
| LOG_ERR("eSPI VW handshake failed %d", ret); |
| return ret; |
| } |
| |
| /* Attempt to use OOB channel to read temperature, regardless of |
| * if is enabled or not. |
| */ |
| #ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
| /* System without host-initiated OOB Rx traffic */ |
| get_pch_temp_sync(espi_dev); |
| #else |
| /* System with host-initiated OOB Rx traffic */ |
| get_pch_temp_async(espi_dev); |
| #endif |
| |
| /* Cleanup */ |
| k_sleep(K_SECONDS(1)); |
| espi_remove_callback(espi_dev, &espi_bus_cb); |
| espi_remove_callback(espi_dev, &vw_rdy_cb); |
| espi_remove_callback(espi_dev, &vw_cb); |
| |
| LOG_INF("eSPI sample completed err: %d", ret); |
| |
| return ret; |
| } |
| |
| int main(void) |
| { |
| espi_test(); |
| return 0; |
| } |