coreboot/util/amdtool/spi.c
Michał Żygowski 7436c59875 util/amdtool: Add support for Phoenix AM5 CPUs
Add register tables and device IDs for Phoenix AM5 desktop CPUs.

TEST=Dump all data with amdtool on MSI PRO B650M-A.

Change-Id: Ia7af9194fb7516e98b7cddee2bfc65af12d56dc0
Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/90009
Reviewed-by: Michał Kopeć <michal.kopec@3mdeb.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2025-11-18 13:30:29 +00:00

424 lines
11 KiB
C

/* amdtool - dump all registers on an AMD CPU + chipset based system */
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdio.h>
#include <stdlib.h>
#include "amdtool.h"
#include "smn.h"
#define AMD_FCH_SPIBAR_OFFSET 0xa0
#define SPI_SMN_BASE 0x2DC4000
static const io_register_t spi100_speed_cfg_fields[] = {
{ 0x0, 4, "TPM Speed" },
{ 0x4, 4, "Alt Speed" },
{ 0x8, 4, "Fast Read Speed" },
{ 0xc, 4, "Normal Speed" },
};
static const io_register_t spi100_host_prefetch_cfg_fields[] = {
{ 0x0, 7, "Host Prefetch Size" },
{ 0x7, 1, "Host Prefetch on 64B Boudnary" },
{ 0x8, 4, "Host Hit Range" },
{ 0xc, 1, "Host Will Hit Enable" },
{ 0xd, 1, "Host Hit Soon Enable" },
{ 0xe, 1, "Host Burst Enable" },
{ 0xf, 1, "Host Burst to 4DW Enable" },
};
static const io_register_t spi_cntl0_fields[] = {
{ 0x0, 8, "Spi OpCode (Reserved)" },
{ 0x8, 1, "Disable Index Retry" },
{ 0x9, 1, "Index Cacheline Stop" },
{ 0xA, 2, "ROM Protection Compare Range" },
{ 0x12, 1, "SPI Read Mode [0]" },
{ 0x15, 1, "Illegal Access" },
{ 0x16, 1, "SPI MAC Access ROM Enable" },
{ 0x17, 1, "SPI Host Access ROM Enable" },
{ 0x1c, 1, "SPI Clock Gate" },
{ 0x1d, 1, "SPI Read Mode [1]" },
{ 0x1e, 1, "SPI Read Mode [2]" },
{ 0x1f, 1, "SPI Busy" },
};
static const io_register_t kunlun_alt_spi_cs_fields[] = {
{ 0x0, 2, "Alt SPI CS Enable" },
{ 0x3, 1, "SPI Protect Enable 0" },
{ 0x5, 1, "SPI Protect Lock" },
{ 0x6, 1, "Lock SPI CS" },
};
static const io_register_t tacoma_alt_spi_cs_fields[] = {
{ 0x0, 2, "Alt SPI CS Enable" },
{ 0x3, 1, "SPI Protect Enable 0" },
{ 0x5, 1, "SPI Protect Lock" },
};
static const char * const spi100_speed_values[] = {
"66.66 MHz",
"33.33 MHz",
"22.22 MHz",
"16.66 Mhz",
"100 MHz",
"800 KHz",
"Defined in SPIx6C[5:0]",
"Defined in SPIx6C[13:8]"
};
static const io_register_t spi_bar_registers[] = {
{ 0x00, 4, "SPI Control 0" },
{ 0x04, 4, "SPI Restricted Command" },
{ 0x08, 4, "SPI Restricted Command 2" },
{ 0x0C, 4, "SPI Control 1" },
{ 0x10, 4, "ESPI Control" },
{ 0x14, 4, "SPI Command Value 1" },
{ 0x18, 4, "SPI Command Value 2" },
{ 0x1C, 1, "ROMCP CS" },
{ 0x1D, 1, "SPI ALT CS" },
{ 0x20, 4, "SPI100 Enable" },
{ 0x24, 4, "SPI100 Precyc0" },
{ 0x28, 4, "SPI100 Precyc1" },
{ 0x2C, 2, "SPI100 Host Prefetch Config" },
{ 0x2E, 2, "TPM SPI DI Timeout" },
{ 0x30, 2, "ROM2 Address Override" },
{ 0x32, 2, "SPI100 Dummy Cycle Config" },
{ 0x32, 2, "SPI100 Rx Timing Config" },
{ 0x40, 1, "DDR Command Code" },
{ 0x41, 1, "QDR Command Code" },
{ 0x42, 1, "DPR Command Code" },
{ 0x43, 1, "QPR Command Code" },
{ 0x44, 1, "Mode Byte" },
{ 0x50, 4, "Addr32 Control 0" },
{ 0x54, 4, "Addr32 Control 1" },
{ 0x58, 4, "Addr32 Control 2" },
{ 0x5C, 4, "Addr32 Control 3" },
{ 0x60, 4, "BAR 64MB ROM3 Low" },
{ 0x64, 4, "BAR 64MB ROM3 High" },
{ 0x68, 4, "SPI Restricted Command 3" },
{ 0x6C, 4, "SPI Speed" },
{ 0x70, 4, "Host Interrupt" },
{ 0x74, 4, "LPC SPI Interrupt" },
{ 0x78, 4, "Flash Idle COunt" },
{ 0xFC, 2, "SPI Misc Control" },
};
int use_smn_access = 0;
volatile uint8_t *spibar = NULL;
uint32_t spi_smn_base = 0;
static uint8_t spi_read8(uint32_t offset)
{
if (use_smn_access)
return smn_read8(spi_smn_base + offset);
else
return read8(spibar + offset);
}
static uint16_t spi_read16(uint32_t offset)
{
if (use_smn_access)
return smn_read16(spi_smn_base + offset);
else
return read16(spibar + offset);
}
static uint32_t spi_read32(uint32_t offset)
{
if (use_smn_access)
return smn_read32(spi_smn_base + offset);
else
return read32(spibar + offset);
}
static int print_spi_cntrl0(struct pci_dev *sb)
{
int i, size = 0;
int smbus_rev = 0;
uint32_t spi_cntrl0 = UINT32_MAX;
const io_register_t *spi_cntrl_register = NULL;
printf("\n");
switch (sb->device_id) {
case PCI_DEVICE_ID_AMD_FCH_LPC_2:
smbus_rev = find_smbus_dev_rev(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FCH_SMB_2);
if (smbus_rev == -1)
return 1;
switch (smbus_rev) {
case 0x71:
spi_cntrl0 = spi_read32(0);
spi_cntrl_register = spi_cntl0_fields;
size = ARRAY_SIZE(spi_cntl0_fields);
break;
default:
printf("Error: Dumping SPI CNTRL on this southbridge is not (yet) supported.\n");
return 1;
}
break;
default:
printf("Error: Dumping SPI CNTRL on this southbridge is not (yet) supported.\n");
return 1;
}
printf("SPI_CNTRL0 = 0x%08x\n\n", spi_cntrl0);
if (spi_cntrl_register) {
for (i = 0; i < size; i++) {
unsigned int val = spi_cntrl0 >> spi_cntrl_register[i].addr;
val &= ((1 << spi_cntrl_register[i].size) -1);
printf("0x%04x = %s\n", val, spi_cntrl_register[i].name);
}
}
return 0;
}
static int print_spi_alt_cs(struct pci_dev *sb, struct pci_dev *nb)
{
int i, size = 0;
int smbus_rev = 0;
uint8_t spi_alt_cs = 0xff;
const io_register_t *spi_alt_cs_register = NULL;
printf("\n");
switch (sb->device_id) {
case PCI_DEVICE_ID_AMD_FCH_LPC_2:
smbus_rev = find_smbus_dev_rev(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FCH_SMB_2);
if (smbus_rev == -1)
return 1;
switch (smbus_rev) {
case 0x71:
switch (nb->device_id) {
case PCI_DEVICE_ID_AMD_BRH_ROOT_COMPLEX:
spi_alt_cs = spi_read8(0x1d);
spi_alt_cs_register = kunlun_alt_spi_cs_fields;
size = ARRAY_SIZE(kunlun_alt_spi_cs_fields);
break;
case PCI_DEVICE_ID_AMD_PHX_ROOT_COMPLEX:
spi_alt_cs = spi_read8(0x1d);
spi_alt_cs_register = tacoma_alt_spi_cs_fields;
size = ARRAY_SIZE(tacoma_alt_spi_cs_fields);
break;
default:
goto alt_cs_err;
}
break;
default:
goto alt_cs_err;
}
break;
default:
goto alt_cs_err;
}
printf("SPI_ALT_CS = 0x%02x\n\n", spi_alt_cs);
if (spi_alt_cs_register) {
for (i = 0; i < size; i++) {
unsigned int val = spi_alt_cs >> spi_alt_cs_register[i].addr;
val &= ((1 << spi_alt_cs_register[i].size) -1);
printf("0x%04x = %s\n", val, spi_alt_cs_register[i].name);
}
}
return 0;
alt_cs_err:
printf("Error: Dumping SPI_ALT_CS on this southbridge is not (yet) supported.\n");
return 1;
}
static int print_spi_speed_config(struct pci_dev *sb)
{
size_t i, size = 0, values_size = 0;
uint16_t spi_speed_cfg = UINT16_MAX;
const io_register_t *spi_speed_cfg_register = NULL;
const char * const *speed_values = NULL;
int smbus_rev = 0;
printf("\n");
switch (sb->device_id) {
case PCI_DEVICE_ID_AMD_FCH_LPC_2:
smbus_rev = find_smbus_dev_rev(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FCH_SMB_2);
if (smbus_rev == -1)
return 1;
switch (smbus_rev) {
case 0x71:
spi_speed_cfg = spi_read16(0x22);
spi_speed_cfg_register = spi100_speed_cfg_fields;
size = ARRAY_SIZE(spi100_speed_cfg_fields);
speed_values = spi100_speed_values;
values_size = ARRAY_SIZE(spi100_speed_values);
break;
default:
printf("Error: Dumping SPI100_SPEED_CFG on this southbridge is not (yet) supported.\n");
return 1;
}
break;
default:
printf("Error: Dumping SPI100_SPEED_CFG on this southbridge is not (yet) supported.\n");
return 1;
}
printf("SPI100_SPEED_CFG = 0x%04x\n\n", spi_speed_cfg);
if (spi_speed_cfg_register && speed_values) {
for (i = 0; i < size; i++) {
unsigned int val = spi_speed_cfg >> spi_speed_cfg_register[i].addr;
val &= ((1 << spi_speed_cfg_register[i].size) -1);
if (val < values_size)
printf("0x%04x = %s (%s)\n", val, spi_speed_cfg_register[i].name,
speed_values[val]);
else
printf("0x%04x = %s (Reserved)\n", val, spi_speed_cfg_register[i].name);
}
}
return 0;
}
static int print_spi_host_prefetch(struct pci_dev *sb)
{
int i, size = 0;
uint16_t spi_host_prefetch = UINT16_MAX;
const io_register_t *spi_host_prefetch_register = NULL;
int smbus_rev = 0;
printf("\n");
switch (sb->device_id) {
case PCI_DEVICE_ID_AMD_FCH_LPC_2:
smbus_rev = find_smbus_dev_rev(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FCH_SMB_2);
if (smbus_rev == -1)
return 1;
switch (smbus_rev) {
case 0x71:
spi_host_prefetch = spi_read16(0x2c);
spi_host_prefetch_register = spi100_host_prefetch_cfg_fields;
size = ARRAY_SIZE(spi100_host_prefetch_cfg_fields);
break;
default:
printf("Error: Dumping SPI100_HOST_PREFETCH_CFG on this southbridge is not (yet) supported.\n");
return 1;
}
break;
default:
printf("Error: Dumping SPI100_HOST_PREFETCH_CFG on this southbridge is not (yet) supported.\n");
return 1;
}
printf("SPI100_HOST_PREFETCH_CFG = 0x%04x\n\n", spi_host_prefetch);
if (spi_host_prefetch_register) {
for (i = 0; i < size; i++) {
unsigned int val = spi_host_prefetch >> spi_host_prefetch_register[i].addr;
val &= ((1 << spi_host_prefetch_register[i].size) - 1);
printf("0x%04x = %s\n", val, spi_host_prefetch_register[i].name);
}
}
return 0;
}
static int print_spibar(struct pci_dev *sb, struct pci_dev *nb)
{
int i, size = 0, spi_size = 0x1000;
uint32_t spibar_phys, spibar_mask;
const io_register_t *spi_register = NULL;
int smbus_rev;
printf("\n============= SPI Bar ==============\n\n");
switch (sb->device_id) {
case PCI_DEVICE_ID_AMD_FCH_LPC_2:
smbus_rev = find_smbus_dev_rev(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FCH_SMB_2);
if (smbus_rev == -1)
return 1;
switch (smbus_rev) {
case 0x71:
size = ARRAY_SIZE(spi_bar_registers);
spi_register = spi_bar_registers;
spibar_mask = 0xffffff00;
spi_smn_base = SPI_SMN_BASE;
break;
default:
printf("Error: Dumping SPI on this southbridge is not (yet) supported.\n");
return 1;
}
break;
default:
printf("Error: Dumping SPI on this southbridge is not (yet) supported.\n");
return 1;
}
spibar_phys = pci_read_long(sb, AMD_FCH_SPIBAR_OFFSET);
if ((spibar_phys & spibar_mask) == 0 || (spibar_phys == 0xffffffff)) {
if (spi_smn_base != 0) {
use_smn_access = 1;
} else {
perror("Error SPIBAR not programmed");
return 1;
}
} else {
if (spibar_phys == UINT32_MAX) {
perror("Error SPIBAR invalid");
return 1;
}
spibar = map_physical(spibar_phys & spibar_mask, spi_size);
if (spibar == NULL) {
perror("Error mapping SPIBAR");
return 1;
}
}
for (i = 0; i < size; i++) {
switch(spi_register[i].size) {
case 1:
printf("0x%08x = %s\n", spi_read8(spi_register[i].addr), spi_register[i].name);
break;
case 2:
printf("0x%08x = %s\n", spi_read16(spi_register[i].addr), spi_register[i].name);
break;
case 4:
printf("0x%08x = %s\n", spi_read32(spi_register[i].addr), spi_register[i].name);
break;
case 8:
printf("0x%08x%08x = %s\n", spi_read32(spi_register[i].addr + 4),
spi_read32(spi_register[i].addr), spi_register[i].name);
break;
}
}
print_spi_cntrl0(sb);
print_spi_alt_cs(sb, nb);
print_spi_speed_config(sb);
print_spi_host_prefetch(sb);
if (spibar)
unmap_physical((void *)spibar, spi_size);
printf("\n====================================\n");
return 0;
}
int print_spi(struct pci_dev *sb, struct pci_dev *nb)
{
return print_spibar(sb, nb);
}