diff options
-rw-r--r-- | src/soc/intel/common/block/imc/Kconfig | 8 | ||||
-rw-r--r-- | src/soc/intel/common/block/imc/Makefile.mk | 6 | ||||
-rw-r--r-- | src/soc/intel/common/block/imc/imc.c | 198 | ||||
-rw-r--r-- | src/soc/intel/common/block/imc/imclib.h | 25 | ||||
-rw-r--r-- | src/soc/intel/common/block/imc/spd_access.c | 38 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/imc.h | 19 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/Makefile.mk | 7 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/smbuslib.c | 13 |
8 files changed, 292 insertions, 22 deletions
diff --git a/src/soc/intel/common/block/imc/Kconfig b/src/soc/intel/common/block/imc/Kconfig new file mode 100644 index 0000000000..f731954730 --- /dev/null +++ b/src/soc/intel/common/block/imc/Kconfig @@ -0,0 +1,8 @@ +## SPDX-License-Identifier: GPL-2.0-only + +config SOC_INTEL_COMMON_BLOCK_IMC + bool + depends on ECAM_MMCONF_SUPPORT + default n + help + Select this to access SPD data through Integrated Memory Controller. diff --git a/src/soc/intel/common/block/imc/Makefile.mk b/src/soc/intel/common/block/imc/Makefile.mk new file mode 100644 index 0000000000..0c074a33fc --- /dev/null +++ b/src/soc/intel/common/block/imc/Makefile.mk @@ -0,0 +1,6 @@ +## SPDX-License-Identifier: GPL-2.0-only + +ifeq ($(CONFIG_SOC_INTEL_COMMON_BLOCK_IMC),y) +romstage-y += imc.c +romstage-y += spd_access.c +endif diff --git a/src/soc/intel/common/block/imc/imc.c b/src/soc/intel/common/block/imc/imc.c new file mode 100644 index 0000000000..ccc3d5c6fb --- /dev/null +++ b/src/soc/intel/common/block/imc/imc.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <commonlib/bsd/cb_err.h> +#include <console/console.h> +#include <delay.h> +#include <device/mmio.h> +#include <device/pci_ops.h> +#include <intelblocks/imc.h> +#include <soc/pci_devs.h> +#include <stdbool.h> +#include <timer.h> + +#include "imclib.h" + +#define IMC_SMBUS_TIMEOUT_MS 100 + +#define SMB_CMD_CFG 0x80 +#define SMB_CKOVRD BIT(29) +#define SMB_DIS_WRT BIT(28) +#define SMB_SOFT_RST BIT(24) +#define SMB_TSOD_POLL_EN BIT(20) +#define SMB_CMD_TRIGGER BIT(19) +#define SMB_WORD_ACCESS BIT(17) +#define SMB_WRT_READ (0 << 15) +#define SMB_WRT_WRITE BIT(15) +#define SMB_DTI_MASK (7 << 11) +#define SMB_CMD_SA_SHIFT 8 +#define SMB_CMD_BA_SHIFT 0 +#define SMB_CMD_DTI_SHIFT 11 +#define SMB_STATUS_CFG 0x84 +#define SMB_SBE BIT(1) +#define SMB_BUSY BIT(0) +#define SMB_DATA_CFG 0x88 +#define SMB_PERIOD_CFG 0x90 +#define SMB_CLOCK_PERIOD_400K 250 /* Clock period for 400K. */ +#define SMB_CLOCK_OFFSET_400K 35 /* Clock offset for 400K. */ + +static void imc_spd_smbus_reset(pci_devfn_t dev) +{ + uint32_t cmd; + + cmd = pci_read_config32(dev, SMB_CMD_CFG); + cmd &= ~SMB_CKOVRD; + cmd |= SMB_SOFT_RST; + pci_write_config32(dev, SMB_CMD_CFG, cmd); + + mdelay(35); /* See description of `SMB_CKOVRD` field. */ + + cmd = pci_read_config32(dev, SMB_CMD_CFG); + cmd |= SMB_CKOVRD; + cmd &= ~SMB_SOFT_RST; + pci_write_config32(dev, SMB_CMD_CFG, cmd); +} + +void imc_spd_smbus_init(pci_devfn_t dev) +{ + uint32_t status, cmd; + if (pci_read_config16(dev, 0) == 0xffff) { + printk(BIOS_ERR, + "IMC SMBUS controller PCI: %02x:%02x:%02x.%01x isn't present!\n", + PCI_DEV2SEG(dev), PCI_DEV2BUS(dev), PCI_SLOT(PCI_DEV2DEVFN(dev)), + PCI_FUNC(PCI_DEV2DEVFN(dev))); + return; + } + + /* Set SMB CLOCK to 400K to detect DIMM SPDs. */ + pci_write_config32(dev, SMB_PERIOD_CFG, + (SMB_CLOCK_OFFSET_400K << 16) | SMB_CLOCK_PERIOD_400K); + + /* Reset the bus if the first access is busy. */ + status = pci_read_config32(dev, SMB_STATUS_CFG); + if (status & SMB_BUSY) + imc_spd_smbus_reset(dev); + + /* Disable TSOD polling. */ + cmd = pci_read_config32(dev, SMB_CMD_CFG); + cmd &= ~SMB_TSOD_POLL_EN; + pci_write_config32(dev, SMB_CMD_CFG, cmd); +} + +static bool poll_ready(pci_devfn_t dev, uint32_t *status) +{ + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, IMC_SMBUS_TIMEOUT_MS); + + do { + *status = pci_read_config32(dev, SMB_STATUS_CFG); + if ((*status & SMB_BUSY) == 0) + return true; + } while (!stopwatch_expired(&sw)); + + return false; +} + +static bool claim_controller(pci_devfn_t dev) +{ + uint32_t cmd, status; + + cmd = pci_read_config32(dev, SMB_CMD_CFG); + cmd &= ~SMB_TSOD_POLL_EN; + cmd &= ~SMB_DIS_WRT; + pci_write_config32(dev, SMB_CMD_CFG, cmd); + + return poll_ready(dev, &status); +} + +static bool release_controller(pci_devfn_t dev) +{ + uint32_t cmd, status; + + cmd = pci_read_config32(dev, SMB_CMD_CFG); + cmd |= SMB_TSOD_POLL_EN; + pci_write_config32(dev, SMB_CMD_CFG, cmd); + + return poll_ready(dev, &status); +} + +int imc_smbus_spd_xfer(pci_devfn_t dev, uint8_t slave_addr, uint8_t bus_addr, + enum device_type_id dti, enum access_width width, + enum memory_controller_id mcid, enum smbus_command cmd, void *data) +{ + int ret = CB_ERR; + uint32_t cmdbits, stat, databits, data_mask; + uint16_t wdata = 0, rdata = 0; + + /* Slaves addresses are 3 bits length, and bus address is 8 bits length. */ + if (slave_addr > (1 << 7) - 1) { + printk(BIOS_ERR, "Invalid SMBus slave 0x%02x\n", slave_addr); + return CB_ERR; + } + + if (!claim_controller(dev)) { + printk(BIOS_ERR, "Claim controller failed!\n"); + return CB_ERR; + } + + cmdbits = slave_addr << SMB_CMD_SA_SHIFT; + cmdbits |= bus_addr << SMB_CMD_BA_SHIFT; + + if (cmd == IMC_WRITE) { + databits = pci_read_config32(dev, SMB_DATA_CFG); + wdata = (width == IMC_DATA_BYTE ? read8(data) : cpu_to_be16(read16(data))); + databits |= (wdata << 16); + pci_write_config32(dev, SMB_DATA_CFG, databits); + + cmdbits |= SMB_WRT_WRITE; + cmdbits &= ~SMB_DIS_WRT; + } else { + cmdbits |= SMB_WRT_READ; + } + + if (width == IMC_DATA_WORD) { + cmdbits |= SMB_WORD_ACCESS; + data_mask = 0xffff; + } else { + data_mask = 0xff; + } + + cmdbits &= ~SMB_DTI_MASK; + cmdbits |= dti << SMB_CMD_DTI_SHIFT; + cmdbits |= SMB_CKOVRD; + + /* Pull the trigger */ + cmdbits |= SMB_CMD_TRIGGER; + pci_write_config32(dev, SMB_CMD_CFG, cmdbits); + + if (!poll_ready(dev, &stat)) { + printk(BIOS_ERR, "IMC transfer didn't finished for slave 0x%02x\n", slave_addr); + ret = CB_ERR; + goto cleanup; + } + + if (stat & SMB_SBE) { + printk(BIOS_ERR, "IMC SMBUS SBE for slave 0x%02x\n", slave_addr); + ret = CB_ERR; + goto cleanup; + } + + if (cmd == IMC_READ) { + databits = pci_read_config32(dev, SMB_DATA_CFG); + rdata = databits & data_mask; + if (width == IMC_DATA_WORD) + write16(data, be16_to_cpu(rdata)); + else + write8(data, rdata); + } + + ret = CB_SUCCESS; + +cleanup: + if (!release_controller(dev)) { + printk(BIOS_ERR, "Release controller failed!\n"); + return CB_ERR; + } + + return ret; +} diff --git a/src/soc/intel/common/block/imc/imclib.h b/src/soc/intel/common/block/imc/imclib.h new file mode 100644 index 0000000000..2b0989e71f --- /dev/null +++ b/src/soc/intel/common/block/imc/imclib.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SOC_INTEL_COMMON_BLOCK_IMC_IMCLIB_H +#define SOC_INTEL_COMMON_BLOCK_IMC_IMCLIB_H + +#include <device/pci_type.h> + +enum smbus_command { IMC_READ, IMC_WRITE }; + +enum access_width { IMC_DATA_BYTE, IMC_DATA_WORD }; + +enum memory_controller_id { IMC_CONTROLLER_ID0, IMC_CONTROLLER_ID1 }; + +enum device_type_id { + IMC_DEVICE_TSOD = 0x3, + IMC_DEVICE_WP_EEPROM = 0x6, + IMC_DEVICE_EEPROM = 0xa +}; + +/* Initiate SMBus/I2C transaction to DIMM EEPROM */ +int imc_smbus_spd_xfer(pci_devfn_t dev, uint8_t slave_addr, uint8_t bus_addr, + enum device_type_id dti, enum access_width width, + enum memory_controller_id mcid, enum smbus_command cmd, void *data); + +#endif diff --git a/src/soc/intel/common/block/imc/spd_access.c b/src/soc/intel/common/block/imc/spd_access.c new file mode 100644 index 0000000000..31cc484f95 --- /dev/null +++ b/src/soc/intel/common/block/imc/spd_access.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <commonlib/bsd/cb_err.h> +#include <intelblocks/imc.h> +#include <soc/pci_devs.h> +#include <spd_bin.h> + +#include "imclib.h" + +int spd_read_byte(u8 slave_addr, u8 bus_addr) +{ + uint8_t value; + + if (imc_smbus_spd_xfer(IMC_SPD_DEV, slave_addr, bus_addr, IMC_DEVICE_EEPROM, IMC_DATA_BYTE, + IMC_CONTROLLER_ID0, IMC_READ, &value) == CB_SUCCESS) { + return value; + } + + return -1; +} + +int spd_read_word(u8 slave_addr, u8 bus_addr) +{ + uint16_t value = 0; + + if (imc_smbus_spd_xfer(IMC_SPD_DEV, slave_addr, bus_addr, IMC_DEVICE_EEPROM, IMC_DATA_WORD, + IMC_CONTROLLER_ID0, IMC_READ, &value) == CB_SUCCESS) { + return value; + } + + return -1; +} + +void spd_write_byte(u8 slave_addr, u8 bus_addr, u8 value) +{ + imc_smbus_spd_xfer(IMC_SPD_DEV, slave_addr, bus_addr, IMC_DEVICE_WP_EEPROM, + IMC_DATA_BYTE, IMC_CONTROLLER_ID0, IMC_WRITE, &value); +} diff --git a/src/soc/intel/common/block/include/intelblocks/imc.h b/src/soc/intel/common/block/include/intelblocks/imc.h index 1607794425..50ae6df757 100644 --- a/src/soc/intel/common/block/include/intelblocks/imc.h +++ b/src/soc/intel/common/block/include/intelblocks/imc.h @@ -1,25 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#include <device/pci.h> -#include <stdint.h> +#include <device/pci_type.h> #ifndef SOC_INTEL_COMMON_BLOCK_IMC_H #define SOC_INTEL_COMMON_BLOCK_IMC_H -enum smbus_command { IMC_READ, IMC_WRITE }; +void imc_spd_smbus_init(pci_devfn_t dev); -enum access_width { IMC_DATA_BYTE, IMC_DATA_WORD }; - -enum memory_controller_id { IMC_CONTROLLER_ID0 = 0, IMC_CONTROLLER_ID1 }; - -enum device_type_id { - IMC_DEVICE_TSOD = 0x3, - IMC_DEVICE_WP_EEPROM = 0x6, - IMC_DEVICE_EEPROM = 0xa -}; - -/* Initiate SMBus/I2C transaction to DIMM EEPROM */ -int imc_smbus_spd_xfer(pci_devfn_t dev, uint8_t slave_addr, uint8_t bus_addr, - enum device_type_id dti, enum access_width width, - enum memory_controller_id mcid, enum smbus_command cmd, void *data); #endif diff --git a/src/soc/intel/common/block/smbus/Makefile.mk b/src/soc/intel/common/block/smbus/Makefile.mk index dd568144e5..2ef3a5b752 100644 --- a/src/soc/intel/common/block/smbus/Makefile.mk +++ b/src/soc/intel/common/block/smbus/Makefile.mk @@ -1,10 +1,8 @@ ## SPDX-License-Identifier: GPL-2.0-only -bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += spd_access.c bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbuslib.c bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbus_early.c bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_TCO) += tco.c -romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += spd_access.c romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbuslib.c romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbus_early.c romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_TCO) += tco.c @@ -15,3 +13,8 @@ ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_TCO) += tco.c postcar-$(CONFIG_SOC_INTEL_COMMON_BLOCK_TCO) += tco.c smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_TCO) += tco.c verstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_TCO) += tco.c + +ifneq ($(CONFIG_SOC_INTEL_COMMON_BLOCK_IMC),y) +bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += spd_access.c +romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += spd_access.c +endif diff --git a/src/soc/intel/common/block/smbus/smbuslib.c b/src/soc/intel/common/block/smbus/smbuslib.c index deb09b390d..cf60e64c1d 100644 --- a/src/soc/intel/common/block/smbus/smbuslib.c +++ b/src/soc/intel/common/block/smbus/smbuslib.c @@ -52,7 +52,9 @@ static int get_spd(u8 *spd, u8 addr) return -1; } - if (i2c_eeprom_read(addr, 0, SPD_PAGE_LEN, spd) < 0) { + /* IMC doesn't support i2c eeprom read. */ + if (CONFIG(SOC_INTEL_COMMON_BLOCK_IMC) || + i2c_eeprom_read(addr, 0, SPD_PAGE_LEN, spd) < 0) { printk(BIOS_INFO, "do_i2c_eeprom_read failed, using fallback\n"); spd_read(spd, addr); } @@ -62,7 +64,9 @@ static int get_spd(u8 *spd, u8 addr) /* Switch to page 1 */ spd_write_byte(SPD_PAGE_1, 0, 0); - if (i2c_eeprom_read(addr, 0, SPD_PAGE_LEN, spd + SPD_PAGE_LEN) < 0) { + /* IMC doesn't support i2c eeprom read. */ + if (CONFIG(SOC_INTEL_COMMON_BLOCK_IMC) || + i2c_eeprom_read(addr, 0, SPD_PAGE_LEN, spd + SPD_PAGE_LEN) < 0) { printk(BIOS_INFO, "do_i2c_eeprom_read failed, using fallback\n"); spd_read(spd + SPD_PAGE_LEN, addr); } @@ -78,7 +82,10 @@ void get_spd_smbus(struct spd_block *blk) { u8 i; for (i = 0 ; i < CONFIG_DIMM_MAX; i++) { - if (blk->addr_map[i] == 0) { + /** + * Slave address 0 is also available for IMC based SPD SMBus. + */ + if (!CONFIG(SOC_INTEL_COMMON_BLOCK_IMC) && blk->addr_map[i] == 0) { blk->spd_array[i] = NULL; continue; } |