summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/intel/common/block/imc/Kconfig8
-rw-r--r--src/soc/intel/common/block/imc/Makefile.mk6
-rw-r--r--src/soc/intel/common/block/imc/imc.c198
-rw-r--r--src/soc/intel/common/block/imc/imclib.h25
-rw-r--r--src/soc/intel/common/block/imc/spd_access.c38
-rw-r--r--src/soc/intel/common/block/include/intelblocks/imc.h19
-rw-r--r--src/soc/intel/common/block/smbus/Makefile.mk7
-rw-r--r--src/soc/intel/common/block/smbus/smbuslib.c13
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;
}