diff options
author | Furquan Shaikh <furquan@google.com> | 2020-05-11 14:28:13 -0700 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2020-05-13 08:41:20 +0000 |
commit | 70063ff565cbc9fafd54dc3efeb313d1789ce9eb (patch) | |
tree | 212be0bc58f85d208e631b893b66e6482ffef385 | |
parent | 785a3b4a6fa7c0d2597d3322e8c0394d51c9c41a (diff) |
soc/amd/common/block: Add support for configuring eSPI connection to slave
This change adds a helper function espi_setup() which allows SoCs to
configure connection to slave. Most of the configuration is dependent
upon mainboard settings in espi_config done as part of the device
tree. The general flow for setup involves the following steps:
1. Set initial configuration (lowest operating frequency and single mode).
2. Perform in-band reset and set initial configuration since the
settings would be lost by the reset.
3. Read slave capabilities.
4. Set slave configuration based on mainboard settings.
5. Perform eSPI host controller configuration to match the slave
configuration and set polarities for VW interrupts.
6. Perform VW channel setup and deassert PLTRST#.
7. Perform peripheral channel setup.
8. Perform OOB channel setup.
9. Perform flash channel setup.
10. Enable subtractive decoding if requested by mainboard.
BUG=b:153675913
Signed-off-by: Furquan Shaikh <furquan@google.com>
Change-Id: I872ec09cd92e9bb53f22e38d2773f3491355279e
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41272
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Raul Rangel <rrangel@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
-rw-r--r-- | src/soc/amd/common/block/include/amdblocks/espi.h | 61 | ||||
-rw-r--r-- | src/soc/amd/common/block/lpc/espi_util.c | 630 |
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; +} |