diff --git a/src/arch/ppc64/include/arch/io.h b/src/arch/ppc64/include/arch/io.h index 132a5ce353d..0462047a322 100644 --- a/src/arch/ppc64/include/arch/io.h +++ b/src/arch/ppc64/include/arch/io.h @@ -9,6 +9,7 @@ #define MMIO_GROUP0_CHIP0_LPC_BASE_ADDR 0x8006030000000000 #define LPCHC_IO_SPACE 0xD0010000 #define LPC_BASE_ADDR (MMIO_GROUP0_CHIP0_LPC_BASE_ADDR + LPCHC_IO_SPACE) +#define MMIO_GROUP0_CHIP0_SCOM_BASE_ADDR 0x8006030000000000 /* Enforce In-order Execution of I/O */ static inline void eieio(void) diff --git a/src/cpu/power9/scom.c b/src/cpu/power9/scom.c index 76269b5d3c0..113d047d303 100644 --- a/src/cpu/power9/scom.c +++ b/src/cpu/power9/scom.c @@ -5,11 +5,15 @@ #include #define XSCOM_DATA_IND_READ PPC_BIT(0) -#define XSCOM_DATA_IND_COMPLETE PPC_BIT(32) +#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_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 /* * WARNING: @@ -157,3 +161,18 @@ 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); + eieio(); +} diff --git a/src/include/cpu/power/scom.h b/src/include/cpu/power/scom.h index a839f84c619..df2100e6bc0 100644 --- a/src/include/cpu/power/scom.h +++ b/src/include/cpu/power/scom.h @@ -33,6 +33,7 @@ #ifndef __ASSEMBLER__ #include #include +#include // TODO: these are probably specific to POWER9 typedef enum @@ -85,26 +86,53 @@ typedef enum EC23_CHIPLET_ID = 0x37 ///< Core23 chiplet (Quad5, EX11, C1) } chiplet_id_t; +void reset_scom_engine(void); + static uint64_t read_scom_direct(uint64_t reg_address) { uint64_t val; - asm volatile( - "ldcix %0, %1, %2": - "=r"(val): - "b"(0x800603FC00000000), - "r"(reg_address << 3)); - eieio(); + uint64_t hmer; + do { + 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; } static void write_scom_direct(uint64_t reg_address, uint64_t data) { - asm volatile( - "stdcix %0, %1, %2":: - "r"(data), - "b"(0x800603FC00000000), - "r"(reg_address << 3)); - eieio(); + uint64_t hmer; + do { + 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(); } /* diff --git a/src/include/cpu/power/spr.h b/src/include/cpu/power/spr.h index 59c089d8ef9..97a3399e911 100644 --- a/src/include/cpu/power/spr.h +++ b/src/include/cpu/power/spr.h @@ -3,22 +3,23 @@ #include // PPC_BIT() -#define SPR_HMER 0x150 +#define SPR_HMER 0x150 /* Bits in HMER/HMEER */ -#define SPR_HMER_MALFUNCTION_ALERT PPC_BIT(0) -#define SPR_HMER_PROC_RECV_DONE PPC_BIT(2) -#define SPR_HMER_PROC_RECV_ERROR_MASKED PPC_BIT(3) -#define SPR_HMER_TFAC_ERROR PPC_BIT(4) -#define SPR_HMER_TFMR_PARITY_ERROR PPC_BIT(5) -#define SPR_HMER_XSCOM_FAIL PPC_BIT(8) -#define SPR_HMER_XSCOM_DONE PPC_BIT(9) -#define SPR_HMER_PROC_RECV_AGAIN PPC_BIT(11) -#define SPR_HMER_WARN_RISE PPC_BIT(14) -#define SPR_HMER_WARN_FALL PPC_BIT(15) -#define SPR_HMER_SCOM_FIR_HMI PPC_BIT(16) -#define SPR_HMER_TRIG_FIR_HMI PPC_BIT(17) -#define SPR_HMER_HYP_RESOURCE_ERR PPC_BIT(20) -#define SPR_HMER_XSCOM_STATUS PPC_BITMASK(21,23) +#define SPR_HMER_MALFUNCTION_ALERT PPC_BIT(0) +#define SPR_HMER_PROC_RECV_DONE PPC_BIT(2) +#define SPR_HMER_PROC_RECV_ERROR_MASKED PPC_BIT(3) +#define SPR_HMER_TFAC_ERROR PPC_BIT(4) +#define SPR_HMER_TFMR_PARITY_ERROR PPC_BIT(5) +#define SPR_HMER_XSCOM_FAIL PPC_BIT(8) +#define SPR_HMER_XSCOM_DONE PPC_BIT(9) +#define SPR_HMER_PROC_RECV_AGAIN PPC_BIT(11) +#define SPR_HMER_WARN_RISE PPC_BIT(14) +#define SPR_HMER_WARN_FALL PPC_BIT(15) +#define SPR_HMER_SCOM_FIR_HMI PPC_BIT(16) +#define SPR_HMER_TRIG_FIR_HMI PPC_BIT(17) +#define SPR_HMER_HYP_RESOURCE_ERR PPC_BIT(20) +#define SPR_HMER_XSCOM_STATUS PPC_BITMASK(21,23) +#define SPR_HMER_XSCOM_OCCUPIED PPC_BIT(23) #ifndef __ASSEMBLER__ #include @@ -30,6 +31,11 @@ static inline uint64_t read_hmer(void) return val; } +static inline void clear_hmer(void) +{ + asm volatile("mtspr %0, %1" :: "i"(SPR_HMER), "r"(0) : "memory"); +} + static inline uint64_t read_msr(void) { uint64_t val;