blob: 16d87d918a5a715697d1b9e71b7614d9a33d8815 [file] [log] [blame]
Abhi.Singhc8c5faf2024-08-28 14:17:52 -05001/*
2 * Copyright (c) 2025, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <errno.h>
9#include <stdbool.h>
10#include <string.h>
11
12#include <drivers/gpio_spi.h>
13#include <drivers/tpm/tpm2.h>
14#include <drivers/tpm/tpm2_chip.h>
15#include <drivers/tpm/tpm2_interface.h>
16
17#define ENCODE_LIMIT 128
18#define CS_ASSERT_OFFSET 0xD4
19#define RETRY_COUNT 50
20
21#define TPM_READ false
22#define TPM_WRITE true
23
24extern struct spi_plat *spidev;
25
26static int tpm2_spi_transfer(const void *data_out, void *data_in, uint8_t len)
27{
28 return spidev->ops->xfer(len, data_out, data_in);
29}
30
31/*
32 * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05
33 */
34static int tpm2_spi_start_transaction(uint16_t tpm_reg, bool write, uint8_t len)
35{
36 int rc;
37 uint8_t header[4];
38 uint8_t header_response[4];
39 uint8_t zero = 0, byte;
40 int retries;
41
42 /* check to make sure len does not exceed the encoding limit */
43 if (len > ENCODE_LIMIT) {
44 return TPM_INVALID_PARAM;
45 }
46
47 /*
48 * 7.4.6 TPM SPI Bit protocol calls for the following header
49 * to be sent to the TPM at the start of every attempted read/write.
50 */
51
52 /* header[0] contains the r/w and the xfer size, if the msb is not
53 * set, the operation is write, if it is set then it is read.
54 * The size of the transfer is encoded, and must not overwrite
55 * the msb, therefore an ENCODE LIMIT of 128 is present.
56 */
57 header[0] = ((write) ? 0x00 : 0x80) | (len - 1);
58
59 /*
60 * header[1] contains the address offset 0xD4_xxxx as defined
61 * in the TPM spec, since the CS# is asserted.
62 */
63 header[1] = CS_ASSERT_OFFSET;
64
65 /*
66 * header[2] and header[3] contain the address of the register
67 * to be read/written.
68 */
69 header[2] = tpm_reg >> 8;
70 header[3] = tpm_reg;
71
72 rc = tpm2_spi_transfer(header, header_response, 4);
73 if (rc != 0) {
74 return TPM_ERR_TRANSFER;
75 }
76
77 /*
78 * 7.4.5 Flow Control defines a wait state in order to accommodate
79 * the TPM in case it needs to free its buffer.
80 */
81 if ((header_response[3] & 0x01) != 0U) {
82 return TPM_SUCCESS;
83 }
84
85 /*
86 * if the wait state over bit is not set in the initial header_response,
87 * poll for the wait state over by sending a zeroed byte, if the
88 * RETRY_COUNT is exceeded the transfer fails.
89 */
90 for (retries = RETRY_COUNT; retries > 0; retries--) {
91 rc = tpm2_spi_transfer(&zero, &byte, 1);
92 if (rc != 0) {
93 return TPM_ERR_TRANSFER;
94 }
95 if ((byte & 0x01) != 0U) {
96 return TPM_SUCCESS;
97 }
98 }
99
100 if (retries == 0) {
101 ERROR("%s: TPM Timeout\n", __func__);
102 return TPM_ERR_TIMEOUT;
103 }
104
105 return TPM_SUCCESS;
106}
107
108static void tpm2_spi_end_transaction(void)
109{
110 spidev->ops->stop();
111}
112
113static void tpm2_spi_init(void)
114{
115 spidev->ops->get_access();
116 spidev->ops->start();
117}
118
119static int tpm2_fifo_io(uint16_t tpm_reg, bool is_write, uint8_t len, void *val)
120{
121 int rc;
122
123 tpm2_spi_init();
124 rc = tpm2_spi_start_transaction(tpm_reg, is_write, len);
125 if (rc != 0) {
126 tpm2_spi_end_transaction();
127 return rc;
128 }
129
130 rc = tpm2_spi_transfer(
131 is_write ? val : NULL,
132 is_write ? NULL : val,
133 len);
134 if (rc != 0) {
135 tpm2_spi_end_transaction();
136 return rc;
137 }
138
139 tpm2_spi_end_transaction();
140
141 return TPM_SUCCESS;
142}
143
144int tpm2_fifo_write_byte(uint16_t tpm_reg, uint8_t val)
145{
146 return tpm2_fifo_io(tpm_reg, TPM_WRITE, BYTE, &val);
147}
148
149int tpm2_fifo_read_byte(uint16_t tpm_reg, uint8_t *val)
150{
151 return tpm2_fifo_io(tpm_reg, TPM_READ, BYTE, val);
152}
153
154int tpm2_fifo_read_chunk(uint16_t tpm_reg, uint8_t len, void *val)
155{
156 if ((len != BYTE) && (len != WORD) && (len != DWORD)) {
157 return TPM_INVALID_PARAM;
158 }
159
160 return tpm2_fifo_io(tpm_reg, TPM_READ, len, val);
161}