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