src/cpu/power9: add file structure for power9, implement SCOM access
Change-Id: Ib555ce51294c94b22d9a7c0db84d38d7928f7015 Signed-off-by: Igor Bagnucki <igor.bagnucki@3mdeb.com> Signed-off-by: Krystian Hebel <krystian.hebel@3mdeb.com> Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/57078 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Michał Żygowski <michal.zygowski@3mdeb.com>
This commit is contained in:
committed by
Felix Held
parent
3c00c7ec6b
commit
252fc29d1a
@@ -7,6 +7,7 @@ subdirs-y += intel
|
||||
subdirs-y += ti
|
||||
subdirs-$(CONFIG_ARCH_X86) += x86
|
||||
subdirs-$(CONFIG_CPU_QEMU_X86) += qemu-x86
|
||||
subdirs-$(CONFIG_CPU_POWER9) += power9
|
||||
|
||||
$(eval $(call create_class_compiler,cpu_microcode,x86_32))
|
||||
################################################################################
|
||||
|
||||
8
src/cpu/power9/Kconfig
Normal file
8
src/cpu/power9/Kconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_POWER9
|
||||
bool
|
||||
select ARCH_BOOTBLOCK_PPC64
|
||||
select ARCH_VERSTAGE_PPC64
|
||||
select ARCH_ROMSTAGE_PPC64
|
||||
select ARCH_RAMSTAGE_PPC64
|
||||
6
src/cpu/power9/Makefile.inc
Normal file
6
src/cpu/power9/Makefile.inc
Normal file
@@ -0,0 +1,6 @@
|
||||
## SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
ramstage-y += power9.c
|
||||
|
||||
bootblock-y += scom.c
|
||||
romstage-y += scom.c
|
||||
20
src/cpu/power9/power9.c
Normal file
20
src/cpu/power9/power9.c
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cpu/cpu.h>
|
||||
#include <device/device.h>
|
||||
|
||||
static void power9_cpu_init(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static struct device_operations cpu_dev_ops = {
|
||||
.init = power9_cpu_init,
|
||||
};
|
||||
|
||||
static const struct cpu_driver driver __cpu_driver = {
|
||||
.ops = &cpu_dev_ops,
|
||||
};
|
||||
|
||||
struct chip_operations cpu_power8_qemu_ops = {
|
||||
CHIP_NAME("POWER9 CPU")
|
||||
};
|
||||
135
src/cpu/power9/scom.c
Normal file
135
src/cpu/power9/scom.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cpu/power/scom.h>
|
||||
#include <cpu/power/spr.h> // HMER
|
||||
#include <console/console.h>
|
||||
|
||||
#define XSCOM_DATA_IND_READ PPC_BIT(0)
|
||||
#define XSCOM_DATA_IND_COMPLETE PPC_BIT(32)
|
||||
#define XSCOM_DATA_IND_ERR PPC_BITMASK(33, 35)
|
||||
#define XSCOM_DATA_IND_DATA PPC_BITMASK(48, 63)
|
||||
#define XSCOM_DATA_IND_FORM1_DATA PPC_BITMASK(12, 63)
|
||||
#define XSCOM_IND_MAX_RETRIES 10
|
||||
|
||||
#define XSCOM_RCVED_STAT_REG 0x00090018
|
||||
#define XSCOM_LOG_REG 0x00090012
|
||||
#define XSCOM_ERR_REG 0x00090013
|
||||
|
||||
uint64_t read_scom_direct(uint64_t reg_address)
|
||||
{
|
||||
uint64_t val;
|
||||
uint64_t hmer = 0;
|
||||
do {
|
||||
/*
|
||||
* Clearing HMER on every SCOM access seems to slow down CCS up
|
||||
* to a point where it starts hitting timeout on "less ideal"
|
||||
* DIMMs for write centering. Clear it only if this do...while
|
||||
* executes more than once.
|
||||
*/
|
||||
if ((hmer & SPR_HMER_XSCOM_STATUS) == SPR_HMER_XSCOM_OCCUPIED)
|
||||
clear_hmer();
|
||||
|
||||
eieio();
|
||||
asm volatile(
|
||||
"ldcix %0, %1, %2" :
|
||||
"=r"(val) :
|
||||
"b"(MMIO_GROUP0_CHIP0_SCOM_BASE_ADDR),
|
||||
"r"(reg_address << 3));
|
||||
eieio();
|
||||
hmer = read_hmer();
|
||||
} while ((hmer & SPR_HMER_XSCOM_STATUS) == SPR_HMER_XSCOM_OCCUPIED);
|
||||
|
||||
if (hmer & SPR_HMER_XSCOM_STATUS) {
|
||||
reset_scom_engine();
|
||||
/*
|
||||
* All F's are returned in case of error, but code polls for a set bit
|
||||
* after changes that can make such error appear (e.g. clock settings).
|
||||
* Return 0 so caller won't have to test for all F's in that case.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_scom_direct(uint64_t reg_address, uint64_t data)
|
||||
{
|
||||
uint64_t hmer = 0;
|
||||
do {
|
||||
/* See comment in read_scom_direct() */
|
||||
if ((hmer & SPR_HMER_XSCOM_STATUS) == SPR_HMER_XSCOM_OCCUPIED)
|
||||
clear_hmer();
|
||||
|
||||
eieio();
|
||||
asm volatile(
|
||||
"stdcix %0, %1, %2"::
|
||||
"r"(data),
|
||||
"b"(MMIO_GROUP0_CHIP0_SCOM_BASE_ADDR),
|
||||
"r"(reg_address << 3));
|
||||
eieio();
|
||||
hmer = read_hmer();
|
||||
} while ((hmer & SPR_HMER_XSCOM_STATUS) == SPR_HMER_XSCOM_OCCUPIED);
|
||||
|
||||
if (hmer & SPR_HMER_XSCOM_STATUS)
|
||||
reset_scom_engine();
|
||||
}
|
||||
|
||||
void write_scom_indirect(uint64_t reg_address, uint64_t value)
|
||||
{
|
||||
uint64_t addr;
|
||||
uint64_t data;
|
||||
addr = reg_address & 0x7FFFFFFF;
|
||||
data = reg_address & XSCOM_ADDR_IND_ADDR;
|
||||
data |= value & XSCOM_ADDR_IND_DATA;
|
||||
|
||||
write_scom_direct(addr, data);
|
||||
|
||||
for (int retries = 0; retries < XSCOM_IND_MAX_RETRIES; ++retries) {
|
||||
data = read_scom_direct(addr);
|
||||
if ((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) {
|
||||
return;
|
||||
} else if (data & XSCOM_DATA_IND_COMPLETE) {
|
||||
printk(BIOS_EMERG, "SCOM WR error %16.16llx = %16.16llx : %16.16llx\n",
|
||||
reg_address, value, data);
|
||||
}
|
||||
// TODO: delay?
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t read_scom_indirect(uint64_t reg_address)
|
||||
{
|
||||
uint64_t addr;
|
||||
uint64_t data;
|
||||
addr = reg_address & 0x7FFFFFFF;
|
||||
data = XSCOM_DATA_IND_READ | (reg_address & XSCOM_ADDR_IND_ADDR);
|
||||
|
||||
write_scom_direct(addr, data);
|
||||
|
||||
for (int retries = 0; retries < XSCOM_IND_MAX_RETRIES; ++retries) {
|
||||
data = read_scom_direct(addr);
|
||||
if ((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) {
|
||||
break;
|
||||
} else if (data & XSCOM_DATA_IND_COMPLETE) {
|
||||
printk(BIOS_EMERG, "SCOM RD error %16.16llx : %16.16llx\n",
|
||||
reg_address, data);
|
||||
}
|
||||
// TODO: delay?
|
||||
}
|
||||
|
||||
return data & XSCOM_DATA_IND_DATA;
|
||||
}
|
||||
|
||||
/* This function should be rarely called, don't make it inlined */
|
||||
void reset_scom_engine(void)
|
||||
{
|
||||
/*
|
||||
* With cross-CPU SCOM accesses, first register should be cleared on the
|
||||
* executing CPU, the other two on target CPU. In that case it may be
|
||||
* necessary to do the remote writes in assembly directly to skip checking
|
||||
* HMER and possibly end in a loop.
|
||||
*/
|
||||
write_scom_direct(XSCOM_RCVED_STAT_REG, 0);
|
||||
write_scom_direct(XSCOM_LOG_REG, 0);
|
||||
write_scom_direct(XSCOM_ERR_REG, 0);
|
||||
clear_hmer();
|
||||
eieio();
|
||||
}
|
||||
Reference in New Issue
Block a user