blob: d77ad48d8f874672080a55b6e287ca180c4e36dd [file] [log] [blame]
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -07001/* SPDX-License-Identifier: BSD-3-Clause */
2
Grzegorz Bernacki7758b472023-06-14 12:01:32 +00003#include <delay.h>
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -07004#include <drivers/spi/tpm/tpm.h>
5#include <security/tpm/tis.h>
6#include <string.h>
Yu-Ping Wuae1e7022022-05-17 09:33:18 +08007#include <timer.h>
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -07008#include <types.h>
9
Jes Klinke1430b042022-03-28 14:22:24 -070010#define CR50_DID_VID 0x00281ae0L
Jett Rinkd41ad722024-06-10 09:31:14 -060011#define TI50_DT_DID_VID 0x504a6666L
12#define TI50_OT_DID_VID 0x50666666L
Jes Klinke1430b042022-03-28 14:22:24 -070013
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070014#define CR50_BOARD_CFG_LOCKBIT_MASK 0x80000000U
15#define CR50_BOARD_CFG_FEATUREBITS_MASK 0x3FFFFFFFU
16
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)
21
Grzegorz Bernacki7758b472023-06-14 12:01:32 +000022#define CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */
23
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070024enum cr50_register {
25 CR50_FW_VER_REG,
26 CR50_BOARD_CFG_REG,
27};
28
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)
31
Tim Wawrzynczak1e50dfb2022-02-16 13:48:07 -070032#define CR50_FW_VER_REG_I2C 0x0f
33#define CR50_BOARD_CFG_REG_I2C 0x1c
34
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070035/* Return register address, which depends on the bus type, or -1 for error. */
36static int get_reg_addr(enum cr50_register reg)
37{
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;
46 }
47 }
48
Tim Wawrzynczak1e50dfb2022-02-16 13:48:07 -070049 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;
57 }
58 }
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070059
60 return -1;
61}
62
63static bool cr50_fw_supports_board_cfg(struct cr50_firmware_version *version)
64{
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;
70
71 printk(BIOS_INFO, "Cr50 firmware does not support CR50_BOARD_CFG, version: %d.%d.%d\n",
72 version->epoch, version->major, version->minor);
73
74 return false;
75}
76
77/*
78 * Expose method to read the CR50_BOARD_CFG register, will return zero if
79 * register not supported by Cr50 firmware.
80 */
81static uint32_t cr50_get_board_cfg(void)
82{
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -060083 struct cr50_firmware_version ver;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070084 uint32_t value;
85
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -060086 if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
87 return 0;
88
89 if (!cr50_fw_supports_board_cfg(&ver))
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070090 return 0;
91
Subrata Banik60b2ab82022-03-09 12:55:34 +053092 const enum cb_err ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value,
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070093 sizeof(value));
94 if (ret != CB_SUCCESS) {
Jes Klinke1430b042022-03-28 14:22:24 -070095 printk(BIOS_ERR, "Error reading from Cr50\n");
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070096 return 0;
97 }
98
99 return value & CR50_BOARD_CFG_FEATUREBITS_MASK;
100}
101
Grzegorz Bernacki7758b472023-06-14 12:01:32 +0000102__weak int cr50_plat_irq_status(void)
103{
104 static int warning_displayed;
105
106 if (!warning_displayed) {
107 printk(BIOS_WARNING, "%s() not implemented, wasting 20ms to wait on Cr50!\n",
108 __func__);
109 warning_displayed = 1;
110 }
111 mdelay(CR50_TIMEOUT_NOIRQ_MS);
112
113 return 1;
114}
115
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700116/**
117 * Set the BOARD_CFG register on the TPM chip to a particular compile-time constant value.
118 */
Subrata Banik60b2ab82022-03-09 12:55:34 +0530119enum cb_err cr50_set_board_cfg(void)
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700120{
Jes Klinke1430b042022-03-28 14:22:24 -0700121 /* 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;
125
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600126 struct cr50_firmware_version ver;
Subrata Banik60b2ab82022-03-09 12:55:34 +0530127 enum cb_err ret;
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600128 uint32_t value;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700129
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600130 if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
131 return CB_ERR;
132
133 if (!cr50_fw_supports_board_cfg(&ver))
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700134 return CB_ERR;
135
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) {
Jes Klinke1430b042022-03-28 14:22:24 -0700139 printk(BIOS_ERR, "Error reading from Cr50\n");
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700140 return CB_ERR;
141 }
142
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;
147 }
148
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;
155 }
156
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;
160
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;
165 }
166
167 return CB_SUCCESS;
168}
169
170bool cr50_is_long_interrupt_pulse_enabled(void)
171{
Jes Klinke1430b042022-03-28 14:22:24 -0700172 if (CONFIG(TPM_GOOGLE_CR50))
173 return !!(cr50_get_board_cfg() & CR50_BOARD_CFG_100US_READY_PULSE);
Reka Normanf08e3bb2022-03-29 09:55:57 +1100174
Jes Klinke1430b042022-03-28 14:22:24 -0700175 /* Ti50 and future GSCs will support only long interrupt pulses. */
176 return true;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700177}
178
Subrata Banik60b2ab82022-03-09 12:55:34 +0530179static enum cb_err cr50_parse_fw_version(const char *version_str,
Jes Klinke1430b042022-03-28 14:22:24 -0700180 struct cr50_firmware_version *ver)
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700181{
182 int epoch, major, minor;
183
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. */
190
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);
198
199 ver->epoch = epoch;
200 ver->major = major;
201 ver->minor = minor;
202 return CB_SUCCESS;
203}
204
Subrata Banik60b2ab82022-03-09 12:55:34 +0530205enum cb_err cr50_get_firmware_version(struct cr50_firmware_version *version)
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700206{
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600207 static struct cr50_firmware_version cr50_firmware_version;
208
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700209 if (cr50_firmware_version.epoch || cr50_firmware_version.major ||
210 cr50_firmware_version.minor)
211 goto success;
212
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);
217
218 /*
219 * Does not really matter what's written, this just makes sure
220 * the version is reported from the beginning.
221 */
222 tis_vendor_write(addr, &chunk_size, 1);
223
224 /*
225 * Read chunk_size bytes at a time, last chunk will be zero padded.
226 */
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);
235
236 version_str[chunk_count * chunk_size] = '\0';
237 printk(BIOS_INFO, "Firmware version: %s\n", version_str);
238
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;
242 }
243
244success:
Jes Klinke1430b042022-03-28 14:22:24 -0700245 if (version)
246 *version = cr50_firmware_version;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700247 return CB_SUCCESS;
248}
Yu-Ping Wuae1e7022022-05-17 09:33:18 +0800249
250enum cb_err cr50_wait_tpm_ready(void)
251{
252 struct stopwatch sw;
253
254 stopwatch_init_msecs_expire(&sw, CONFIG_GOOGLE_TPM_IRQ_TIMEOUT_MS);
255
Grzegorz Bernacki7758b472023-06-14 12:01:32 +0000256 while (!cr50_plat_irq_status())
Yu-Ping Wuae1e7022022-05-17 09:33:18 +0800257 if (stopwatch_expired(&sw)) {
258 printk(BIOS_ERR, "Cr50 TPM IRQ timeout!\n");
259 return CB_ERR;
260 }
261
262 return CB_SUCCESS;
263}