summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/amd/common/block/include/amdblocks/espi.h61
-rw-r--r--src/soc/amd/common/block/lpc/espi_util.c630
2 files changed, 691 insertions, 0 deletions
diff --git a/src/soc/amd/common/block/include/amdblocks/espi.h b/src/soc/amd/common/block/include/amdblocks/espi.h
index f47386bc5d..e882af0305 100644
--- a/src/soc/amd/common/block/include/amdblocks/espi.h
+++ b/src/soc/amd/common/block/include/amdblocks/espi.h
@@ -26,6 +26,46 @@
#define ESPI_GENERIC_MMIO_WIN_COUNT 4
#define ESPI_GENERIC_MMIO_MAX_WIN_SIZE 0x10000
+#define ESPI_SLAVE0_CONFIG 0x68
+#define ESPI_CRC_CHECKING_EN (1 << 31)
+#define ESPI_ALERT_MODE (1 << 30)
+
+#define ESPI_IO_MODE_SHIFT 28
+#define ESPI_IO_MODE_MASK (0x3 << ESPI_IO_MODE_SHIFT)
+#define ESPI_IO_MODE_VALUE(x) ((x) << ESPI_IO_MODE_SHIFT)
+
+#define ESPI_OP_FREQ_SHIFT 25
+#define ESPI_OP_FREQ_MASK (0x7 << ESPI_OP_FREQ_SHIFT)
+#define ESPI_OP_FREQ_VALUE(x) ((x) << ESPI_OP_FREQ_SHIFT)
+
+#define ESPI_PERIPH_CH_EN (1 << 3)
+#define ESPI_VW_CH_EN (1 << 2)
+#define ESPI_OOB_CH_EN (1 << 1)
+#define ESPI_FLASH_CH_EN (1 << 0)
+
+/*
+ * Virtual wire interrupt polarity. If the interrupt is active level high or active falling
+ * edge, then controller expects its bit to be cleared in ESPI_RXVW_POLARITY whereas if the
+ * interrupt is active level low or active rising edge, then its bit needs to be set in
+ * ESPI_RXVW_POLARITY.
+ */
+#define ESPI_VW_IRQ_LEVEL_HIGH(x) (0 << (x))
+#define ESPI_VW_IRQ_LEVEL_LOW(x) (1 << (x))
+#define ESPI_VW_IRQ_EDGE_HIGH(x) (1 << (x))
+#define ESPI_VW_IRQ_EDGE_LOW(x) (0 << (x))
+
+enum espi_io_mode {
+ ESPI_IO_MODE_SINGLE = ESPI_IO_MODE_VALUE(0),
+ ESPI_IO_MODE_DUAL = ESPI_IO_MODE_VALUE(1),
+ ESPI_IO_MODE_QUAD = ESPI_IO_MODE_VALUE(2),
+};
+
+enum espi_op_freq {
+ ESPI_OP_FREQ_16_MHZ = ESPI_OP_FREQ_VALUE(0),
+ ESPI_OP_FREQ_33_MHZ = ESPI_OP_FREQ_VALUE(1),
+ ESPI_OP_FREQ_66_MHZ = ESPI_OP_FREQ_VALUE(2),
+};
+
struct espi_config {
/* Bitmap for standard IO decodes. Use ESPI_DECODE_IO_* above. */
uint32_t std_io_decode_bitmap;
@@ -34,6 +74,21 @@ struct espi_config {
uint16_t base;
size_t size;
} generic_io_range[ESPI_GENERIC_IO_WIN_COUNT];
+
+ /* Slave configuration parameters */
+ enum espi_io_mode io_mode;
+ enum espi_op_freq op_freq_mhz;
+
+ uint32_t crc_check_enable:1;
+ uint32_t dedicated_alert_pin:1;
+ uint32_t periph_ch_en:1;
+ uint32_t vw_ch_en:1;
+ uint32_t oob_ch_en:1;
+ uint32_t flash_ch_en:1;
+ uint32_t subtractive_decode:1;
+
+ /* Use ESPI_VW_IRQ_* above */
+ uint32_t vw_irq_polarity;
};
/*
@@ -60,4 +115,10 @@ void espi_configure_decodes(void);
*/
void espi_update_static_bar(uintptr_t bar);
+/*
+ * Perform eSPI connection setup to the slave. Currently, this supports slave0 only.
+ * Returns 0 on success and -1 on error.
+ */
+int espi_setup(void);
+
#endif /* __AMDBLOCKS_ESPI_H__ */
diff --git a/src/soc/amd/common/block/lpc/espi_util.c b/src/soc/amd/common/block/lpc/espi_util.c
index 83f36957bd..5ca0d2831a 100644
--- a/src/soc/amd/common/block/lpc/espi_util.c
+++ b/src/soc/amd/common/block/lpc/espi_util.c
@@ -5,9 +5,11 @@
#include <amdblocks/lpc.h>
#include <arch/mmio.h>
#include <console/console.h>
+#include <espi.h>
#include <soc/pci_devs.h>
#include <stddef.h>
#include <stdint.h>
+#include <timer.h>
#include <types.h>
static uintptr_t espi_bar;
@@ -302,3 +304,631 @@ void espi_configure_decodes(void)
cfg->generic_io_range[i].size);
}
}
+
+#define ESPI_DN_TX_HDR0 0x00
+enum espi_cmd_type {
+ CMD_TYPE_SET_CONFIGURATION = 0,
+ CMD_TYPE_GET_CONFIGURATION = 1,
+ CMD_TYPE_IN_BAND_RESET = 2,
+ CMD_TYPE_PERIPHERAL = 4,
+ CMD_TYPE_VW = 5,
+ CMD_TYPE_OOB = 6,
+ CMD_TYPE_FLASH = 7,
+};
+
+#define ESPI_DN_TX_HDR1 0x04
+#define ESPI_DN_TX_HDR2 0x08
+#define ESPI_DN_TX_DATA 0x0c
+
+#define ESPI_MASTER_CAP 0x2c
+#define ESPI_VW_MAX_SIZE_SHIFT 13
+#define ESPI_VW_MAX_SIZE_MASK (0x3f << ESPI_VW_MAX_SIZE_SHIFT)
+
+#define ESPI_GLOBAL_CONTROL_1 0x34
+#define ESPI_SUB_DECODE_SLV_SHIFT 3
+#define ESPI_SUB_DECODE_SLV_MASK (0x3 << ESPI_SUB_DECODE_SLV_SHIFT)
+#define ESPI_SUB_DECODE_EN (1 << 2)
+
+#define SLAVE0_INT_STS 0x70
+#define ESPI_STATUS_DNCMD_COMPLETE (1 << 28)
+#define ESPI_STATUS_NON_FATAL_ERROR (1 << 6)
+#define ESPI_STATUS_FATAL_ERROR (1 << 5)
+#define ESPI_STATUS_NO_RESPONSE (1 << 4)
+#define ESPI_STATUS_CRC_ERR (1 << 2)
+#define ESPI_STATUS_WAIT_TIMEOUT (1 << 1)
+#define ESPI_STATUS_BUS_ERROR (1 << 0)
+
+#define ESPI_RXVW_POLARITY 0xac
+
+#define ESPI_CMD_TIMEOUT_US 100
+#define ESPI_CH_READY_TIMEOUT_US 1000
+
+union espi_txhdr0 {
+ uint32_t val;
+ struct {
+ uint32_t cmd_type:3;
+ uint32_t cmd_sts:1;
+ uint32_t slave_sel:2;
+ uint32_t rsvd:2;
+ uint32_t hdata0:8;
+ uint32_t hdata1:8;
+ uint32_t hdata2:8;
+ };
+} __packed;
+
+union espi_txhdr1 {
+ uint32_t val;
+ struct {
+ uint32_t hdata3:8;
+ uint32_t hdata4:8;
+ uint32_t hdata5:8;
+ uint32_t hdata6:8;
+ };
+} __packed;
+
+union espi_txhdr2 {
+ uint32_t val;
+ struct {
+ uint32_t hdata7:8;
+ uint32_t rsvd:24;
+ };
+} __packed;
+
+union espi_txdata {
+ uint32_t val;
+ struct {
+ uint32_t byte0:8;
+ uint32_t byte1:8;
+ uint32_t byte2:8;
+ uint32_t byte3:8;
+ };
+} __packed;
+
+struct espi_cmd {
+ union espi_txhdr0 hdr0;
+ union espi_txhdr1 hdr1;
+ union espi_txhdr2 hdr2;
+ union espi_txdata data;
+} __packed;
+
+/* Wait up to ESPI_CMD_TIMEOUT_US for hardware to clear DNCMD_STATUS bit. */
+static int espi_wait_ready(void)
+{
+ struct stopwatch sw;
+ union espi_txhdr0 hdr0;
+
+ stopwatch_init_usecs_expire(&sw, ESPI_CMD_TIMEOUT_US);
+ do {
+ hdr0.val = espi_read32(ESPI_DN_TX_HDR0);
+ if (!hdr0.cmd_sts)
+ return 0;
+ } while (!stopwatch_expired(&sw));
+
+ return -1;
+}
+
+/* Clear interrupt status register */
+static void espi_clear_status(void)
+{
+ uint32_t status = espi_read32(SLAVE0_INT_STS);
+ if (status)
+ espi_write32(SLAVE0_INT_STS, status);
+}
+
+/*
+ * Wait up to ESPI_CMD_TIMEOUT_US for interrupt status register to update after sending a
+ * command.
+ */
+static int espi_check_status(uint32_t *status)
+{
+ struct stopwatch sw;
+
+ stopwatch_init_usecs_expire(&sw, ESPI_CMD_TIMEOUT_US);
+ do {
+ *status = espi_read32(SLAVE0_INT_STS);
+ if (*status)
+ return 0;
+ } while (!stopwatch_expired(&sw));
+
+ printk(BIOS_ERR, "Error: eSPI timed out waiting for status update.\n");
+
+ return -1;
+}
+
+static void espi_show_failure(const struct espi_cmd *cmd, const char *str, uint32_t status)
+{
+ printk(BIOS_ERR, "eSPI cmd0-cmd2: %08x %08x %08x data: %08x.\n",
+ cmd->hdr0.val, cmd->hdr1.val, cmd->hdr2.val, cmd->data.val);
+ printk(BIOS_ERR, "%s (Status = 0x%x)\n", str, status);
+}
+
+static int espi_send_command(const struct espi_cmd *cmd)
+{
+ uint32_t status;
+
+ if (CONFIG(ESPI_DEBUG))
+ printk(BIOS_ERR, "eSPI cmd0-cmd2: %08x %08x %08x data: %08x.\n",
+ cmd->hdr0.val, cmd->hdr1.val, cmd->hdr2.val, cmd->data.val);
+
+ if (espi_wait_ready() == -1) {
+ espi_show_failure(cmd, "Error: eSPI was not ready to accept a command", 0);
+ return -1;
+ }
+
+ espi_clear_status();
+
+ espi_write32(ESPI_DN_TX_HDR1, cmd->hdr1.val);
+ espi_write32(ESPI_DN_TX_HDR2, cmd->hdr2.val);
+ espi_write32(ESPI_DN_TX_DATA, cmd->data.val);
+
+ /* Dword 0 must be last as this write triggers the transaction */
+ espi_write32(ESPI_DN_TX_HDR0, cmd->hdr0.val);
+
+ if (espi_wait_ready() == -1) {
+ espi_show_failure(cmd,
+ "Error: eSPI timed out waiting for command to complete", 0);
+ return -1;
+ }
+
+ if (espi_check_status(&status) == -1) {
+ espi_show_failure(cmd, "Error: eSPI check status failed", 0);
+ return -1;
+ }
+
+ /* If command did not complete downstream, return error. */
+ if (!(status & ESPI_STATUS_DNCMD_COMPLETE)) {
+ espi_show_failure(cmd, "Error: eSPI downstream command completion failure",
+ status);
+ return -1;
+ }
+
+ if (status & ~ESPI_STATUS_DNCMD_COMPLETE) {
+ espi_show_failure(cmd, "Error: eSPI status register bits set", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int espi_send_reset(void)
+{
+ struct espi_cmd cmd = {
+ .hdr0 = {
+ .cmd_type = CMD_TYPE_IN_BAND_RESET,
+ .cmd_sts = 1,
+ },
+ };
+
+ return espi_send_command(&cmd);
+}
+
+static int espi_send_pltrst_deassert(const struct espi_config *mb_cfg)
+{
+ struct espi_cmd cmd = {
+ .hdr0 = {
+ .cmd_type = CMD_TYPE_VW,
+ .cmd_sts = 1,
+ .hdata0 = 0, /* 1 VW group */
+ },
+ .data = {
+ .byte0 = ESPI_VW_INDEX_SYSTEM_EVENT_3,
+ .byte1 = ESPI_VW_SIGNAL_HIGH(ESPI_VW_PLTRST),
+ },
+ };
+
+ if (!mb_cfg->vw_ch_en)
+ return 0;
+
+ return espi_send_command(&cmd);
+}
+
+/*
+ * In case of get configuration command, hdata0 contains bits 15:8 of the slave register address
+ * and hdata1 contains bits 7:0 of the slave register address.
+ */
+#define ESPI_CONFIGURATION_HDATA0(a) (((a) >> 8) & 0xff)
+#define ESPI_CONFIGURATION_HDATA1(a) ((a) & 0xff)
+
+static int espi_get_configuration(uint16_t slave_reg_addr, uint32_t *config)
+{
+ struct espi_cmd cmd = {
+ .hdr0 = {
+ .cmd_type = CMD_TYPE_GET_CONFIGURATION,
+ .cmd_sts = 1,
+ .hdata0 = ESPI_CONFIGURATION_HDATA0(slave_reg_addr),
+ .hdata1 = ESPI_CONFIGURATION_HDATA1(slave_reg_addr),
+ },
+ };
+
+ *config = 0;
+
+ if (espi_send_command(&cmd))
+ return -1;
+
+ *config = espi_read32(ESPI_DN_TX_HDR1);
+
+ if (CONFIG(ESPI_DEBUG))
+ printk(BIOS_DEBUG, "Get configuration for slave register(0x%x): 0x%x\n",
+ slave_reg_addr, *config);
+
+ return 0;
+}
+
+static int espi_set_configuration(uint16_t slave_reg_addr, uint32_t config)
+{
+ struct espi_cmd cmd = {
+ .hdr0 = {
+ .cmd_type = CMD_TYPE_SET_CONFIGURATION,
+ .cmd_sts = 1,
+ .hdata0 = ESPI_CONFIGURATION_HDATA0(slave_reg_addr),
+ .hdata1 = ESPI_CONFIGURATION_HDATA1(slave_reg_addr),
+ },
+ .hdr1 = {
+ .val = config,
+ },
+ };
+
+ return espi_send_command(&cmd);
+}
+
+static int espi_get_general_configuration(uint32_t *config)
+{
+ int ret = espi_get_configuration(ESPI_SLAVE_GENERAL_CFG, config);
+ if (ret == -1)
+ return -1;
+
+ espi_show_slave_general_configuration(*config);
+ return 0;
+}
+
+static void espi_set_io_mode_config(enum espi_io_mode mb_io_mode, uint32_t slave_caps,
+ uint32_t *slave_config, uint32_t *ctrlr_config)
+{
+ switch (mb_io_mode) {
+ case ESPI_IO_MODE_QUAD:
+ if (espi_slave_supports_quad_io(slave_caps)) {
+ *slave_config |= ESPI_SLAVE_IO_MODE_SEL_QUAD;
+ *ctrlr_config |= ESPI_IO_MODE_QUAD;
+ break;
+ }
+ printk(BIOS_ERR, "Error: eSPI Quad I/O not supported. Dropping to dual mode.\n");
+ /* Intentional fall-through */
+ case ESPI_IO_MODE_DUAL:
+ if (espi_slave_supports_dual_io(slave_caps)) {
+ *slave_config |= ESPI_SLAVE_IO_MODE_SEL_DUAL;
+ *ctrlr_config |= ESPI_IO_MODE_DUAL;
+ break;
+ }
+ printk(BIOS_ERR,
+ "Error: eSPI Dual I/O not supported. Dropping to single mode.\n");
+ /* Intentional fall-through */
+ case ESPI_IO_MODE_SINGLE:
+ /* Single I/O mode is always supported. */
+ *slave_config |= ESPI_SLAVE_IO_MODE_SEL_SINGLE;
+ *ctrlr_config |= ESPI_IO_MODE_SINGLE;
+ break;
+ default:
+ die("No supported eSPI I/O modes!\n");
+ }
+}
+
+static void espi_set_op_freq_config(enum espi_op_freq mb_op_freq, uint32_t slave_caps,
+ uint32_t *slave_config, uint32_t *ctrlr_config)
+{
+ int slave_max_speed_mhz = espi_slave_max_speed_mhz_supported(slave_caps);
+
+ switch (mb_op_freq) {
+ case ESPI_OP_FREQ_66_MHZ:
+ if (slave_max_speed_mhz >= 66) {
+ *slave_config |= ESPI_SLAVE_OP_FREQ_SEL_66_MHZ;
+ *ctrlr_config |= ESPI_OP_FREQ_66_MHZ;
+ break;
+ }
+ printk(BIOS_ERR, "Error: eSPI 66MHz not supported. Dropping to 33MHz.\n");
+ /* Intentional fall-through */
+ case ESPI_OP_FREQ_33_MHZ:
+ if (slave_max_speed_mhz >= 33) {
+ *slave_config |= ESPI_SLAVE_OP_FREQ_SEL_33_MHZ;
+ *ctrlr_config |= ESPI_OP_FREQ_33_MHZ;
+ break;
+ }
+ printk(BIOS_ERR, "Error: eSPI 33MHz not supported. Dropping to 16MHz.\n");
+ /* Intentional fall-through */
+ case ESPI_OP_FREQ_16_MHZ:
+ /*
+ * eSPI spec says the minimum frequency is 20MHz, but AMD datasheets support
+ * 16.7 Mhz.
+ */
+ if (slave_max_speed_mhz > 0) {
+ *slave_config |= ESPI_SLAVE_OP_FREQ_SEL_20_MHZ;
+ *ctrlr_config |= ESPI_OP_FREQ_16_MHZ;
+ break;
+ }
+ /* Intentional fall-through */
+ default:
+ die("No supported eSPI Operating Frequency!\n");
+ }
+}
+
+static int espi_set_general_configuration(const struct espi_config *mb_cfg, uint32_t slave_caps)
+{
+ uint32_t slave_config = 0;
+ uint32_t ctrlr_config = 0;
+
+ if (mb_cfg->crc_check_enable) {
+ slave_config |= ESPI_SLAVE_CRC_ENABLE;
+ ctrlr_config |= ESPI_CRC_CHECKING_EN;
+ }
+
+ if (mb_cfg->dedicated_alert_pin) {
+ slave_config |= ESPI_SLAVE_ALERT_MODE_PIN;
+ ctrlr_config |= ESPI_ALERT_MODE;
+ }
+
+ espi_set_io_mode_config(mb_cfg->io_mode, slave_caps, &slave_config, &ctrlr_config);
+ espi_set_op_freq_config(mb_cfg->op_freq_mhz, slave_caps, &slave_config, &ctrlr_config);
+
+ if (CONFIG(ESPI_DEBUG))
+ printk(BIOS_INFO, "Setting general configuration: slave: 0x%x controller: 0x%x\n",
+ slave_config, ctrlr_config);
+
+ if (espi_set_configuration(ESPI_SLAVE_GENERAL_CFG, slave_config) == -1)
+ return -1;
+
+ espi_write32(ESPI_SLAVE0_CONFIG, ctrlr_config);
+ return 0;
+}
+
+static int espi_wait_channel_ready(uint16_t slave_reg_addr)
+{
+ struct stopwatch sw;
+ uint32_t config;
+
+ stopwatch_init_usecs_expire(&sw, ESPI_CH_READY_TIMEOUT_US);
+ do {
+ espi_get_configuration(slave_reg_addr, &config);
+ if (espi_slave_is_channel_ready(config))
+ return 0;
+ } while (!stopwatch_expired(&sw));
+
+ printk(BIOS_ERR, "Error: Channel is not ready after %d usec (slave addr: 0x%x)\n",
+ ESPI_CH_READY_TIMEOUT_US, slave_reg_addr);
+ return -1;
+
+}
+
+static void espi_enable_ctrlr_channel(uint32_t channel_en)
+{
+ uint32_t reg = espi_read32(ESPI_SLAVE0_CONFIG);
+
+ reg |= channel_en;
+
+ espi_write32(ESPI_SLAVE0_CONFIG, reg);
+}
+
+static int espi_set_channel_configuration(uint32_t slave_config, uint32_t slave_reg_addr,
+ uint32_t ctrlr_enable)
+{
+ if (espi_set_configuration(slave_reg_addr, slave_config) == -1)
+ return -1;
+
+ if (!(slave_config & ESPI_SLAVE_CHANNEL_ENABLE))
+ return 0;
+
+ if (espi_wait_channel_ready(slave_reg_addr) == -1)
+ return -1;
+
+ espi_enable_ctrlr_channel(ctrlr_enable);
+ return 0;
+}
+
+static int espi_setup_vw_channel(const struct espi_config *mb_cfg, uint32_t slave_caps)
+{
+ uint32_t slave_vw_caps;
+ uint32_t ctrlr_vw_caps;
+ uint32_t slave_vw_count_supp;
+ uint32_t ctrlr_vw_count_supp;
+ uint32_t use_vw_count;
+ uint32_t slave_config;
+
+ if (!mb_cfg->vw_ch_en)
+ return 0;
+
+ if (!espi_slave_supports_vw_channel(slave_caps)) {
+ printk(BIOS_ERR, "Error: eSPI slave doesn't support VW channel!\n");
+ return -1;
+ }
+
+ if (espi_get_configuration(ESPI_SLAVE_VW_CFG, &slave_vw_caps) == -1)
+ return -1;
+
+ ctrlr_vw_caps = espi_read32(ESPI_MASTER_CAP);
+ ctrlr_vw_count_supp = (ctrlr_vw_caps & ESPI_VW_MAX_SIZE_MASK) >> ESPI_VW_MAX_SIZE_SHIFT;
+
+ slave_vw_count_supp = espi_slave_get_vw_count_supp(slave_vw_caps);
+ use_vw_count = MIN(ctrlr_vw_count_supp, slave_vw_count_supp);
+
+ slave_config = ESPI_SLAVE_CHANNEL_ENABLE | ESPI_SLAVE_VW_COUNT_SEL_VAL(use_vw_count);
+ return espi_set_channel_configuration(slave_config, ESPI_SLAVE_VW_CFG, ESPI_VW_CH_EN);
+}
+
+static int espi_setup_periph_channel(const struct espi_config *mb_cfg, uint32_t slave_caps)
+{
+ uint32_t slave_config;
+ /* Peripheral channel requires BME bit to be set when enabling the channel. */
+ const uint32_t slave_en_mask = ESPI_SLAVE_CHANNEL_READY |
+ ESPI_SLAVE_PERIPH_BUS_MASTER_ENABLE;
+
+ if (espi_get_configuration(ESPI_SLAVE_PERIPH_CFG, &slave_config) == -1)
+ return -1;
+
+ /*
+ * Peripheral channel is the only one which is enabled on reset. So, if the mainboard
+ * wants to disable it, set configuration to disable peripheral channel. It also
+ * requires that BME bit be cleared.
+ */
+ if (mb_cfg->periph_ch_en) {
+ if (!espi_slave_supports_periph_channel(slave_caps)) {
+ printk(BIOS_ERR, "Error: eSPI slave doesn't support periph channel!\n");
+ return -1;
+ }
+ slave_config |= slave_en_mask;
+ } else {
+ slave_config &= ~slave_en_mask;
+ }
+
+ return espi_set_channel_configuration(slave_config, ESPI_SLAVE_PERIPH_CFG,
+ ESPI_PERIPH_CH_EN);
+}
+
+static int espi_setup_oob_channel(const struct espi_config *mb_cfg, uint32_t slave_caps)
+{
+ uint32_t slave_config;
+
+ if (!mb_cfg->oob_ch_en)
+ return 0;
+
+ if (!espi_slave_supports_oob_channel(slave_caps)) {
+ printk(BIOS_ERR, "Error: eSPI slave doesn't support OOB channel!\n");
+ return -1;
+ }
+
+ if (espi_get_configuration(ESPI_SLAVE_OOB_CFG, &slave_config) == -1)
+ return -1;
+
+ slave_config |= ESPI_SLAVE_CHANNEL_ENABLE;
+
+ return espi_set_channel_configuration(slave_config, ESPI_SLAVE_OOB_CFG,
+ ESPI_OOB_CH_EN);
+}
+
+static int espi_setup_flash_channel(const struct espi_config *mb_cfg, uint32_t slave_caps)
+{
+ uint32_t slave_config;
+
+ if (!mb_cfg->flash_ch_en)
+ return 0;
+
+ if (!espi_slave_supports_flash_channel(slave_caps)) {
+ printk(BIOS_ERR, "Error: eSPI slave doesn't support flash channel!\n");
+ return -1;
+ }
+
+ if (espi_get_configuration(ESPI_SLAVE_FLASH_CFG, &slave_config) == -1)
+ return -1;
+
+ slave_config |= ESPI_SLAVE_CHANNEL_ENABLE;
+
+ return espi_set_channel_configuration(slave_config, ESPI_SLAVE_FLASH_CFG,
+ ESPI_FLASH_CH_EN);
+}
+
+static void espi_set_initial_config(const struct espi_config *mb_cfg)
+{
+ uint32_t espi_initial_mode = ESPI_OP_FREQ_16_MHZ | ESPI_IO_MODE_SINGLE;
+
+ if (mb_cfg->dedicated_alert_pin)
+ espi_initial_mode |= ESPI_ALERT_MODE;
+
+ espi_write32(ESPI_SLAVE0_CONFIG, espi_initial_mode);
+}
+
+static void espi_setup_subtractive_decode(const struct espi_config *mb_cfg)
+{
+ uint32_t global_ctrl_reg;
+ global_ctrl_reg = espi_read32(ESPI_GLOBAL_CONTROL_1);
+
+ if (mb_cfg->subtractive_decode) {
+ global_ctrl_reg &= ~ESPI_SUB_DECODE_SLV_MASK;
+ global_ctrl_reg |= ESPI_SUB_DECODE_EN;
+
+ } else {
+ global_ctrl_reg &= ~ESPI_SUB_DECODE_EN;
+ }
+ espi_write32(ESPI_GLOBAL_CONTROL_1, global_ctrl_reg);
+}
+
+int espi_setup(void)
+{
+ uint32_t slave_caps;
+ const struct espi_config *cfg = espi_get_config();
+
+ /*
+ * Boot sequence: Step 1
+ * Set correct initial configuration to talk to the slave:
+ * Set clock frequency to 16.7MHz and single IO mode.
+ */
+ espi_set_initial_config(cfg);
+
+ /*
+ * Boot sequence: Step 2
+ * Send in-band reset
+ * The resets affects both host and slave devices, so set initial config again.
+ */
+ if (espi_send_reset() == -1) {
+ printk(BIOS_ERR, "Error: In-band reset failed!\n");
+ return -1;
+ }
+ espi_set_initial_config(cfg);
+
+ /*
+ * Boot sequence: Step 3
+ * Get configuration of slave device.
+ */
+ if (espi_get_general_configuration(&slave_caps) == -1) {
+ printk(BIOS_ERR, "Error: Slave GET_CONFIGURATION failed!\n");
+ return -1;
+ }
+
+ /*
+ * Boot sequence:
+ * Step 4: Write slave device general config
+ * Step 5: Set host slave config
+ */
+ if (espi_set_general_configuration(cfg, slave_caps) == -1) {
+ printk(BIOS_ERR, "Error: Slave SET_CONFIGURATION failed!\n");
+ return -1;
+ }
+
+ /*
+ * Setup polarity before enabling the VW channel so any interrupts
+ * received will have the correct polarity.
+ */
+ espi_write32(ESPI_RXVW_POLARITY, cfg->vw_irq_polarity);
+
+ /*
+ * Boot Sequences: Steps 6 - 9
+ * Channel setup
+ */
+ /* Set up VW first so we can deassert PLTRST#. */
+ if (espi_setup_vw_channel(cfg, slave_caps) == -1) {
+ printk(BIOS_ERR, "Error: Setup VW channel failed!\n");
+ return -1;
+ }
+
+ /* De-assert PLTRST# if VW channel is enabled by mainboard. */
+ if (espi_send_pltrst_deassert(cfg) == -1) {
+ printk(BIOS_ERR, "Error: PLTRST deassertion failed!\n");
+ return -1;
+ }
+
+ if (espi_setup_periph_channel(cfg, slave_caps) == -1) {
+ printk(BIOS_ERR, "Error: Setup Periph channel failed!\n");
+ return -1;
+ }
+
+ if (espi_setup_oob_channel(cfg, slave_caps) == -1) {
+ printk(BIOS_ERR, "Error: Setup OOB channel failed!\n");
+ return -1;
+ }
+
+ if (espi_setup_flash_channel(cfg, slave_caps) == -1) {
+ printk(BIOS_ERR, "Error: Setup Flash channel failed!\n");
+ return -1;
+ }
+
+ /* Enable subtractive decode if configured */
+ espi_setup_subtractive_decode(cfg);
+
+ return 0;
+}