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:
parent
c0193c9237
commit
f377fafd94
|
@ -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.
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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 HAVE_FSP_BIN
|
||||
select CPU_INTEL_FIRMWARE_INTERFACE_TABLE
|
||||
select SOC_INTEL_COMMON
|
||||
select SOC_INTEL_COMMON_BLOCK
|
||||
select SOC_INTEL_COMMON_BLOCK_IMC
|
||||
|
||||
config VBOOT
|
||||
select VBOOT_STARTS_IN_ROMSTAGE
|
||||
|
|
Loading…
Reference in New Issue