common/block/imc: Add Integrated Memory Controller (IMC) driver
IMC is found on certain Xeon processors. On such platforms SPDs are not connected to SMBus on PCH but to dedicated IMC-owned pins. The purpose of this driver is to expose access to the i2c/smbus controller associated with IMC. Datasheet used: Intel Xeon Processor D-1500 Product Family, Volume 2, reference 332051-001 This driver is largely based on i2c-imc.c Linux driver. https://lwn.net/Articles/685475/ TEST=single/double reads and single writes on Xeon-D1500. Hardware: Open Compute Project Monolake platform. Signed-off-by: Andrey Petrov <anpetrov@fb.com> Change-Id: Idbcda1c2273b9a5721fcd9470b4de182192779e7 Reviewed-on: https://review.coreboot.org/c/coreboot/+/34678 Reviewed-by: David Hendricks <david.hendricks@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
committed by
Martin Roth
parent
c0193c9237
commit
f377fafd94
7
src/soc/intel/common/block/imc/Kconfig
Normal file
7
src/soc/intel/common/block/imc/Kconfig
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
config SOC_INTEL_COMMON_BLOCK_IMC
|
||||||
|
bool
|
||||||
|
depends on MMCONF_SUPPORT
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Driver for communication with Integrated Memory Controller that is found on
|
||||||
|
some Xeon server processors.
|
9
src/soc/intel/common/block/imc/Makefile.inc
Normal file
9
src/soc/intel/common/block/imc/Makefile.inc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
ifeq ($(CONFIG_SOC_INTEL_COMMON_BLOCK_IMC),y)
|
||||||
|
|
||||||
|
bootblock-y += imc.c
|
||||||
|
romstage-y += imc.c
|
||||||
|
verstage-y += imc.c
|
||||||
|
postcar-y += imc.c
|
||||||
|
ramstage-y += imc.c
|
||||||
|
|
||||||
|
endif
|
171
src/soc/intel/common/block/imc/imc.c
Normal file
171
src/soc/intel/common/block/imc/imc.c
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2019 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Please note: the driver uses MMIO PCIe register access. IO based access will
|
||||||
|
* not work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <device/pci_ops.h>
|
||||||
|
#include <intelblocks/imc.h>
|
||||||
|
#include <soc/pci_devs.h>
|
||||||
|
#include <timer.h>
|
||||||
|
|
||||||
|
#define IMC_SMBUS_TIMEOUT_MS 100
|
||||||
|
#define IMC_SMBCNTL_DTI_TSOD 0x3
|
||||||
|
#define IMC_SMBCNTL_DTI_EEPROM 0xa
|
||||||
|
#define IMC_SMBCNTL_DTI_WP_EEPROM 0x6
|
||||||
|
|
||||||
|
#define SMBSTAT(i) (0x180 + 0x10 * i)
|
||||||
|
#define SMBCMD(i) (0x184 + 0x10 * i)
|
||||||
|
#define SMBCNTL(i) (0x188 + 0x10 * i)
|
||||||
|
|
||||||
|
#define SMBSTAT_RDO (1u << 31) /* Read Data Valid */
|
||||||
|
#define SMBSTAT_WOD (1u << 30) /* Write Operation Done */
|
||||||
|
#define SMBSTAT_SBE (1u << 29) /* SMBus Error */
|
||||||
|
#define SMBSTAT_SMB_BUSY (1u << 28) /* SMBus Busy State */
|
||||||
|
|
||||||
|
#define SMBCMD_TRIGGER (1u << 31) /* CMD Trigger */
|
||||||
|
#define SMBCMD_PNTR_SEL (1u << 30) /* HW polls TSOD with pointer */
|
||||||
|
#define SMBCMD_WORD_ACCESS (1u << 29) /* word (vs byte) access */
|
||||||
|
#define SMBCMD_TYPE_MASK (3u << 27) /* Mask for access type */
|
||||||
|
#define SMBCMD_TYPE_READ (0u << 27) /* Read */
|
||||||
|
#define SMBCMD_TYPE_WRITE (1u << 27) /* Write */
|
||||||
|
#define SMBCMD_TYPE_PNTR_WRITE (3u << 27) /* Write to pointer */
|
||||||
|
#define SMBCMD_SA_MASK (7u << 24) /* Slave Address high bits */
|
||||||
|
#define SMBCMD_SA_SHIFT 24
|
||||||
|
#define SMBCMD_BA_MASK 0xff0000 /* Bus Txn address */
|
||||||
|
#define SMBCMD_BA_SHIFT 16
|
||||||
|
#define SMBCMD_WDATA_MASK 0xffff /* data to write */
|
||||||
|
|
||||||
|
#define SMBCNTL_DTI_MASK 0xf0000000 /* Slave Address low bits */
|
||||||
|
#define SMBCNTL_DTI_SHIFT 28 /* Slave Address low bits */
|
||||||
|
#define SMBCNTL_CKOVRD (1u << 27) /* # Clock Override */
|
||||||
|
#define SMBCNTL_DIS_WRT (1u << 26) /* Disable Write (sadly) */
|
||||||
|
#define SMBCNTL_SOFT_RST (1u << 10) /* Soft Reset */
|
||||||
|
#define SMBCNTL_TSOD_POLL_EN (1u << 8) /* TSOD Polling Enable */
|
||||||
|
|
||||||
|
static bool poll_ready(pci_devfn_t dev, enum memory_controller_id mcid, uint32_t *status)
|
||||||
|
{
|
||||||
|
struct stopwatch sw;
|
||||||
|
|
||||||
|
stopwatch_init_msecs_expire(&sw, IMC_SMBUS_TIMEOUT_MS);
|
||||||
|
|
||||||
|
do {
|
||||||
|
*status = pci_mmio_read_config32(dev, SMBSTAT(mcid));
|
||||||
|
if (!(*status & SMBSTAT_SMB_BUSY))
|
||||||
|
break;
|
||||||
|
} while (!stopwatch_expired(&sw));
|
||||||
|
|
||||||
|
return (!(*status & SMBSTAT_SMB_BUSY));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool claim_controller(pci_devfn_t dev, enum memory_controller_id mcid)
|
||||||
|
{
|
||||||
|
uint32_t cntl, status;
|
||||||
|
|
||||||
|
cntl = pci_mmio_read_config32(dev, SMBCNTL(mcid));
|
||||||
|
cntl &= ~SMBCNTL_TSOD_POLL_EN;
|
||||||
|
cntl &= ~SMBCNTL_DIS_WRT;
|
||||||
|
pci_mmio_write_config32(dev, SMBCNTL(mcid), cntl);
|
||||||
|
|
||||||
|
return poll_ready(dev, mcid, &status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void release_controller(pci_devfn_t dev, enum memory_controller_id mcid)
|
||||||
|
{
|
||||||
|
uint32_t cntl, status;
|
||||||
|
|
||||||
|
cntl = pci_mmio_read_config32(dev, SMBCNTL(mcid));
|
||||||
|
cntl |= SMBCNTL_TSOD_POLL_EN;
|
||||||
|
pci_mmio_write_config32(dev, SMBCNTL(mcid), cntl);
|
||||||
|
|
||||||
|
poll_ready(dev, mcid, &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 = -1;
|
||||||
|
uint32_t cmdbits = 0, stat = 0, cntlbits = 0, data_mask = 0;
|
||||||
|
uint16_t wdata = 0, rdata = 0;
|
||||||
|
|
||||||
|
/* slaves addresses are 7 bits length */
|
||||||
|
if (slave_addr > (1 << 7) - 1) {
|
||||||
|
printk(BIOS_ERR, "invalid SMBus address, aborting xfer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!claim_controller(dev, mcid)) {
|
||||||
|
printk(BIOS_ERR, "ayee! couldn't claim controller, giving up xfer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdbits = (slave_addr << SMBCMD_SA_SHIFT);
|
||||||
|
cmdbits |= (bus_addr << SMBCMD_BA_SHIFT);
|
||||||
|
|
||||||
|
if (cmd == IMC_WRITE) {
|
||||||
|
wdata = (width == IMC_DATA_BYTE ? read8(data) : cpu_to_be16(read16(data)));
|
||||||
|
cmdbits |= (SMBCMD_TYPE_WRITE | wdata);
|
||||||
|
} else {
|
||||||
|
cmdbits |= SMBCMD_TYPE_READ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == IMC_DATA_WORD) {
|
||||||
|
cmdbits |= SMBCMD_WORD_ACCESS;
|
||||||
|
data_mask = 0xffff;
|
||||||
|
} else {
|
||||||
|
data_mask = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
cntlbits = pci_mmio_read_config32(dev, SMBCNTL(mcid));
|
||||||
|
cntlbits &= ~SMBCNTL_DTI_MASK;
|
||||||
|
cntlbits |= (dti << SMBCNTL_DTI_SHIFT);
|
||||||
|
|
||||||
|
pci_mmio_write_config32(dev, SMBCNTL(mcid), cntlbits);
|
||||||
|
|
||||||
|
/* Pull the trigger */
|
||||||
|
cmdbits |= SMBCMD_TRIGGER;
|
||||||
|
pci_mmio_write_config32(dev, SMBCMD(mcid), cmdbits);
|
||||||
|
|
||||||
|
if (!poll_ready(dev, mcid, &stat)) {
|
||||||
|
printk(BIOS_ERR, "IMC xfer failed for slave %x", slave_addr);
|
||||||
|
ret = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat & SMBSTAT_SBE) {
|
||||||
|
ret = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd == IMC_READ) {
|
||||||
|
rdata = stat & data_mask;
|
||||||
|
if (width == IMC_DATA_WORD)
|
||||||
|
write16(data, cpu_to_be16(rdata));
|
||||||
|
else
|
||||||
|
write8(data, rdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
cleanup:
|
||||||
|
release_controller(dev, SMBSTAT(mcid));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
38
src/soc/intel/common/block/include/intelblocks/imc.h
Normal file
38
src/soc/intel/common/block/include/intelblocks/imc.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2019 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 <device/pci.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef SOC_INTEL_COMMON_BLOCK_IMC_H
|
||||||
|
#define SOC_INTEL_COMMON_BLOCK_IMC_H
|
||||||
|
|
||||||
|
enum smbus_command { IMC_READ, IMC_WRITE };
|
||||||
|
|
||||||
|
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
|
@@ -26,6 +26,9 @@ config CPU_SPECIFIC_OPTIONS
|
|||||||
select TSC_CONSTANT_RATE
|
select TSC_CONSTANT_RATE
|
||||||
select HAVE_FSP_BIN
|
select HAVE_FSP_BIN
|
||||||
select CPU_INTEL_FIRMWARE_INTERFACE_TABLE
|
select CPU_INTEL_FIRMWARE_INTERFACE_TABLE
|
||||||
|
select SOC_INTEL_COMMON
|
||||||
|
select SOC_INTEL_COMMON_BLOCK
|
||||||
|
select SOC_INTEL_COMMON_BLOCK_IMC
|
||||||
|
|
||||||
config VBOOT
|
config VBOOT
|
||||||
select VBOOT_STARTS_IN_ROMSTAGE
|
select VBOOT_STARTS_IN_ROMSTAGE
|
||||||
|
Reference in New Issue
Block a user