Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement istep 10.1 #76

Merged
merged 5 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/arch/ppc64/include/arch/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define FW_SPACE_SIZE 0x10000000
#define LPC_BASE_ADDR (MMIO_GROUP0_CHIP0_LPC_BASE_ADDR + LPCHC_IO_SPACE)
#define MMIO_GROUP0_CHIP0_SCOM_BASE_ADDR 0x800603FC00000000
#define MMIO_GROUP_SIZE 0x0000200000000000

/* Enforce In-order Execution of I/O */
static inline void eieio(void)
Expand Down
104 changes: 82 additions & 22 deletions src/cpu/power9/scom.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include <cpu/power/spr.h> // HMER
#include <console/console.h>

#define XSCOM_ADDR_IND_ADDR PPC_BITMASK(11,31)
#define XSCOM_ADDR_IND_DATA PPC_BITMASK(48,63)

#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)
Expand All @@ -15,18 +18,91 @@
#define XSCOM_LOG_REG 0x00090012
#define XSCOM_ERR_REG 0x00090013

void write_scom_indirect(uint64_t reg_address, uint64_t value)
static void reset_scom_engine(uint8_t chip)
krystian-hebel marked this conversation as resolved.
Show resolved Hide resolved
{
/*
* 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(0, XSCOM_RCVED_STAT_REG, 0);
write_scom_direct(chip, XSCOM_LOG_REG, 0);
write_scom_direct(chip, XSCOM_ERR_REG, 0);
clear_hmer();
eieio();
}

uint64_t read_scom_direct(uint8_t chip, 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 + chip * MMIO_GROUP_SIZE ),
"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(chip);
/*
* 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(uint8_t chip, 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 + chip * MMIO_GROUP_SIZE ),
"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(chip);
}

void write_scom_indirect(uint8_t chip, 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);
write_scom_direct(chip, addr, data);

for (int retries = 0; retries < XSCOM_IND_MAX_RETRIES; ++retries) {
data = read_scom_direct(addr);
data = read_scom_direct(chip, addr);
if((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) {
return;
}
Expand All @@ -38,17 +114,17 @@ void write_scom_indirect(uint64_t reg_address, uint64_t value)
}
}

uint64_t read_scom_indirect(uint64_t reg_address)
uint64_t read_scom_indirect(uint8_t chip, 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);
write_scom_direct(chip, addr, data);

for (int retries = 0; retries < XSCOM_IND_MAX_RETRIES; ++retries) {
data = read_scom_direct(addr);
data = read_scom_direct(chip, addr);
if((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) {
break;
}
Expand All @@ -61,19 +137,3 @@ uint64_t read_scom_indirect(uint64_t reg_address)

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();
}
1 change: 1 addition & 0 deletions src/include/cpu/power/istep_10.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <stdint.h>

void istep_10_1(uint8_t chips);
void istep_10_10(uint8_t *phb_active_mask, uint8_t *iovalid_enable);
void istep_10_12(void);
void istep_10_13(void);
Expand Down
Loading