xref: /aosp_15_r20/external/coreboot/src/drivers/i2c/tpm/tis_atmel.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
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>
14 
15 #include "tpm.h"
16 
17 #define RECV_TIMEOUT            (1 * 1000)  /* 1 second */
18 #define XMIT_TIMEOUT            (1 * 1000)  /* 1 second */
19 #define SLEEP_DURATION          1000        /* microseconds */
20 
21 struct tpm_output_header {
22 	uint16_t tag;
23 	uint32_t length;
24 	uint32_t return_code;
25 } __packed;
26 
i2c_tis_sendrecv(const uint8_t * sendbuf,size_t sbuf_size,uint8_t * recvbuf,size_t * rbuf_len)27 static tpm_result_t i2c_tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
28 				     uint8_t *recvbuf, size_t *rbuf_len)
29 {
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;
36 
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);
45 	}
46 
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;
58 		}
59 		break;
60 	}
61 
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;
77 
78 	/* Determine the number of bytes remaining */
79 	recv_bytes = MIN(be32_to_cpu(*(uint32_t *)&header->length),
80 		max_recv_bytes);
81 
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);
87 
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;
94 		}
95 	}
96 
97 	/* Return the number of bytes received */
98 	*rbuf_len = status;
99 
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);
106 	}
107 
108 	/* Successful transfer */
109 	return TPM_SUCCESS;
110 }
111 
i2c_tis_probe(enum tpm_family * family)112 tis_sendrecv_fn i2c_tis_probe(enum tpm_family *family)
113 {
114 	/*
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).
117 	 *
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.
122 	 */
123 	if (family != NULL)
124 		*family = CONFIG(TPM1) ? TPM_1 : TPM_2;
125 
126 	return &i2c_tis_sendrecv;
127 }
128