mb/google/brya: Create rull variant
[coreboot2.git] / src / drivers / i2c / tpm / tis_atmel.c
blobad5e7f37b7b7cc0562d5547bf99fe5af7c2fc3bd
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 #include <assert.h>
4 #include <commonlib/endian.h>
5 #include <commonlib/helpers.h>
6 #include <console/console.h>
7 #include <delay.h>
8 #include <device/i2c_simple.h>
9 #include <endian.h>
10 #include <lib.h>
11 #include <security/tpm/tis.h>
12 #include <timer.h>
13 #include <types.h>
15 #include "tpm.h"
17 #define RECV_TIMEOUT (1 * 1000) /* 1 second */
18 #define XMIT_TIMEOUT (1 * 1000) /* 1 second */
19 #define SLEEP_DURATION 1000 /* microseconds */
21 struct tpm_output_header {
22 uint16_t tag;
23 uint32_t length;
24 uint32_t return_code;
25 } __packed;
27 static tpm_result_t i2c_tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
28 uint8_t *recvbuf, size_t *rbuf_len)
30 size_t hdr_bytes;
31 struct tpm_output_header *header;
32 size_t max_recv_bytes;
33 size_t recv_bytes;
34 int status;
35 struct stopwatch sw;
37 ASSERT(sbuf_size >= 10);
38 if (CONFIG(DRIVER_TPM_DISPLAY_TIS_BYTES)) {
39 /* Display the TPM command */
40 if (sbuf_size >= 10)
41 printk(BIOS_DEBUG, "TPM Command: 0x%08x\n",
42 read_at_be32(sendbuf, sizeof(uint16_t)
43 + sizeof(uint32_t)));
44 hexdump(sendbuf, sbuf_size);
47 /* Send the command to the TPM */
48 stopwatch_init_msecs_expire(&sw, XMIT_TIMEOUT);
49 while (1) {
50 status = i2c_write_raw(CONFIG_DRIVER_TPM_I2C_BUS,
51 CONFIG_DRIVER_TPM_I2C_ADDR, (uint8_t *)sendbuf,
52 sbuf_size);
53 if ((status < 0) && (!stopwatch_expired(&sw)))
54 continue;
55 if (status < 0) {
56 printk(BIOS_ERR, "I2C write error: %d\n", status);
57 return TPM_CB_COMMUNICATION_ERROR;
59 break;
62 /* Read the TPM response header */
63 max_recv_bytes = *rbuf_len;
64 ASSERT(max_recv_bytes >= sizeof(*header));
65 hdr_bytes = sizeof(*header);
66 header = (struct tpm_output_header *)recvbuf;
67 stopwatch_init_msecs_expire(&sw, RECV_TIMEOUT);
68 do {
69 status = i2c_read_raw(CONFIG_DRIVER_TPM_I2C_BUS,
70 CONFIG_DRIVER_TPM_I2C_ADDR, recvbuf, hdr_bytes);
71 if (status > 0)
72 break;
73 udelay(SLEEP_DURATION);
74 } while (!stopwatch_expired(&sw));
75 if (status != sizeof(*header))
76 return TPM_CB_COMMUNICATION_ERROR;
78 /* Determine the number of bytes remaining */
79 recv_bytes = MIN(be32_to_cpu(*(uint32_t *)&header->length),
80 max_recv_bytes);
82 /* Determine if there is additional response data */
83 if (recv_bytes > hdr_bytes) {
84 /* Display the TPM response */
85 if (CONFIG(DRIVER_TPM_DISPLAY_TIS_BYTES))
86 hexdump(recvbuf, hdr_bytes);
88 /* Read the full TPM response */
89 status = i2c_read_raw(CONFIG_DRIVER_TPM_I2C_BUS,
90 CONFIG_DRIVER_TPM_I2C_ADDR, recvbuf, recv_bytes);
91 if (status < 0) {
92 printk(BIOS_ERR, "I2C read error: %d\n", status);
93 return TPM_CB_COMMUNICATION_ERROR;
97 /* Return the number of bytes received */
98 *rbuf_len = status;
100 /* Display the TPM response */
101 if (CONFIG(DRIVER_TPM_DISPLAY_TIS_BYTES)) {
102 printk(BIOS_DEBUG, "TPM Response: 0x%08x\n",
103 read_at_be32(recvbuf, sizeof(uint16_t)
104 + sizeof(uint32_t)));
105 hexdump(recvbuf, *rbuf_len);
108 /* Successful transfer */
109 return TPM_SUCCESS;
112 tis_sendrecv_fn i2c_tis_probe(enum tpm_family *family)
115 * Can't query version or really anything as the device doesn't support
116 * much through this interface (can't specify address on accesses).
118 * Hence the assumption is that whatever TPM version is enabled at
119 * compile-time defines what the device supports. The check is written
120 * in a way to give TPM 1 preference even if support for both versions
121 * is compiled in.
123 if (family != NULL)
124 *family = CONFIG(TPM1) ? TPM_1 : TPM_2;
126 return &i2c_tis_sendrecv;