mb/google/brya: Create rull variant
[coreboot2.git] / src / drivers / tpm / cr50.c
blobd77ad48d8f874672080a55b6e287ca180c4e36dd
1 /* SPDX-License-Identifier: BSD-3-Clause */
3 #include <delay.h>
4 #include <drivers/spi/tpm/tpm.h>
5 #include <security/tpm/tis.h>
6 #include <string.h>
7 #include <timer.h>
8 #include <types.h>
10 #define CR50_DID_VID 0x00281ae0L
11 #define TI50_DT_DID_VID 0x504a6666L
12 #define TI50_OT_DID_VID 0x50666666L
14 #define CR50_BOARD_CFG_LOCKBIT_MASK 0x80000000U
15 #define CR50_BOARD_CFG_FEATUREBITS_MASK 0x3FFFFFFFU
17 #define CR50_BOARD_CFG_100US_READY_PULSE 0x00000001U
18 #define CR50_BOARD_CFG_VALUE \
19 (CONFIG(CR50_USE_LONG_INTERRUPT_PULSES) \
20 ? CR50_BOARD_CFG_100US_READY_PULSE : 0)
22 #define CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */
24 enum cr50_register {
25 CR50_FW_VER_REG,
26 CR50_BOARD_CFG_REG,
29 #define CR50_FW_VER_REG_SPI (TPM_LOCALITY_0_SPI_BASE + 0xf90)
30 #define CR50_BOARD_CFG_REG_SPI (TPM_LOCALITY_0_SPI_BASE + 0xfe0)
32 #define CR50_FW_VER_REG_I2C 0x0f
33 #define CR50_BOARD_CFG_REG_I2C 0x1c
35 /* Return register address, which depends on the bus type, or -1 for error. */
36 static int get_reg_addr(enum cr50_register reg)
38 if (CONFIG(SPI_TPM)) {
39 switch (reg) {
40 case CR50_FW_VER_REG:
41 return CR50_FW_VER_REG_SPI;
42 case CR50_BOARD_CFG_REG:
43 return CR50_BOARD_CFG_REG_SPI;
44 default:
45 return -1;
49 if (CONFIG(I2C_TPM)) {
50 switch (reg) {
51 case CR50_FW_VER_REG:
52 return CR50_FW_VER_REG_I2C;
53 case CR50_BOARD_CFG_REG:
54 return CR50_BOARD_CFG_REG_I2C;
55 default:
56 return -1;
60 return -1;
63 static bool cr50_fw_supports_board_cfg(struct cr50_firmware_version *version)
65 /* Cr50 supports the CR50_BOARD_CFG register from version 0.5.5 / 0.6.5
66 * and onwards. */
67 if (version->epoch > 0 || version->major >= 7
68 || (version->major >= 5 && version->minor >= 5))
69 return true;
71 printk(BIOS_INFO, "Cr50 firmware does not support CR50_BOARD_CFG, version: %d.%d.%d\n",
72 version->epoch, version->major, version->minor);
74 return false;
78 * Expose method to read the CR50_BOARD_CFG register, will return zero if
79 * register not supported by Cr50 firmware.
81 static uint32_t cr50_get_board_cfg(void)
83 struct cr50_firmware_version ver;
84 uint32_t value;
86 if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
87 return 0;
89 if (!cr50_fw_supports_board_cfg(&ver))
90 return 0;
92 const enum cb_err ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value,
93 sizeof(value));
94 if (ret != CB_SUCCESS) {
95 printk(BIOS_ERR, "Error reading from Cr50\n");
96 return 0;
99 return value & CR50_BOARD_CFG_FEATUREBITS_MASK;
102 __weak int cr50_plat_irq_status(void)
104 static int warning_displayed;
106 if (!warning_displayed) {
107 printk(BIOS_WARNING, "%s() not implemented, wasting 20ms to wait on Cr50!\n",
108 __func__);
109 warning_displayed = 1;
111 mdelay(CR50_TIMEOUT_NOIRQ_MS);
113 return 1;
117 * Set the BOARD_CFG register on the TPM chip to a particular compile-time constant value.
119 enum cb_err cr50_set_board_cfg(void)
121 /* If we get here and we aren't cr50, then we must be ti50 which does
122 * not currently need to support a board_cfg register. */
123 if (!CONFIG(TPM_GOOGLE_CR50))
124 return CB_SUCCESS;
126 struct cr50_firmware_version ver;
127 enum cb_err ret;
128 uint32_t value;
130 if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
131 return CB_ERR;
133 if (!cr50_fw_supports_board_cfg(&ver))
134 return CB_ERR;
136 /* Set the CR50_BOARD_CFG register, for e.g. asking cr50 to use longer ready pulses. */
137 ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value));
138 if (ret != CB_SUCCESS) {
139 printk(BIOS_ERR, "Error reading from Cr50\n");
140 return CB_ERR;
143 if ((value & CR50_BOARD_CFG_FEATUREBITS_MASK) == CR50_BOARD_CFG_VALUE) {
144 printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, matches desired = 0x%08x\n",
145 value, CR50_BOARD_CFG_VALUE);
146 return CB_SUCCESS;
149 if (value & CR50_BOARD_CFG_LOCKBIT_MASK) {
150 /* The high bit is set, meaning that the Cr50 is already locked on a particular
151 * value for the register, but not the one we wanted. */
152 printk(BIOS_ERR, "Current CR50_BOARD_CFG = 0x%08x, does not match"
153 "desired = 0x%08x\n", value, CR50_BOARD_CFG_VALUE);
154 return CB_ERR;
157 printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, setting to 0x%08x\n",
158 value, CR50_BOARD_CFG_VALUE);
159 value = CR50_BOARD_CFG_VALUE;
161 ret = tis_vendor_write(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value));
162 if (ret != CB_SUCCESS) {
163 printk(BIOS_ERR, "Error writing to cr50\n");
164 return ret;
167 return CB_SUCCESS;
170 bool cr50_is_long_interrupt_pulse_enabled(void)
172 if (CONFIG(TPM_GOOGLE_CR50))
173 return !!(cr50_get_board_cfg() & CR50_BOARD_CFG_100US_READY_PULSE);
175 /* Ti50 and future GSCs will support only long interrupt pulses. */
176 return true;
179 static enum cb_err cr50_parse_fw_version(const char *version_str,
180 struct cr50_firmware_version *ver)
182 int epoch, major, minor;
184 char *number = strstr(version_str, " RW_A:");
185 if (!number)
186 number = strstr(version_str, " RW_B:");
187 if (!number)
188 return CB_ERR_ARG;
189 number += 6; /* Skip past the colon. */
191 epoch = skip_atoi(&number);
192 if (*number++ != '.')
193 return CB_ERR_ARG;
194 major = skip_atoi(&number);
195 if (*number++ != '.')
196 return CB_ERR_ARG;
197 minor = skip_atoi(&number);
199 ver->epoch = epoch;
200 ver->major = major;
201 ver->minor = minor;
202 return CB_SUCCESS;
205 enum cb_err cr50_get_firmware_version(struct cr50_firmware_version *version)
207 static struct cr50_firmware_version cr50_firmware_version;
209 if (cr50_firmware_version.epoch || cr50_firmware_version.major ||
210 cr50_firmware_version.minor)
211 goto success;
213 int chunk_count = 0;
214 size_t chunk_size = 50;
215 char version_str[301];
216 int addr = get_reg_addr(CR50_FW_VER_REG);
219 * Does not really matter what's written, this just makes sure
220 * the version is reported from the beginning.
222 tis_vendor_write(addr, &chunk_size, 1);
225 * Read chunk_size bytes at a time, last chunk will be zero padded.
227 do {
228 uint8_t *buf = (uint8_t *)version_str + chunk_count * chunk_size;
229 tis_vendor_read(addr, buf, chunk_size);
230 if (!version_str[++chunk_count * chunk_size - 1])
231 /* Zero padding detected: end of string. */
232 break;
233 /* Check if there is enough room for reading one more chunk. */
234 } while (chunk_count * chunk_size < sizeof(version_str) - chunk_size);
236 version_str[chunk_count * chunk_size] = '\0';
237 printk(BIOS_INFO, "Firmware version: %s\n", version_str);
239 if (cr50_parse_fw_version(version_str, &cr50_firmware_version) != CB_SUCCESS) {
240 printk(BIOS_ERR, "Did not recognize Cr50 version format\n");
241 return CB_ERR;
244 success:
245 if (version)
246 *version = cr50_firmware_version;
247 return CB_SUCCESS;
250 enum cb_err cr50_wait_tpm_ready(void)
252 struct stopwatch sw;
254 stopwatch_init_msecs_expire(&sw, CONFIG_GOOGLE_TPM_IRQ_TIMEOUT_MS);
256 while (!cr50_plat_irq_status())
257 if (stopwatch_expired(&sw)) {
258 printk(BIOS_ERR, "Cr50 TPM IRQ timeout!\n");
259 return CB_ERR;
262 return CB_SUCCESS;