1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 #include <cpu/power/scom.h>
4 #include <cpu/power/spr.h> // HMER
5 #include <console/console.h>
7 #define XSCOM_ADDR_IND_ADDR PPC_BITMASK(11, 31)
8 #define XSCOM_ADDR_IND_DATA PPC_BITMASK(48, 63)
10 #define XSCOM_DATA_IND_READ PPC_BIT(0)
11 #define XSCOM_DATA_IND_COMPLETE PPC_BIT(32)
12 #define XSCOM_DATA_IND_ERR PPC_BITMASK(33, 35)
13 #define XSCOM_DATA_IND_DATA PPC_BITMASK(48, 63)
14 #define XSCOM_DATA_IND_FORM1_DATA PPC_BITMASK(12, 63)
15 #define XSCOM_IND_MAX_RETRIES 10
17 #define XSCOM_RCVED_STAT_REG 0x00090018
18 #define XSCOM_LOG_REG 0x00090012
19 #define XSCOM_ERR_REG 0x00090013
21 static void reset_scom_engine(void)
24 * With cross-CPU SCOM accesses, first register should be cleared on the
25 * executing CPU, the other two on target CPU. In that case it may be
26 * necessary to do the remote writes in assembly directly to skip checking
27 * HMER and possibly end in a loop.
29 write_scom_direct(XSCOM_RCVED_STAT_REG
, 0);
30 write_scom_direct(XSCOM_LOG_REG
, 0);
31 write_scom_direct(XSCOM_ERR_REG
, 0);
36 uint64_t read_scom_direct(uint64_t reg_address
)
42 * Clearing HMER on every SCOM access seems to slow down CCS up
43 * to a point where it starts hitting timeout on "less ideal"
44 * DIMMs for write centering. Clear it only if this do...while
45 * executes more than once.
47 if ((hmer
& SPR_HMER_XSCOM_STATUS
) == SPR_HMER_XSCOM_OCCUPIED
)
54 "b"(MMIO_GROUP0_CHIP0_SCOM_BASE_ADDR
),
55 "r"(reg_address
<< 3));
58 } while ((hmer
& SPR_HMER_XSCOM_STATUS
) == SPR_HMER_XSCOM_OCCUPIED
);
60 if (hmer
& SPR_HMER_XSCOM_STATUS
) {
63 * All F's are returned in case of error, but code polls for a set bit
64 * after changes that can make such error appear (e.g. clock settings).
65 * Return 0 so caller won't have to test for all F's in that case.
72 void write_scom_direct(uint64_t reg_address
, uint64_t data
)
76 /* See comment in read_scom_direct() */
77 if ((hmer
& SPR_HMER_XSCOM_STATUS
) == SPR_HMER_XSCOM_OCCUPIED
)
84 "b"(MMIO_GROUP0_CHIP0_SCOM_BASE_ADDR
),
85 "r"(reg_address
<< 3));
88 } while ((hmer
& SPR_HMER_XSCOM_STATUS
) == SPR_HMER_XSCOM_OCCUPIED
);
90 if (hmer
& SPR_HMER_XSCOM_STATUS
)
94 void write_scom_indirect(uint64_t reg_address
, uint64_t value
)
98 addr
= reg_address
& 0x7FFFFFFF;
99 data
= reg_address
& XSCOM_ADDR_IND_ADDR
;
100 data
|= value
& XSCOM_ADDR_IND_DATA
;
102 write_scom_direct(addr
, data
);
104 for (int retries
= 0; retries
< XSCOM_IND_MAX_RETRIES
; ++retries
) {
105 data
= read_scom_direct(addr
);
106 if ((data
& XSCOM_DATA_IND_COMPLETE
) && ((data
& XSCOM_DATA_IND_ERR
) == 0)) {
108 } else if (data
& XSCOM_DATA_IND_COMPLETE
) {
109 printk(BIOS_EMERG
, "SCOM WR error %16.16llx = %16.16llx : %16.16llx\n",
110 reg_address
, value
, data
);
116 uint64_t read_scom_indirect(uint64_t reg_address
)
120 addr
= reg_address
& 0x7FFFFFFF;
121 data
= XSCOM_DATA_IND_READ
| (reg_address
& XSCOM_ADDR_IND_ADDR
);
123 write_scom_direct(addr
, data
);
125 for (int retries
= 0; retries
< XSCOM_IND_MAX_RETRIES
; ++retries
) {
126 data
= read_scom_direct(addr
);
127 if ((data
& XSCOM_DATA_IND_COMPLETE
) && ((data
& XSCOM_DATA_IND_ERR
) == 0)) {
129 } else if (data
& XSCOM_DATA_IND_COMPLETE
) {
130 printk(BIOS_EMERG
, "SCOM RD error %16.16llx : %16.16llx\n",
136 return data
& XSCOM_DATA_IND_DATA
;