| /* |
| * This file is part of the coreboot project. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <console/console.h> |
| #include <device/pci_ops.h> |
| #include <soc/espi.h> |
| #include <soc/pci_devs.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <timer.h> |
| #include <amdblocks/lpc.h> |
| #include <amdblocks/lpc_espi_checker.h> |
| #include <lib.h> |
| #include <assert.h> |
| |
| #if CONFIG(DEBUG_ESPI_INIT) |
| # define ESPI_DBG BIOS_DEBUG |
| # define ESPI_EXT_DBG BIOS_SPEW |
| #else |
| # define ESPI_DBG BIOS_NEVER |
| # define ESPI_EXT_DBG BIOS_NEVER |
| #endif |
| |
| static void write32_espi(uint8_t *espi, uint8_t offset, uint32_t val) |
| { |
| printk(ESPI_EXT_DBG, "Writing 0x%08x to espi offset 0x%02x\n", |
| val, offset); |
| write32(espi + offset, val); |
| } |
| |
| static uint32_t read32_espi(uint8_t *espi, uint8_t offset) |
| { |
| uint32_t val = read32(espi + offset); |
| printk(ESPI_EXT_DBG, "Read 0x%08x from espi offset 0x%02x\n", |
| val, offset); |
| return val; |
| } |
| |
| void dump_espi_regs(void) |
| { |
| /* Show the entire address space of 64 dwords*/ |
| hexdump32(ESPI_EXT_DBG, espi_read_base_address(), 0x40); |
| } |
| |
| static void espi_show_slave_configuration(uint32_t config) |
| { |
| if ((ESPI_DBG < (int)BIOS_EMERG) || (ESPI_DBG >= (int)BIOS_NEVER)) |
| return; |
| |
| printk(ESPI_DBG, "eSPI Slave configuration:\n"); |
| |
| if (config & ESPI_SLAVE_CRC_CHECKING_EN) |
| printk(ESPI_DBG, " CRC checking enabled\n"); |
| |
| if (config & ESPI_SLAVE_RESP_MOD_EN) |
| printk(ESPI_DBG, " Response modifier enabled\n"); |
| |
| if (config & ESPI_SLAVE_ALERT_MODE) |
| printk(ESPI_DBG, " Dedicated Alert# used to signal alert event\n"); |
| else |
| printk(ESPI_DBG, " IO bit1 pin used to signal alert event\n"); |
| |
| if ((config & (0x03 << 26)) == ESPI_SLAVE_IO_MODE_SINGLE) |
| printk(ESPI_DBG, " eSPI single IO mode selected\n"); |
| else if ((config & (0x03 << 26)) == ESPI_SLAVE_IO_MODE_DUAL) |
| printk(ESPI_DBG, " eSPI dual IO mode selected\n"); |
| else if ((config & (0x03 << 26)) == ESPI_SLAVE_IO_MODE_QUAD) |
| printk(ESPI_DBG, " eSPI quad IO mode selected\n"); |
| else |
| printk(ESPI_DBG, " Error: Invalid eSPI IO mode selected\n"); |
| |
| if ((config & (0x03 << 24)) == ESPI_SLAVE_QUAD_IO_SUPPORTED) |
| printk(ESPI_DBG, " eSPI quad and single IO modes supported\n"); |
| else if ((config & (0x03 << 24)) == ESPI_SLAVE_DUAL_IO_SUPPORTED) |
| printk(ESPI_DBG, " eSPI dual and single IO mode supported\n"); |
| else if ((config & (0x03 << 24)) == (ESPI_SLAVE_DUAL_IO_SUPPORTED | |
| ESPI_SLAVE_QUAD_IO_SUPPORTED)) |
| printk(ESPI_DBG, " eSPI quad, dual, and single IO modes supported\n"); |
| else |
| printk(ESPI_DBG, " Only eSPI single IO mode supported\n"); |
| |
| if (config & ESPI_SLAVE_OPEN_DRAIN_ALERT) |
| printk(ESPI_DBG, " Alert# pin is open-drain\n"); |
| else |
| printk(ESPI_DBG, " Alert# pin is driven\n"); |
| |
| if ((config & (0x07 << 20)) == ESPI_SLAVE_OP_FREQ_20_MHZ) |
| printk(ESPI_DBG, " eSPI 20MHz selected\n"); |
| else if ((config & (0x07 << 20)) == ESPI_SLAVE_OP_FREQ_25_MHZ) |
| printk(ESPI_DBG, " eSPI 25MHz selected\n"); |
| else if ((config & (0x07 << 20)) == ESPI_SLAVE_OP_FREQ_33_MHZ) |
| printk(ESPI_DBG, " eSPI 33MHz selected\n"); |
| else if ((config & (0x07 << 20)) == ESPI_SLAVE_OP_FREQ_50_MHZ) |
| printk(ESPI_DBG, " eSPI 50MHz selected\n"); |
| else if ((config & (0x07 << 20)) == ESPI_SLAVE_OP_FREQ_66_MHZ) |
| printk(ESPI_DBG, " eSPI 66MHz selected\n"); |
| else |
| printk(ESPI_DBG, " Error: Invalid eSPI frequency\n"); |
| |
| if (config & ESPI_SLAVE_SUPP_OPEN_DRAIN_ALERT) |
| printk(ESPI_DBG, " Open-drain Alert# pin supported\n"); |
| |
| if ((config & (0x07 << 16)) == ESPI_SLAVE_SUPP_FREQ_20_MHZ) |
| printk(ESPI_DBG, " eSPI 20MHz supported\n"); |
| else if ((config & (0x07 << 16)) == ESPI_SLAVE_SUPP_FREQ_25_MHZ) |
| printk(ESPI_DBG, " eSPI 25MHz supported\n"); |
| else if ((config & (0x07 << 16)) == ESPI_SLAVE_SUPP_FREQ_33_MHZ) |
| printk(ESPI_DBG, " eSPI 33MHz supported\n"); |
| else if ((config & (0x07 << 16)) == ESPI_SLAVE_SUPP_FREQ_50_MHZ) |
| printk(ESPI_DBG, " eSPI 50MHz supported\n"); |
| else if ((config & (0x07 << 16)) == ESPI_SLAVE_SUPP_FREQ_66_MHZ) |
| printk(ESPI_DBG, " eSPI 66MHz supported\n"); |
| else |
| printk(ESPI_DBG, " Error: Invalid eSPI frequency\n"); |
| |
| printk(ESPI_DBG, " Maximum Wait state: %d\n", (config >> 12) & 0x0f); |
| |
| if (config & ESPI_SLAVE_SUPP_PERIPHERAL_CH) |
| printk(ESPI_DBG, " Peripheral Channel supported\n"); |
| if (config & ESPI_SLAVE_SUPP_VIRTUAL_WIRE_CH) |
| printk(ESPI_DBG, " Virtual Wire Channel supported\n"); |
| if (config & ESPI_SLAVE_SUPP_OOB_CH) |
| printk(ESPI_DBG, " OOB Channel supported\n"); |
| if (config & ESPI_SLAVE_SUPP_FLASH_CH) |
| printk(ESPI_DBG, " Flash Access Channel supported\n"); |
| printk(ESPI_DBG, "\n"); |
| } |
| |
| static void espi_show_host_configuration(void) |
| { |
| uint8_t *espi = espi_read_base_address(); |
| uint32_t slave0_decode_en = read32(espi + ESPI_DECODE); |
| uint32_t slave0_config = read32(espi + ESPI_SLAVE0_CONFIG); |
| uint32_t global_ctrl_reg_1 = read32(espi + ESPI_GLOBAL_CONTROL_1); |
| |
| if ((ESPI_DBG < (int)BIOS_EMERG) || (ESPI_DBG >= (int)BIOS_NEVER)) |
| return; |
| |
| printk(ESPI_DBG, "eSPI Host configuration:\n"); |
| if (global_ctrl_reg_1 & ESPI_SUB_DECODE_EN) |
| printk(ESPI_DBG, " eSPI subtractive decode enabled\n"); |
| if (slave0_decode_en & ESPI_DECODE_IO_0X2E_0X2F_EN) |
| printk(ESPI_DBG, " eSPI decode of 0x2e - 0x2f enabled\n"); |
| if (slave0_decode_en & ESPI_DECODE_IO_0X60_0X64_EN) |
| printk(ESPI_DBG, " eSPI decode of 0x60 - 0x64 enabled\n"); |
| if (slave0_decode_en & ESPI_DECODE_IO_0x80_EN) |
| printk(ESPI_DBG, " eSPI decode of 0x80 enabled\n"); |
| |
| for (int i = 0; i < 4; i++) { |
| if (slave0_decode_en & ESPI_DECODE_IO_RANGE_EN(i)) { |
| printk(ESPI_DBG, " IO_RANGE%hd: 0x%04hx- 0x%04hhx\n", i, |
| read16(espi + ESPI_IO_RANGE_BASE(i)), |
| read16(espi + ESPI_IO_RANGE_BASE(i)) + |
| read8(espi + ESPI_IO_RANGE_SIZE(i))); |
| } |
| } |
| |
| for (int i = 0; i < 4; i++) { |
| if (slave0_decode_en & ESPI_DECODE_MMIO_RANGE_EN(i)) { |
| printk(ESPI_DBG, " MMIO_RANGE%d: 0x%08x- 0x%08x\n", i, |
| read32(espi + ESPI_MMIO_RANGE_BASE(i)), |
| (read32(espi + ESPI_MMIO_RANGE_BASE(i)) + |
| read16(espi + ESPI_MMIO_RANGE_SIZE(i)))); |
| } |
| } |
| |
| printk(ESPI_DBG, " IRQ_POLARITY: 0x%08x\n", |
| read32(espi + ESPI_RXVW_POLARITY)); |
| |
| if (slave0_config & ESPI_CRC_CHECKING_EN) |
| printk(ESPI_DBG, " CRC Check Enabled\n"); |
| |
| if (slave0_config & ESPI_ALERT_MODE) |
| printk(ESPI_DBG, " Dedicated Alert# used to signal alert event\n"); |
| else |
| printk(ESPI_DBG, " IO bit1 pin used to signal alert event\n"); |
| |
| if ((slave0_config & (0x03 << 28)) == ESPI_IO_MODE_SINGLE) |
| printk(ESPI_DBG, " eSPI enabled in single IO mode\n"); |
| else if ((slave0_config & (0x03 << 28)) == ESPI_IO_MODE_DUAL) |
| printk(ESPI_DBG, " eSPI enabled in dual IO mode\n"); |
| else if ((slave0_config & (0x03 << 28)) == ESPI_IO_MODE_QUAD) |
| printk(ESPI_DBG, " eSPI enabled in quad IO mode\n"); |
| else |
| printk(ESPI_DBG, " Error: Invalid eSPI IO mode\n"); |
| |
| if ((slave0_config & (0x07 << 25)) == ESPI_OP_FREQ_16_MHZ) |
| printk(ESPI_DBG, " eSPI enabled at 16MHz\n"); |
| else if ((slave0_config & (0x07 << 25)) == ESPI_OP_FREQ_33_MHZ) |
| printk(ESPI_DBG, " eSPI enabled at 33MHz\n"); |
| else if ((slave0_config & (0x07 << 25)) == ESPI_OP_FREQ_66_MHZ) |
| printk(ESPI_DBG, " eSPI enabled at 66MHz\n"); |
| else |
| printk(ESPI_DBG, " Error: Invalid eSPI frequency\n"); |
| |
| if (slave0_config & ESPI_PR_CH_EN) |
| printk(ESPI_DBG, " Peripheral Channel Enabled\n"); |
| |
| if (slave0_config & ESPI_VIRTUAL_WIRE_CH_EN) |
| printk(ESPI_DBG, " Virtual Wire Channel Enabled\n"); |
| |
| if (slave0_config & ESPI_OOB_CH_EN) |
| printk(ESPI_DBG, " OOB Message Channel Enabled\n"); |
| |
| if (slave0_config & ESPI_FLASH_CH_EN) |
| printk(ESPI_DBG, " Flash Access Channel Enabled\n"); |
| printk(ESPI_DBG, "\n"); |
| } |
| |
| const char *status_strings[32] = { |
| "Bus Error", |
| "Wait Timeout", |
| "CRC_Error", |
| "Reserved", |
| "No Response", |
| "Fatal Error", |
| "Non-Fatal Error", |
| "Unknown Response", |
| "Unknown Cycle Type", |
| "Unsuccessful Completion", |
| "Illegal Tag", |
| "Illegal Length", |
| "RX OOB Overflow", |
| "RX Message Overflow", |
| "Rx Flash Overflow", |
| "Protocol Error", |
| "Reserved", |
| "Reserved", |
| "MST Abort", |
| "Watchdog Timeout", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "RX Virtual Wire Group 0", |
| "RX Virtual Wire Group 1", |
| "RX Virtual Wire Group 2", |
| "RX Virtual Wire Group 3", |
| "Downstream command complete", |
| "Peripheral message received", |
| "OOB message received", |
| "Flash request received" |
| }; |
| |
| static void show_espi_status(uint32_t status) |
| { |
| if ((ESPI_DBG < (int)BIOS_EMERG) || (ESPI_DBG >= (int)BIOS_NEVER)) |
| return; |
| |
| if (!status) { |
| printk(ESPI_DBG, "No eSPI status bits set.\n"); |
| return; |
| } |
| |
| for (int i = 0; i < 32; i++) { |
| if (status & (1 << i)) { |
| printk(ESPI_DBG, " Status bit: %s\n", status_strings[i]); |
| } |
| } |
| } |
| |
| static uint32_t espi_wait_response(uint8_t *espi) |
| { |
| struct stopwatch sw; |
| uint32_t status; |
| |
| stopwatch_init_usecs_expire(&sw, 100); |
| do { |
| status = read32(espi + SLAVE0_INT_STS); |
| |
| if (status) |
| return status; |
| |
| } while (!stopwatch_expired(&sw)); |
| ASSERT_MSG(0, "Error: eSPI timed out waiting for a response.\n"); |
| |
| return 0; |
| } |
| |
| static bool espi_wait_ready(uint8_t *espi) |
| { |
| struct stopwatch sw; |
| |
| stopwatch_init_usecs_expire(&sw, 100); |
| do { |
| if (!(read32(espi + ESPI_DN_TXDR0) & ESPI_TX_GO_STATUS)) |
| return true; |
| } while (!stopwatch_expired(&sw)); |
| |
| return false; |
| } |
| |
| static void espi_clear(uint8_t *espi, int ccr) |
| { |
| /* Clear status register */ |
| uint32_t status = read32_espi(espi, SLAVE0_INT_STS); |
| if (status) |
| write32_espi(espi, SLAVE0_INT_STS, status); |
| |
| /* Clear the command registers if requested */ |
| if (!ccr) |
| return; |
| |
| for (int i = 0; i < 0xf; i += 4) { |
| write32_espi(espi, ESPI_DN_TXDR0 + i, 0x00); |
| } |
| } |
| |
| /* TODO: look at turning the command into a struct */ |
| static uint32_t espi_send_command(uint8_t *espi, uint32_t cmd0, uint32_t cmd1, |
| uint32_t cmd2, uint32_t cmd3) |
| { |
| uint32_t status; |
| if (!espi_wait_ready(espi)) { |
| printk(BIOS_WARNING, "Error: eSPI was not ready to accept a command.\n"); |
| return -1; |
| } |
| |
| espi_clear(espi, 0); |
| |
| write32_espi(espi, ESPI_DN_TXDR1, cmd1); |
| write32_espi(espi, ESPI_DN_TXDR2, cmd2); |
| write32_espi(espi, ESPI_DN_TXDR3, cmd3); |
| /* Dword 0 must be last as this write triggers the transaction */ |
| write32_espi(espi, ESPI_DN_TXDR0, cmd0); |
| |
| if (!espi_wait_ready(espi)) { |
| printk(ESPI_DBG, "eSPI cmd0-cmd3: %08x %08x %08x %08x.\n", |
| cmd0, cmd1, cmd2, cmd3); |
| ASSERT_MSG(0, "eSPI timed out waiting for command to complete\n"); |
| return -1; |
| } |
| status = espi_wait_response(espi); |
| show_espi_status(status); |
| |
| return status; |
| } |
| |
| static uint32_t espi_send_reset(uint8_t *espi) |
| { |
| uint32_t tx_ctl; |
| |
| tx_ctl = ESPI_TX_CMD_RESET; |
| tx_ctl |= ESPI_TX_GO_STATUS; |
| |
| return espi_send_command(espi, tx_ctl, 0, 0, 0); |
| } |
| |
| static uint32_t espi_send_vw_cmd(uint8_t *espi, const struct vw_config_def *vw_config, |
| uint8_t val) |
| { |
| uint32_t tx_ctl, vw_cmd, status; |
| |
| tx_ctl = (0 << 8); /* 1 group in the packet */ |
| tx_ctl |= (0x0 << 4); /* Slave select -- always 0 */ |
| tx_ctl |= ESPI_VW_CMD; |
| tx_ctl |= ESPI_TX_GO_STATUS; |
| |
| vw_cmd = vw_config->index; |
| if (vw_config->valid_bit < 8) |
| vw_cmd |= ((1 << vw_config->valid_bit) << 8); |
| if (vw_config->enable_bit < 8) |
| vw_cmd |= ((val << vw_config->enable_bit) << 8); |
| else |
| vw_cmd |= (val << 8); |
| |
| status = espi_send_command(espi, tx_ctl, 0, 0, vw_cmd); |
| dump_espi_regs(); |
| return status; |
| } |
| |
| static uint32_t espi_get_configuration(uint8_t *espi, uint16_t addr) |
| { |
| uint32_t tx_ctl, config = -1, status; |
| |
| printk(ESPI_EXT_DBG, "Enter %s - addr: 0x%04hx\n", __func__, addr); |
| |
| tx_ctl = addr & 0x0f00; /* HDATA0, Address [15:8]. [15:12]:0h */ |
| tx_ctl |= (0 << 24); /* Reserved, always 0 */ |
| tx_ctl |= (addr & 0xfc) << 16; /* HDATA1, Address [7:0]. [1:0]:0h */ |
| tx_ctl |= (0x0 << 4); /* Slave select, always 0 */ |
| tx_ctl |= ESPI_TX_CMD_GET_CONFIGURATION; |
| tx_ctl |= ESPI_TX_GO_STATUS; |
| |
| status = espi_send_command(espi, tx_ctl, 0, 0, 0); |
| |
| /* Read the response */ |
| if ((status != -1) && (status & ESPI_STATUS_DNCMD) && |
| !(status & ESPI_STATUS_NO_RESPONSE)) { |
| config = read32_espi(espi, ESPI_DN_TXDR1); |
| if (addr == ESPI_SLAVE_GENERAL_CFG) |
| espi_show_slave_configuration(config); |
| else |
| printk(ESPI_DBG, |
| "Get Configuration of reg 0x%04hx returned 0x%08x\n", |
| addr, config); |
| } |
| |
| dump_espi_regs(); |
| |
| return config; |
| } |
| |
| static uint32_t espi_set_configuration(uint8_t *espi, uint16_t addr, uint32_t val) |
| { |
| uint32_t tx_ctl, status; |
| printk(ESPI_EXT_DBG, "Enter %s - addr: 0x%04hx, val 0x%08x\n", __func__, |
| addr, val); |
| |
| tx_ctl = addr & 0x0f00; /* HDATA0, Address [15:8]. [15:12]:0h */ |
| tx_ctl |= (addr & 0xfc) << 16; /* HDATA1, Address [7:0]. [1:0]:0h */ |
| tx_ctl |= (0x0 << 4); /* Slave select -- always 0 */ |
| tx_ctl |= ESPI_TX_CMD_SET_CONFIGURATION; |
| tx_ctl |= ESPI_TX_GO_STATUS; |
| |
| status = espi_send_command(espi, tx_ctl, val, 0, 0); |
| dump_espi_regs(); |
| return status; |
| } |
| |
| static uint32_t espi_wait_channel_ready(uint8_t *espi, uint8_t channel) |
| { |
| struct stopwatch sw; |
| uint32_t config; |
| |
| stopwatch_init_usecs_expire(&sw, 1000); |
| do { |
| config = espi_get_configuration(espi, channel); |
| if (config & ESPI_SLAVE_CHANNEL_READY) |
| return config; |
| } while (!stopwatch_expired(&sw)); |
| |
| return -1; |
| } |
| |
| static bool is_0x2e_0x2f(uintptr_t base) |
| { |
| return ((base & ~0x01) == 0x2e); |
| } |
| |
| static bool is_0x60_0x64(uintptr_t base) |
| { |
| return ((base & ~0x04) == 0x60); |
| } |
| |
| static bool is_0x80(uintptr_t base) |
| { |
| return (base == 0x80); |
| } |
| |
| /* |
| * Contrary to the ESPI_BASE_ADDRESS macro in iomap.h, |
| * this is a not a fixed resource. |
| */ |
| void *espi_read_base_address(void) |
| { |
| uintptr_t spi_espi_bar, espi; |
| |
| spi_espi_bar = pci_read_config32(SOC_LPC_DEV, SPIROM_BASE_ADDRESS_REGISTER); |
| espi = (spi_espi_bar & SPI_BAR_ADDRESS_MASK) + ESPI_OFFSET_FROM_BAR; |
| return (void *)espi; |
| } |
| |
| |
| static void set_frequency(uint32_t req, uint32_t slave_supports, uint32_t *host_cfg_reg, |
| uint32_t *slave_cfg_reg) |
| { |
| if (req == ESPI_OP_FREQ_66_MHZ) { |
| if (slave_supports & ESPI_SLAVE_SUPP_FREQ_66_MHZ) { |
| *host_cfg_reg |= req; |
| *slave_cfg_reg |= ESPI_SLAVE_OP_FREQ_66_MHZ; |
| printk(ESPI_DBG, "Set slave bus to 66MHz\n"); |
| } else { |
| printk(BIOS_WARNING, "Couldn't use 66MHz espi bus speed. Downgrading\n"); |
| req = ESPI_OP_FREQ_33_MHZ; |
| } |
| } |
| if (req == ESPI_OP_FREQ_33_MHZ) { |
| if (slave_supports & ESPI_SLAVE_SUPP_FREQ_33_MHZ) { |
| *host_cfg_reg |= req; |
| *slave_cfg_reg |= ESPI_SLAVE_OP_FREQ_33_MHZ; |
| printk(ESPI_DBG, "Set slave bus to 33MHz\n"); |
| } else { |
| printk(BIOS_WARNING, "Couldn't use 33MHz espi bus speed. Downgrading\n"); |
| req = ESPI_OP_FREQ_16_MHZ; |
| } |
| } |
| if (req == ESPI_OP_FREQ_16_MHZ) { |
| *host_cfg_reg |= ESPI_OP_FREQ_16_MHZ; /* SOC version */ |
| *slave_cfg_reg |= ESPI_SLAVE_OP_FREQ_20_MHZ; /* slave version */ |
| printk(ESPI_DBG, "Set slave bus to 20MHz, SOC to 16MHz\n"); |
| } |
| } |
| |
| static void set_bus_width(uint32_t req, uint32_t slave_supports, uint32_t *host_cfg_reg, |
| uint32_t *slave_cfg_reg) |
| { |
| if (req == ESPI_IO_MODE_QUAD) { |
| if (slave_supports & ESPI_SLAVE_QUAD_IO_SUPPORTED) { |
| *host_cfg_reg |= req; |
| *slave_cfg_reg |= ESPI_SLAVE_IO_MODE_QUAD; |
| printk(ESPI_DBG, "Set slave IO Mode to Quad\n"); |
| } else { |
| printk(BIOS_WARNING, "Couldn't use Quad mode IO. Downgrading\n"); |
| req = ESPI_IO_MODE_DUAL; |
| } |
| } |
| if (req == ESPI_IO_MODE_DUAL) { |
| if (slave_supports & ESPI_SLAVE_DUAL_IO_SUPPORTED) { |
| *host_cfg_reg |= req; |
| *slave_cfg_reg |= ESPI_SLAVE_IO_MODE_DUAL; |
| printk(ESPI_DBG, "Set slave IO Mode to Dual\n"); |
| } else { |
| printk(BIOS_WARNING, "Couldn't use Dual mode IO. Downgrading\n"); |
| req = ESPI_IO_MODE_SINGLE; |
| } |
| } |
| if (req == ESPI_IO_MODE_SINGLE) { |
| *host_cfg_reg |= ESPI_IO_MODE_SINGLE; |
| *slave_cfg_reg |= ESPI_SLAVE_IO_MODE_SINGLE; |
| printk(ESPI_DBG, "Set IO Mode to Single\n"); |
| } |
| } |
| |
| static void finalize_channel(uint8_t *espi, uint32_t chan_addr, uint32_t host_chan_en, |
| uint32_t *host_cfg_reg, uint32_t channel_reg) |
| { |
| uint32_t result; |
| /* boot sequence 7) write channel capability */ |
| espi_set_configuration(espi, chan_addr, channel_reg); |
| |
| /* boot sequence 8) wait for channel ready */ |
| if (channel_reg & ESPI_SLAVE_CHANNEL_ENABLE) { |
| result = espi_wait_channel_ready(espi, chan_addr); |
| if (result == -1) { |
| printk(BIOS_ERR, "ESPI Channel %08x did not go ready\n", chan_addr); |
| ASSERT_MSG(0, "ESPI Channel did not go to ready"); |
| |
| /* if the slave failed to go ready, don't enable master */ |
| *host_cfg_reg &= ~host_chan_en; |
| } |
| } |
| |
| /* boot sequence 9) enable channel in master/host */ |
| write32_espi(espi, ESPI_SLAVE0_CONFIG, *host_cfg_reg); |
| } |
| |
| |
| static void setup_vw_channel(uint8_t *espi, const struct espi_config *cfg, |
| uint32_t *host_cfg_reg, uint32_t slave_supports) |
| { |
| /* boot sequence 6) read channel capability */ |
| uint32_t channel_reg = espi_get_configuration(espi, ESPI_SLAVE_VW_CFG); |
| const struct vw_config_def pltrst_deassert = {VW_PLTRST_CONFIG}; |
| |
| if (cfg->virtual_wire_ch_en) { |
| if (slave_supports & ESPI_SLAVE_SUPP_VIRTUAL_WIRE_CH) { |
| *host_cfg_reg |= ESPI_VIRTUAL_WIRE_CH_EN; |
| channel_reg |= ESPI_SLAVE_CHANNEL_ENABLE; |
| |
| /* |
| * size the number of virtual wires allowed |
| * current generation part supports 4b sizing, but espi 1.0 spec |
| * supports 6b sizing. both start with value 0 => actual size of 1. |
| */ |
| uint32_t master_cap = read32_espi(espi, ESPI_MASTER_CAP); |
| uint8_t slave_vw_supported = |
| (channel_reg & ESPI_SLAVE_CHANNEL_SUPP_VW_COUNT) >> |
| ESPI_SLAVE_CHANNEL_SUPP_VW_COUNT_SHIFT; |
| uint8_t host_vw_supported = |
| (master_cap & ESPI_VW_MAX_SIZE) >> ESPI_VW_MAX_SIZE_SHIFT; |
| |
| uint8_t selected_vw_count = |
| min(slave_vw_supported, host_vw_supported); |
| channel_reg |= |
| selected_vw_count << ESPI_SLAVE_CHANNEL_OP_VW_COUNT_SHIFT; |
| } else { |
| ASSERT_MSG(0, "Virtual Wire requested but slave does not support"); |
| } |
| } else { |
| *host_cfg_reg &= ~ESPI_VIRTUAL_WIRE_CH_EN; |
| channel_reg &= ~ESPI_SLAVE_CHANNEL_ENABLE; |
| } |
| |
| finalize_channel(espi, ESPI_SLAVE_VW_CFG, ESPI_VIRTUAL_WIRE_CH_EN, |
| host_cfg_reg, channel_reg); |
| |
| /* send pltrst# deassertion if we have a VW setup */ |
| if (channel_reg & ESPI_SLAVE_CHANNEL_ENABLE) { |
| espi_send_vw_cmd(espi, &pltrst_deassert, 1); |
| } |
| |
| } |
| |
| static void setup_generic_channel(uint8_t *espi, |
| uint32_t enable_requested, |
| uint32_t supported, |
| uint32_t chan_addr, |
| uint32_t extra_channel_bits, |
| uint32_t host_chan_en, |
| uint32_t *host_cfg_reg) |
| { |
| uint32_t channel_reg = espi_get_configuration(espi, chan_addr); |
| if (enable_requested) { |
| if (supported) { |
| *host_cfg_reg |= host_chan_en; |
| channel_reg |= extra_channel_bits | ESPI_SLAVE_CHANNEL_ENABLE; |
| } else { |
| printk(BIOS_ERR, |
| "ESPI Slave channel %08x not supported\n", |
| chan_addr); |
| ASSERT_MSG(0, "espi slave channel not supported"); |
| } |
| } else { |
| *host_cfg_reg &= ~host_chan_en; |
| channel_reg &= ~ESPI_SLAVE_CHANNEL_ENABLE; |
| } |
| |
| finalize_channel(espi, chan_addr, host_chan_en, host_cfg_reg, channel_reg); |
| |
| } |
| |
| static void setup_periph_channel(uint8_t *espi, const struct espi_config *cfg, |
| uint32_t *host_cfg_reg, uint32_t slave_supports) |
| { |
| setup_generic_channel(espi, |
| cfg->peripheral_ch_en, |
| slave_supports & ESPI_SLAVE_SUPP_PERIPHERAL_CH, |
| ESPI_SLAVE_PERIPH_CFG, |
| ESPI_SLAVE_PERIPH_BM_ENABLE, |
| ESPI_PR_CH_EN, |
| host_cfg_reg); |
| } |
| |
| static void setup_oob_channel(uint8_t *espi, const struct espi_config *cfg, |
| uint32_t *host_cfg_reg, uint32_t slave_supports) |
| { |
| setup_generic_channel(espi, |
| cfg->out_of_band_ch_en, |
| slave_supports & ESPI_SLAVE_SUPP_OOB_CH, |
| ESPI_SLAVE_OOB_CFG, |
| 0x0, |
| ESPI_OOB_CH_EN, |
| host_cfg_reg); |
| } |
| |
| static void setup_flash_channel(uint8_t *espi, const struct espi_config *cfg, |
| uint32_t *host_cfg_reg, uint32_t slave_supports) |
| { |
| setup_generic_channel(espi, |
| cfg->flash_ch_en, |
| slave_supports & ESPI_SLAVE_SUPP_FLASH_CH, |
| ESPI_SLAVE_FLASH_CFG, |
| 0x0, |
| ESPI_FLASH_CH_EN, |
| host_cfg_reg); |
| } |
| |
| static void set_generics(const struct espi_config *cfg, uint32_t *cfg_reg, |
| uint32_t *slave_cfg_reg) |
| { |
| /* clear the bits of interest just in case we have been requested to clear them */ |
| *cfg_reg &= ~(ESPI_CRC_CHECKING_EN | ESPI_ALERT_MODE); |
| *slave_cfg_reg &= ~(ESPI_SLAVE_CRC_CHECKING_EN | ESPI_SLAVE_ALERT_MODE); |
| |
| /* now set any bits of interest */ |
| *cfg_reg |= cfg->enable_crc_checking ? ESPI_CRC_CHECKING_EN : 0; |
| *cfg_reg |= cfg->alert_pin_on_io1 ? 0 : ESPI_ALERT_MODE; |
| *slave_cfg_reg |= cfg->enable_crc_checking ? ESPI_SLAVE_CRC_CHECKING_EN : 0; |
| *slave_cfg_reg |= cfg->alert_pin_on_io1 ? 0 : ESPI_SLAVE_ALERT_MODE; |
| } |
| |
| void espi_setup(const struct espi_config *cfg) |
| { |
| uint32_t cfg_reg = 0, global_ctrl_reg; |
| uint8_t *espi = espi_read_base_address(); |
| uint32_t slave_supports, slave_cfg_reg; |
| uint32_t espi_initial_mode = ESPI_OP_FREQ_16_MHZ | ESPI_IO_MODE_SINGLE; |
| |
| |
| /* Set correct initial configuration to talk to the slave |
| boot sequence: 1) Set to 16.7MHz */ |
| write32_espi(espi, ESPI_SLAVE0_CONFIG, espi_initial_mode); |
| |
| /* The resets affects both host and slave devices, so set initial config again |
| boot sequence: 2) send in band reset */ |
| espi_send_reset(espi); |
| write32_espi(espi, ESPI_SLAVE0_CONFIG, espi_initial_mode); |
| |
| /* boot sequence 3) get_config for slave device */ |
| slave_supports = espi_get_configuration(espi, ESPI_SLAVE_GENERAL_CFG); |
| slave_cfg_reg = 0; |
| |
| set_generics(cfg, &cfg_reg, &slave_cfg_reg); |
| set_frequency(cfg->espi_freq_mhz, slave_supports, &cfg_reg, &slave_cfg_reg); |
| set_bus_width(cfg->bus_width, slave_supports, &cfg_reg, &slave_cfg_reg); |
| |
| /* boot sequence 4) write slave device general config */ |
| printk(ESPI_DBG, "Configure slave general cfg\n"); |
| espi_set_configuration(espi, ESPI_SLAVE_GENERAL_CFG, slave_cfg_reg); |
| |
| /* boot sequence 5) set the Master/host Slave0 config */ |
| printk(ESPI_DBG, "Configure host - CRC, IO, Alert, Clk Freq\n"); |
| write32_espi(espi, ESPI_SLAVE0_CONFIG, cfg_reg); /* host config */ |
| |
| /* boot sequence 6-9 channel setup. */ |
| |
| /* Setup polarity before enabling the VW channel so any interrupts |
| received will have the correct polarity. */ |
| write32_espi(espi, ESPI_RXVW_POLARITY, cfg->irq_polarity); |
| |
| /* Set up VW first so we can deassert PLTRST#. */ |
| setup_vw_channel(espi, cfg, &cfg_reg, slave_supports); |
| setup_periph_channel(espi, cfg, &cfg_reg, slave_supports); |
| setup_oob_channel(espi, cfg, &cfg_reg, slave_supports); |
| setup_flash_channel(espi, cfg, &cfg_reg, slave_supports); |
| |
| /* Enable subtractive decode if configured */ |
| global_ctrl_reg = read32_espi(espi, ESPI_GLOBAL_CONTROL_1); |
| if (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; |
| } |
| write32_espi(espi, ESPI_GLOBAL_CONTROL_1, global_ctrl_reg); |
| |
| /* just used for debug */ |
| if (CONFIG(DEBUG_ESPI_INIT)) { |
| espi_get_configuration(espi, ESPI_SLAVE_GENERAL_CFG); |
| for (int i = 0; i < 4; i++) { |
| espi_get_configuration(espi, ESPI_SLAVE_PERIPH_CFG * (i+1)); |
| } |
| espi_show_host_configuration(); |
| } |
| } |
| |
| static int espi_allocate_io(struct espi_resource_allocator *allocation, |
| const struct resource *resource) |
| { |
| if (is_0x2e_0x2f(resource->base) && resource->size == 1) { |
| allocation->enable_0x2e_0x2f = true; |
| return 1; |
| } |
| |
| if (is_0x60_0x64(resource->base) && resource->size == 1) { |
| allocation->enable_0x60_0x64 = true; |
| return 1; |
| } |
| |
| if (is_0x80(resource->base) && resource->size == 1) { |
| allocation->enable_0x80 = true; |
| return 1; |
| } |
| |
| if (allocation->num_io_ranges >= 4) { |
| printk(BIOS_ERR, "ESPI: Out of IO ranges!!!\n"); |
| return 0; |
| } |
| |
| allocation->io_ranges[allocation->num_io_ranges++] = *resource; |
| return 1; |
| } |
| |
| static int espi_allocate_mmio(struct espi_resource_allocator *allocation, |
| const struct resource *resource) |
| { |
| if (allocation->num_mmio_ranges >= 4) { |
| printk(BIOS_ERR, "ESPI: Out of MMIO ranges!!!\n"); |
| return 0; |
| } |
| |
| allocation->mmio_ranges[allocation->num_mmio_ranges++] = *resource; |
| return 1; |
| } |
| |
| static void espi_write_resources(struct espi_resource_allocator *allocation) |
| { |
| uint32_t espi_capabilities, decode_enable = 0; |
| uint8_t *espi = espi_read_base_address(); |
| int i; |
| |
| espi_capabilities = read32(espi); |
| if (espi_capabilities == ~0) |
| printk(BIOS_ERR, "ESPI controller appears to be unpowered!\n"); |
| |
| for (i = 0; i < allocation->num_io_ranges; i++) { |
| if (allocation->io_ranges[i].size > 0x100) { |
| printk(BIOS_EMERG, "Error: IO range has a max size of 0x100\n"); |
| printk(BIOS_EMERG, "IO_RANGE_%d has a size of 0x%04x\n", i, |
| (unsigned int)allocation->io_ranges[i].size); |
| ASSERT_MSG(0, "IO size too big for ESPI"); |
| } |
| |
| decode_enable |= ESPI_DECODE_IO_RANGE_EN(i); |
| write16(espi + ESPI_IO_RANGE_BASE(i), |
| allocation->io_ranges[i].base); |
| write8(espi + ESPI_IO_RANGE_SIZE(i), |
| allocation->io_ranges[i].size - 1); |
| printk(ESPI_DBG, "Enabling eSPI decode of 0x%04x - 0x%04x\n", |
| (unsigned int)allocation->io_ranges[i].base, |
| (unsigned int)(allocation->io_ranges[i].base + |
| allocation->io_ranges[i].size - 1)); |
| } |
| |
| for (i = 0; i < allocation->num_mmio_ranges; i++) { |
| decode_enable |= ESPI_DECODE_MMIO_RANGE_EN(i); |
| write32(espi + ESPI_MMIO_RANGE_BASE(i), |
| allocation->mmio_ranges[i].base); |
| write16(espi + ESPI_MMIO_RANGE_SIZE(i), |
| allocation->mmio_ranges[i].size - 1); |
| printk(ESPI_DBG, "ESPI MMIO range: %08llx, Size: %04llx", |
| allocation->mmio_ranges[i].base, |
| allocation->mmio_ranges[i].size-1); |
| } |
| |
| if (allocation->enable_0x2e_0x2f) { |
| printk(ESPI_DBG, "Enabling eSPI decode of 0x2e - 0x2f\n"); |
| decode_enable |= ESPI_DECODE_IO_0X2E_0X2F_EN; |
| } |
| |
| if (allocation->enable_0x60_0x64) { |
| printk(ESPI_DBG, "Enabling eSPI decode of 0x60 - 0x64\n"); |
| decode_enable |= ESPI_DECODE_IO_0X60_0X64_EN; |
| } |
| |
| if (allocation->enable_0x80) { |
| printk(ESPI_DBG, "Enabling eSPI decode of 0x80\n"); |
| decode_enable |= ESPI_DECODE_IO_0x80_EN; |
| } |
| |
| printk(ESPI_DBG, "ESPI Decode reg: %08x\n", decode_enable); |
| write32(espi + ESPI_DECODE, decode_enable); |
| |
| check_lpc_espi_overlap(); |
| } |
| |
| static int espi_allocate(struct espi_resource_allocator *allocation, |
| const struct resource *resource_linked_list) |
| { |
| int ret, num_total_ranges = 0; |
| const struct resource *res; |
| |
| for (res = resource_linked_list; res; res = res->next) { |
| ret = 0; |
| |
| if (res->flags & IORESOURCE_IO && res->size) |
| ret = espi_allocate_io(allocation, res); |
| else if (res->flags & IORESOURCE_MEM && res->size) |
| ret = espi_allocate_mmio(allocation, res); |
| |
| num_total_ranges += ret; |
| } |
| |
| return num_total_ranges; |
| } |
| |
| /* |
| * Enable resources specified by resource_list. |
| * 'resource_list is a linked' list describing resources to be enabled. Only IO |
| * and MEM resources are decoded. This function is not additive. Any previously |
| * enabled ranges will be nullified. |
| * Intended to be used in early romstage to open up decoding windows to EC and |
| * other eSPI devices needed during boot. |
| * |
| * Returns the total number of resources that have been enabled. |
| */ |
| int espi_enable_resources(const struct resource *resource_linked_list) |
| { |
| struct espi_resource_allocator allocation; |
| int num_total_ranges = 0; |
| |
| memset(&allocation, 0, sizeof(allocation)); |
| num_total_ranges = espi_allocate(&allocation, resource_linked_list); |
| espi_write_resources(&allocation); |
| return num_total_ranges; |
| } |
| |
| /* |
| * Same as above, but also opens IO windows for all probed subordinate devices. |
| * Any previously enabled ranges will be nullified if any values are set here. |
| * Intended to be used as the ops->enable_resources of the eSPI bridge device. |
| */ |
| void espi_enable_children_resources(struct device *espi) |
| { |
| struct espi_resource_allocator allocation; |
| const struct device *child; |
| const struct bus *link; |
| int set = 0; |
| |
| memset(&allocation, 0, sizeof(allocation)); |
| |
| for (link = espi->link_list; link; link = link->next) { |
| for (child = link->children; child; child = child->sibling) { |
| if (!child->enabled) |
| continue; |
| set = 1; |
| espi_allocate(&allocation, child->resource_list); |
| } |
| } |
| |
| if (set) |
| espi_write_resources(&allocation); |
| } |