diff options
author | Igor Bagnucki <igor.bagnucki@3mdeb.com> | 2020-12-14 14:52:50 +0100 |
---|---|---|
committer | Felix Held <felix-coreboot@felixheld.de> | 2022-02-11 13:53:29 +0000 |
commit | 252fc29d1afc4c1eb42122ed302a0d25a3331e7c (patch) | |
tree | a4f9f1ca0aead8182b52b7ab53ac4fc547c25fc1 /src/cpu | |
parent | 3c00c7ec6b1a26699f7aab39c6cb702aa16a3d42 (diff) |
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>
Diffstat (limited to 'src/cpu')
-rw-r--r-- | src/cpu/Makefile.inc | 1 | ||||
-rw-r--r-- | src/cpu/power9/Kconfig | 8 | ||||
-rw-r--r-- | src/cpu/power9/Makefile.inc | 6 | ||||
-rw-r--r-- | src/cpu/power9/power9.c | 20 | ||||
-rw-r--r-- | src/cpu/power9/scom.c | 135 |
5 files changed, 170 insertions, 0 deletions
diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc index 9f1e6c4bf5..2d90638bd5 100644 --- a/src/cpu/Makefile.inc +++ b/src/cpu/Makefile.inc @@ -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)) ################################################################################ diff --git a/src/cpu/power9/Kconfig b/src/cpu/power9/Kconfig new file mode 100644 index 0000000000..c3a628cc34 --- /dev/null +++ b/src/cpu/power9/Kconfig @@ -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 diff --git a/src/cpu/power9/Makefile.inc b/src/cpu/power9/Makefile.inc new file mode 100644 index 0000000000..2fe9e57a96 --- /dev/null +++ b/src/cpu/power9/Makefile.inc @@ -0,0 +1,6 @@ +## SPDX-License-Identifier: GPL-2.0-or-later + +ramstage-y += power9.c + +bootblock-y += scom.c +romstage-y += scom.c diff --git a/src/cpu/power9/power9.c b/src/cpu/power9/power9.c new file mode 100644 index 0000000000..fd33ff219b --- /dev/null +++ b/src/cpu/power9/power9.c @@ -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") +}; diff --git a/src/cpu/power9/scom.c b/src/cpu/power9/scom.c new file mode 100644 index 0000000000..e55d149bff --- /dev/null +++ b/src/cpu/power9/scom.c @@ -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(); +} |