blob: 5618b9db5d359bd0f2eecd198a9a6f21b4c5cc18 [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
11#define TI50_DID_VID 0x504a6666L
12
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070013#define CR50_BOARD_CFG_LOCKBIT_MASK 0x80000000U
14#define CR50_BOARD_CFG_FEATUREBITS_MASK 0x3FFFFFFFU
15
16#define CR50_BOARD_CFG_100US_READY_PULSE 0x00000001U
17#define CR50_BOARD_CFG_VALUE \
18 (CONFIG(CR50_USE_LONG_INTERRUPT_PULSES) \
19 ? CR50_BOARD_CFG_100US_READY_PULSE : 0)
20
Grzegorz Bernacki7758b472023-06-14 12:01:32 +000021#define CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */
22
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070023enum cr50_register {
24 CR50_FW_VER_REG,
25 CR50_BOARD_CFG_REG,
26};
27
28#define CR50_FW_VER_REG_SPI (TPM_LOCALITY_0_SPI_BASE + 0xf90)
29#define CR50_BOARD_CFG_REG_SPI (TPM_LOCALITY_0_SPI_BASE + 0xfe0)
30
Tim Wawrzynczak1e50dfb2022-02-16 13:48:07 -070031#define CR50_FW_VER_REG_I2C 0x0f
32#define CR50_BOARD_CFG_REG_I2C 0x1c
33
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070034/* Return register address, which depends on the bus type, or -1 for error. */
35static int get_reg_addr(enum cr50_register reg)
36{
37 if (CONFIG(SPI_TPM)) {
38 switch (reg) {
39 case CR50_FW_VER_REG:
40 return CR50_FW_VER_REG_SPI;
41 case CR50_BOARD_CFG_REG:
42 return CR50_BOARD_CFG_REG_SPI;
43 default:
44 return -1;
45 }
46 }
47
Tim Wawrzynczak1e50dfb2022-02-16 13:48:07 -070048 if (CONFIG(I2C_TPM)) {
49 switch (reg) {
50 case CR50_FW_VER_REG:
51 return CR50_FW_VER_REG_I2C;
52 case CR50_BOARD_CFG_REG:
53 return CR50_BOARD_CFG_REG_I2C;
54 default:
55 return -1;
56 }
57 }
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070058
59 return -1;
60}
61
62static bool cr50_fw_supports_board_cfg(struct cr50_firmware_version *version)
63{
64 /* Cr50 supports the CR50_BOARD_CFG register from version 0.5.5 / 0.6.5
65 * and onwards. */
66 if (version->epoch > 0 || version->major >= 7
67 || (version->major >= 5 && version->minor >= 5))
68 return true;
69
70 printk(BIOS_INFO, "Cr50 firmware does not support CR50_BOARD_CFG, version: %d.%d.%d\n",
71 version->epoch, version->major, version->minor);
72
73 return false;
74}
75
76/*
77 * Expose method to read the CR50_BOARD_CFG register, will return zero if
78 * register not supported by Cr50 firmware.
79 */
80static uint32_t cr50_get_board_cfg(void)
81{
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -060082 struct cr50_firmware_version ver;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070083 uint32_t value;
84
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -060085 if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
86 return 0;
87
88 if (!cr50_fw_supports_board_cfg(&ver))
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070089 return 0;
90
Subrata Banik60b2ab82022-03-09 12:55:34 +053091 const enum cb_err ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value,
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070092 sizeof(value));
93 if (ret != CB_SUCCESS) {
Jes Klinke1430b042022-03-28 14:22:24 -070094 printk(BIOS_ERR, "Error reading from Cr50\n");
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -070095 return 0;
96 }
97
98 return value & CR50_BOARD_CFG_FEATUREBITS_MASK;
99}
100
Grzegorz Bernacki7758b472023-06-14 12:01:32 +0000101__weak int cr50_plat_irq_status(void)
102{
103 static int warning_displayed;
104
105 if (!warning_displayed) {
106 printk(BIOS_WARNING, "%s() not implemented, wasting 20ms to wait on Cr50!\n",
107 __func__);
108 warning_displayed = 1;
109 }
110 mdelay(CR50_TIMEOUT_NOIRQ_MS);
111
112 return 1;
113}
114
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700115/**
116 * Set the BOARD_CFG register on the TPM chip to a particular compile-time constant value.
117 */
Subrata Banik60b2ab82022-03-09 12:55:34 +0530118enum cb_err cr50_set_board_cfg(void)
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700119{
Jes Klinke1430b042022-03-28 14:22:24 -0700120 /* If we get here and we aren't cr50, then we must be ti50 which does
121 * not currently need to support a board_cfg register. */
122 if (!CONFIG(TPM_GOOGLE_CR50))
123 return CB_SUCCESS;
124
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600125 struct cr50_firmware_version ver;
Subrata Banik60b2ab82022-03-09 12:55:34 +0530126 enum cb_err ret;
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600127 uint32_t value;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700128
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600129 if (cr50_get_firmware_version(&ver) != CB_SUCCESS)
130 return CB_ERR;
131
132 if (!cr50_fw_supports_board_cfg(&ver))
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700133 return CB_ERR;
134
135 /* Set the CR50_BOARD_CFG register, for e.g. asking cr50 to use longer ready pulses. */
136 ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value));
137 if (ret != CB_SUCCESS) {
Jes Klinke1430b042022-03-28 14:22:24 -0700138 printk(BIOS_ERR, "Error reading from Cr50\n");
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700139 return CB_ERR;
140 }
141
142 if ((value & CR50_BOARD_CFG_FEATUREBITS_MASK) == CR50_BOARD_CFG_VALUE) {
143 printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, matches desired = 0x%08x\n",
144 value, CR50_BOARD_CFG_VALUE);
145 return CB_SUCCESS;
146 }
147
148 if (value & CR50_BOARD_CFG_LOCKBIT_MASK) {
149 /* The high bit is set, meaning that the Cr50 is already locked on a particular
150 * value for the register, but not the one we wanted. */
151 printk(BIOS_ERR, "Current CR50_BOARD_CFG = 0x%08x, does not match"
152 "desired = 0x%08x\n", value, CR50_BOARD_CFG_VALUE);
153 return CB_ERR;
154 }
155
156 printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, setting to 0x%08x\n",
157 value, CR50_BOARD_CFG_VALUE);
158 value = CR50_BOARD_CFG_VALUE;
159
160 ret = tis_vendor_write(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value));
161 if (ret != CB_SUCCESS) {
162 printk(BIOS_ERR, "Error writing to cr50\n");
163 return ret;
164 }
165
166 return CB_SUCCESS;
167}
168
169bool cr50_is_long_interrupt_pulse_enabled(void)
170{
Jes Klinke1430b042022-03-28 14:22:24 -0700171 if (CONFIG(TPM_GOOGLE_CR50))
172 return !!(cr50_get_board_cfg() & CR50_BOARD_CFG_100US_READY_PULSE);
Reka Normanf08e3bb2022-03-29 09:55:57 +1100173
Jes Klinke1430b042022-03-28 14:22:24 -0700174 /* Ti50 and future GSCs will support only long interrupt pulses. */
175 return true;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700176}
177
Subrata Banik60b2ab82022-03-09 12:55:34 +0530178static enum cb_err cr50_parse_fw_version(const char *version_str,
Jes Klinke1430b042022-03-28 14:22:24 -0700179 struct cr50_firmware_version *ver)
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700180{
181 int epoch, major, minor;
182
183 char *number = strstr(version_str, " RW_A:");
184 if (!number)
185 number = strstr(version_str, " RW_B:");
186 if (!number)
187 return CB_ERR_ARG;
188 number += 6; /* Skip past the colon. */
189
190 epoch = skip_atoi(&number);
191 if (*number++ != '.')
192 return CB_ERR_ARG;
193 major = skip_atoi(&number);
194 if (*number++ != '.')
195 return CB_ERR_ARG;
196 minor = skip_atoi(&number);
197
198 ver->epoch = epoch;
199 ver->major = major;
200 ver->minor = minor;
201 return CB_SUCCESS;
202}
203
Subrata Banik60b2ab82022-03-09 12:55:34 +0530204enum cb_err cr50_get_firmware_version(struct cr50_firmware_version *version)
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700205{
Tim Wawrzynczakec5a5cc2022-03-21 12:26:58 -0600206 static struct cr50_firmware_version cr50_firmware_version;
207
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700208 if (cr50_firmware_version.epoch || cr50_firmware_version.major ||
209 cr50_firmware_version.minor)
210 goto success;
211
212 int chunk_count = 0;
213 size_t chunk_size = 50;
214 char version_str[301];
215 int addr = get_reg_addr(CR50_FW_VER_REG);
216
217 /*
218 * Does not really matter what's written, this just makes sure
219 * the version is reported from the beginning.
220 */
221 tis_vendor_write(addr, &chunk_size, 1);
222
223 /*
224 * Read chunk_size bytes at a time, last chunk will be zero padded.
225 */
226 do {
227 uint8_t *buf = (uint8_t *)version_str + chunk_count * chunk_size;
228 tis_vendor_read(addr, buf, chunk_size);
229 if (!version_str[++chunk_count * chunk_size - 1])
230 /* Zero padding detected: end of string. */
231 break;
232 /* Check if there is enough room for reading one more chunk. */
233 } while (chunk_count * chunk_size < sizeof(version_str) - chunk_size);
234
235 version_str[chunk_count * chunk_size] = '\0';
236 printk(BIOS_INFO, "Firmware version: %s\n", version_str);
237
238 if (cr50_parse_fw_version(version_str, &cr50_firmware_version) != CB_SUCCESS) {
239 printk(BIOS_ERR, "Did not recognize Cr50 version format\n");
240 return CB_ERR;
241 }
242
243success:
Jes Klinke1430b042022-03-28 14:22:24 -0700244 if (version)
245 *version = cr50_firmware_version;
Tim Wawrzynczak6b8599f2022-02-14 16:04:21 -0700246 return CB_SUCCESS;
247}
Yu-Ping Wuae1e7022022-05-17 09:33:18 +0800248
249enum cb_err cr50_wait_tpm_ready(void)
250{
251 struct stopwatch sw;
252
253 stopwatch_init_msecs_expire(&sw, CONFIG_GOOGLE_TPM_IRQ_TIMEOUT_MS);
254
Grzegorz Bernacki7758b472023-06-14 12:01:32 +0000255 while (!cr50_plat_irq_status())
Yu-Ping Wuae1e7022022-05-17 09:33:18 +0800256 if (stopwatch_expired(&sw)) {
257 printk(BIOS_ERR, "Cr50 TPM IRQ timeout!\n");
258 return CB_ERR;
259 }
260
261 return CB_SUCCESS;
262}