blob: e5f8fc80886e905dc06366ef355122e619adcb8f [file] [log] [blame]
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <zephyr.h>
#include <device.h>
#include <soc.h>
#include <drivers/gpio.h>
#include <drivers/espi.h>
#include <logging/log_ctrl.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(espi, CONFIG_ESPI_LOG_LEVEL);
/* eSPI host entity address */
#define DEST_SLV_ADDR 0x02u
#define SRC_SLV_ADDR 0x21u
/* Temperature command opcode */
#define OOB_CMDCODE 0x01u
#define OOB_RESPONSE_LEN 0x05u
#define OOB_RESPONSE_INDEX 0x03u
/* Maximum bytes for OOB transactions */
#define MAX_RESP_SIZE 20u
/* 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)
struct oob_header {
uint8_t dest_slave_addr;
uint8_t oob_cmd_code;
uint8_t byte_cnt;
uint8_t src_slave_addr;
};
#define PWR_SEQ_TIMEOUT 3000u
/* The devicetree node identifier for the board power rails pins. */
#define BRD_PWR_NODE DT_INST(0, microchip_mec15xx_board_power)
#if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay)
#define BRD_PWR_PWRGD DT_GPIO_LABEL(BRD_PWR_NODE, pwrg_gpios)
#define BRD_PWR_RSMRST DT_GPIO_LABEL(BRD_PWR_NODE, rsm_gpios)
#define BRD_PWR_RSMRST_PIN DT_GPIO_PIN(BRD_PWR_NODE, rsm_gpios)
#define BRD_PWR_PWRGD_PIN DT_GPIO_PIN(BRD_PWR_NODE, pwrg_gpios)
static const struct device *pwrgd_dev;
static const struct device *rsm_dev;
#endif
#define ESPI_DEV DT_LABEL(DT_NODELABEL(espi0))
static const struct device *espi_dev;
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_FLASH_CHANNEL
static uint8_t flash_write_buf[MAX_TEST_BUF_SIZE];
static uint8_t flash_read_buf[MAX_TEST_BUF_SIZE];
#endif
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);
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);
LOG_INF("complete");
return ret;
}
#if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay)
static int wait_for_pin(const struct device *dev, uint8_t pin,
uint16_t timeout,
int exp_level)
{
uint16_t loop_cnt = timeout;
int level;
do {
level = gpio_pin_get(dev, pin);
if (level < 0) {
LOG_ERR("Failed to read %x %d", pin, level);
return -EIO;
}
if (exp_level == level) {
LOG_DBG("PIN %x = %x", 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", 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 */
int get_pch_temp(const struct device *dev, int *temp)
{
struct espi_oob_packet req_pckt;
struct espi_oob_packet resp_pckt;
struct oob_header oob_hdr;
uint8_t buf[MAX_RESP_SIZE];
int ret;
LOG_INF("%s", __func__);
oob_hdr.dest_slave_addr = DEST_SLV_ADDR;
oob_hdr.oob_cmd_code = OOB_CMDCODE;
oob_hdr.byte_cnt = 1;
oob_hdr.src_slave_addr = SRC_SLV_ADDR;
/* Packetize OOB request */
req_pckt.buf = (uint8_t *)&oob_hdr;
req_pckt.len = sizeof(struct oob_header);
resp_pckt.buf = (uint8_t *)&buf;
resp_pckt.len = MAX_RESP_SIZE;
ret = espi_send_oob(dev, &req_pckt);
if (ret) {
LOG_ERR("OOB Tx failed %d", ret);
return ret;
}
ret = espi_receive_oob(dev, &resp_pckt);
if (ret) {
LOG_ERR("OOB Rx failed %d", ret);
return ret;
}
LOG_INF("OOB transaction completed rcvd: %d bytes", resp_pckt.len);
for (int i = 0; i < resp_pckt.len; i++) {
LOG_INF("%x ", buf[i]);
}
if (resp_pckt.len == OOB_RESPONSE_LEN) {
*temp = buf[OOB_RESPONSE_INDEX];
} else {
LOG_ERR("Incorrect size response");
}
return 0;
}
#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)
pwrgd_dev = device_get_binding(BRD_PWR_PWRGD);
if (!pwrgd_dev) {
LOG_WRN("%s not found", BRD_PWR_PWRGD);
return -1;
}
rsm_dev = device_get_binding(BRD_PWR_RSMRST);
if (!rsm_dev) {
LOG_WRN("%s not found", BRD_PWR_RSMRST);
return -1;
}
#endif
espi_dev = device_get_binding(ESPI_DEV);
if (!espi_dev) {
LOG_WRN("Fail to find %s", ESPI_DEV);
return -1;
}
LOG_INF("Hello eSPI test %s", CONFIG_BOARD);
#if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay)
ret = gpio_pin_configure(pwrgd_dev, BRD_PWR_PWRGD_PIN,
GPIO_INPUT | GPIO_ACTIVE_HIGH);
if (ret) {
LOG_ERR("Unable to configure %d:%d",
BRD_PWR_PWRGD_PIN, ret);
return ret;
}
ret = gpio_pin_configure(rsm_dev, BRD_PWR_RSMRST_PIN,
GPIO_OUTPUT | GPIO_ACTIVE_HIGH);
if (ret) {
LOG_ERR("Unable to config %d: %d",
BRD_PWR_RSMRST_PIN, ret);
return ret;
}
ret = gpio_pin_set(rsm_dev, BRD_PWR_RSMRST_PIN, 0);
if (ret) {
LOG_ERR("Unable to initialize %d",
BRD_PWR_RSMRST_PIN);
return -1;
}
#endif
espi_init();
#if DT_NODE_HAS_STATUS(BRD_PWR_NODE, okay)
ret = wait_for_pin(pwrgd_dev, BRD_PWR_PWRGD_PIN,
PWR_SEQ_TIMEOUT, 1);
if (ret) {
LOG_ERR("RSMRST_PWRGD timeout");
return ret;
}
ret = gpio_pin_set(rsm_dev, BRD_PWR_RSMRST_PIN, 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 lenghty operations
* in the eSPI slave, need to explicitly send slave boot
*/
bool vw_ch_sts;
/* Simulate lenghty 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.
*/
for (int i = 0; i < 5; i++) {
int temp;
ret = get_pch_temp(espi_dev, &temp);
if (ret) {
LOG_ERR("eSPI OOB transaction failed %d", ret);
} else {
LOG_INF("Temp: %d ", temp);
}
}
/* 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;
}
void main(void)
{
espi_test();
}