136e3d877SAbhi.Singh /* 236e3d877SAbhi.Singh * Copyright (c) 2025, Arm Limited. All rights reserved. 336e3d877SAbhi.Singh * 436e3d877SAbhi.Singh * SPDX-License-Identifier: BSD-3-Clause 536e3d877SAbhi.Singh */ 636e3d877SAbhi.Singh #include <lib/libc/endian.h> 736e3d877SAbhi.Singh 836e3d877SAbhi.Singh #include <drivers/delay_timer.h> 936e3d877SAbhi.Singh #include <drivers/tpm/tpm2.h> 1036e3d877SAbhi.Singh #include <drivers/tpm/tpm2_chip.h> 1136e3d877SAbhi.Singh #include <drivers/tpm/tpm2_interface.h> 1236e3d877SAbhi.Singh 1336e3d877SAbhi.Singh #define LOCALITY_START_ADDRESS(x, y) \ 1436e3d877SAbhi.Singh ((uint16_t)(x->address + (0x1000 * y))) 1536e3d877SAbhi.Singh 1636e3d877SAbhi.Singh static int tpm2_get_info(struct tpm_chip_data *chip_data, uint8_t locality) 1736e3d877SAbhi.Singh { 1836e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); 1936e3d877SAbhi.Singh uint32_t vid_did; 2036e3d877SAbhi.Singh uint8_t revision; 2136e3d877SAbhi.Singh int err; 2236e3d877SAbhi.Singh 2336e3d877SAbhi.Singh err = tpm2_fifo_read_chunk(tpm_base_addr + TPM_FIFO_REG_VENDID, DWORD, &vid_did); 2436e3d877SAbhi.Singh if (err < 0) { 2536e3d877SAbhi.Singh return err; 2636e3d877SAbhi.Singh } 2736e3d877SAbhi.Singh 2836e3d877SAbhi.Singh err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_REVID, &revision); 2936e3d877SAbhi.Singh if (err < 0) { 3036e3d877SAbhi.Singh return err; 3136e3d877SAbhi.Singh } 3236e3d877SAbhi.Singh 3336e3d877SAbhi.Singh INFO("TPM Chip: vendor-id 0x%x, device-id 0x%x, revision-id: 0x%x\n", 3436e3d877SAbhi.Singh 0xFFFF & vid_did, vid_did >> 16, revision); 3536e3d877SAbhi.Singh 3636e3d877SAbhi.Singh return TPM_SUCCESS; 3736e3d877SAbhi.Singh } 3836e3d877SAbhi.Singh 3936e3d877SAbhi.Singh static int tpm2_wait_reg_bits(uint16_t reg, uint8_t set, unsigned long timeout, uint8_t *status) 4036e3d877SAbhi.Singh { 4136e3d877SAbhi.Singh int err; 4236e3d877SAbhi.Singh uint64_t timeout_delay = timeout_init_us(timeout * 1000); 4336e3d877SAbhi.Singh 4436e3d877SAbhi.Singh do { 4536e3d877SAbhi.Singh err = tpm2_fifo_read_byte(reg, status); 4636e3d877SAbhi.Singh if (err < 0) { 4736e3d877SAbhi.Singh return err; 4836e3d877SAbhi.Singh } 4936e3d877SAbhi.Singh if ((*status & set) == set) { 5036e3d877SAbhi.Singh return TPM_SUCCESS; 5136e3d877SAbhi.Singh } 5236e3d877SAbhi.Singh } while (!timeout_elapsed(timeout_delay)); 5336e3d877SAbhi.Singh 5436e3d877SAbhi.Singh return TPM_ERR_TIMEOUT; 5536e3d877SAbhi.Singh } 5636e3d877SAbhi.Singh 5736e3d877SAbhi.Singh static int tpm2_fifo_request_access(struct tpm_chip_data *chip_data, uint8_t locality) 5836e3d877SAbhi.Singh { 5936e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); 6036e3d877SAbhi.Singh uint8_t status; 6136e3d877SAbhi.Singh int err; 6236e3d877SAbhi.Singh 6336e3d877SAbhi.Singh err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, TPM_ACCESS_REQUEST_USE); 6436e3d877SAbhi.Singh if (err < 0) { 6536e3d877SAbhi.Singh return err; 6636e3d877SAbhi.Singh } 6736e3d877SAbhi.Singh 6836e3d877SAbhi.Singh err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_ACCESS, 6936e3d877SAbhi.Singh TPM_ACCESS_ACTIVE_LOCALITY, 7036e3d877SAbhi.Singh chip_data->timeout_msec_a, &status); 7136e3d877SAbhi.Singh if (err == 0) { 7236e3d877SAbhi.Singh chip_data->locality = locality; 7336e3d877SAbhi.Singh return TPM_SUCCESS; 7436e3d877SAbhi.Singh } 7536e3d877SAbhi.Singh 7636e3d877SAbhi.Singh return err; 7736e3d877SAbhi.Singh } 7836e3d877SAbhi.Singh 7936e3d877SAbhi.Singh static int tpm2_fifo_release_locality(struct tpm_chip_data *chip_data, uint8_t locality) 8036e3d877SAbhi.Singh { 8136e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); 8236e3d877SAbhi.Singh uint8_t buf; 8336e3d877SAbhi.Singh int err; 8436e3d877SAbhi.Singh 8536e3d877SAbhi.Singh err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, &buf); 8636e3d877SAbhi.Singh if (err < 0) { 8736e3d877SAbhi.Singh return err; 8836e3d877SAbhi.Singh } 8936e3d877SAbhi.Singh 9036e3d877SAbhi.Singh if (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { 9136e3d877SAbhi.Singh return tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, 9236e3d877SAbhi.Singh TPM_ACCESS_RELINQUISH_LOCALITY); 9336e3d877SAbhi.Singh } 9436e3d877SAbhi.Singh 9536e3d877SAbhi.Singh ERROR("%s: Unable to release locality\n", __func__); 9636e3d877SAbhi.Singh return TPM_ERR_RESPONSE; 9736e3d877SAbhi.Singh } 9836e3d877SAbhi.Singh 9936e3d877SAbhi.Singh static int tpm2_fifo_prepare(struct tpm_chip_data *chip_data) 10036e3d877SAbhi.Singh { 10136e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); 10236e3d877SAbhi.Singh uint8_t status; 10336e3d877SAbhi.Singh int err; 10436e3d877SAbhi.Singh 10536e3d877SAbhi.Singh err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_COMMAND_READY); 10636e3d877SAbhi.Singh if (err < 0) { 10736e3d877SAbhi.Singh return err; 10836e3d877SAbhi.Singh } 10936e3d877SAbhi.Singh 11036e3d877SAbhi.Singh err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, 11136e3d877SAbhi.Singh TPM_STAT_COMMAND_READY, 11236e3d877SAbhi.Singh chip_data->timeout_msec_b, &status); 11336e3d877SAbhi.Singh if (err < 0) { 11436e3d877SAbhi.Singh ERROR("%s: TPM Status Busy\n", __func__); 11536e3d877SAbhi.Singh return err; 11636e3d877SAbhi.Singh } 11736e3d877SAbhi.Singh 11836e3d877SAbhi.Singh return TPM_SUCCESS; 11936e3d877SAbhi.Singh } 12036e3d877SAbhi.Singh 12136e3d877SAbhi.Singh static int tpm2_fifo_get_burstcount(struct tpm_chip_data *chip_data, uint16_t *burstcount) 12236e3d877SAbhi.Singh { 12336e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); 12436e3d877SAbhi.Singh uint64_t timeout_delay = timeout_init_us(chip_data->timeout_msec_a * 1000); 12536e3d877SAbhi.Singh int err; 12636e3d877SAbhi.Singh 12736e3d877SAbhi.Singh if (burstcount == NULL) { 12836e3d877SAbhi.Singh return TPM_INVALID_PARAM; 12936e3d877SAbhi.Singh } 13036e3d877SAbhi.Singh 13136e3d877SAbhi.Singh do { 13236e3d877SAbhi.Singh uint8_t byte0, byte1; 13336e3d877SAbhi.Singh 13436e3d877SAbhi.Singh err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_LO, &byte0); 13536e3d877SAbhi.Singh if (err < 0) { 13636e3d877SAbhi.Singh return err; 13736e3d877SAbhi.Singh } 13836e3d877SAbhi.Singh 13936e3d877SAbhi.Singh err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_HI, &byte1); 14036e3d877SAbhi.Singh if (err < 0) { 14136e3d877SAbhi.Singh return err; 14236e3d877SAbhi.Singh } 14336e3d877SAbhi.Singh 14436e3d877SAbhi.Singh *burstcount = (uint16_t)((byte1 << 8) + byte0); 14536e3d877SAbhi.Singh if (*burstcount != 0U) { 14636e3d877SAbhi.Singh return TPM_SUCCESS; 14736e3d877SAbhi.Singh } 14836e3d877SAbhi.Singh } while (!timeout_elapsed(timeout_delay)); 14936e3d877SAbhi.Singh 15036e3d877SAbhi.Singh return TPM_ERR_TIMEOUT; 15136e3d877SAbhi.Singh } 15236e3d877SAbhi.Singh 15336e3d877SAbhi.Singh static int tpm2_fifo_send(struct tpm_chip_data *chip_data, const tpm_cmd *buf) 15436e3d877SAbhi.Singh { 15536e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); 15636e3d877SAbhi.Singh uint8_t status; 15736e3d877SAbhi.Singh uint16_t burstcnt; 15836e3d877SAbhi.Singh int err; 15936e3d877SAbhi.Singh uint32_t len, index; 16036e3d877SAbhi.Singh 16136e3d877SAbhi.Singh if (sizeof(buf->header) != TPM_HEADER_SIZE) { 16236e3d877SAbhi.Singh ERROR("%s: invalid command header size.\n", __func__); 16336e3d877SAbhi.Singh return TPM_INVALID_PARAM; 16436e3d877SAbhi.Singh } 16536e3d877SAbhi.Singh 16636e3d877SAbhi.Singh err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, &status); 16736e3d877SAbhi.Singh if (err < 0) { 16836e3d877SAbhi.Singh return err; 16936e3d877SAbhi.Singh } 17036e3d877SAbhi.Singh 17136e3d877SAbhi.Singh if (!(status & TPM_STAT_COMMAND_READY)) { 17236e3d877SAbhi.Singh err = tpm2_fifo_prepare(chip_data); 17336e3d877SAbhi.Singh if (err < 0) { 17436e3d877SAbhi.Singh return err; 17536e3d877SAbhi.Singh } 17636e3d877SAbhi.Singh } 17736e3d877SAbhi.Singh 17836e3d877SAbhi.Singh /* write the command header to the TPM first */ 17936e3d877SAbhi.Singh const uint8_t *header_data = (const uint8_t *)&buf->header; 18036e3d877SAbhi.Singh 18136e3d877SAbhi.Singh for (index = 0; index < TPM_HEADER_SIZE; index++) { 18236e3d877SAbhi.Singh err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, 18336e3d877SAbhi.Singh header_data[index]); 18436e3d877SAbhi.Singh if (err < 0) { 18536e3d877SAbhi.Singh return err; 18636e3d877SAbhi.Singh } 18736e3d877SAbhi.Singh } 18836e3d877SAbhi.Singh 18936e3d877SAbhi.Singh len = be32toh(buf->header.cmd_size); 19036e3d877SAbhi.Singh 19136e3d877SAbhi.Singh while (index < len) { 19236e3d877SAbhi.Singh err = tpm2_fifo_get_burstcount(chip_data, &burstcnt); 19336e3d877SAbhi.Singh if (err < 0) { 19436e3d877SAbhi.Singh return err; 19536e3d877SAbhi.Singh } 19636e3d877SAbhi.Singh 19736e3d877SAbhi.Singh for (; burstcnt > 0U && index < len; burstcnt--) { 19836e3d877SAbhi.Singh err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, 19936e3d877SAbhi.Singh buf->data[index - TPM_HEADER_SIZE]); 20036e3d877SAbhi.Singh if (err < 0) { 20136e3d877SAbhi.Singh return err; 20236e3d877SAbhi.Singh } 20336e3d877SAbhi.Singh index++; 20436e3d877SAbhi.Singh } 20536e3d877SAbhi.Singh } 20636e3d877SAbhi.Singh 20736e3d877SAbhi.Singh err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, 20836e3d877SAbhi.Singh TPM_STAT_VALID, 20936e3d877SAbhi.Singh chip_data->timeout_msec_c, 21036e3d877SAbhi.Singh &status); 21136e3d877SAbhi.Singh if (err < 0) { 21236e3d877SAbhi.Singh return err; 21336e3d877SAbhi.Singh } 21436e3d877SAbhi.Singh 21536e3d877SAbhi.Singh if (status & TPM_STAT_EXPECT) { 21636e3d877SAbhi.Singh ERROR("%s: TPM is still expecting data after command buffer is sent\n", __func__); 21736e3d877SAbhi.Singh return TPM_ERR_TRANSFER; 21836e3d877SAbhi.Singh } 21936e3d877SAbhi.Singh 22036e3d877SAbhi.Singh err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_GO); 22136e3d877SAbhi.Singh if (err < 0) { 22236e3d877SAbhi.Singh return err; 22336e3d877SAbhi.Singh } 22436e3d877SAbhi.Singh 22536e3d877SAbhi.Singh return TPM_SUCCESS; 22636e3d877SAbhi.Singh } 22736e3d877SAbhi.Singh 22836e3d877SAbhi.Singh static int tpm2_fifo_read_data(struct tpm_chip_data *chip_data, tpm_cmd *buf, 22936e3d877SAbhi.Singh uint16_t tpm_base_addr, uint8_t *status, int *size, int bytes_expected) 23036e3d877SAbhi.Singh { 23136e3d877SAbhi.Singh int err, read_size, loop_index; 23236e3d877SAbhi.Singh uint16_t burstcnt; 23336e3d877SAbhi.Singh uint8_t *read_data; 23436e3d877SAbhi.Singh 23536e3d877SAbhi.Singh if (bytes_expected == TPM_READ_HEADER) { 23636e3d877SAbhi.Singh /* read the response header from the TPM first */ 23736e3d877SAbhi.Singh read_data = (uint8_t *)&buf->header; 23836e3d877SAbhi.Singh read_size = TPM_HEADER_SIZE; 23936e3d877SAbhi.Singh loop_index = *size; 24036e3d877SAbhi.Singh } else { 24136e3d877SAbhi.Singh /* process the rest of the mssg with bytes_expected */ 24236e3d877SAbhi.Singh read_data = buf->data; 24336e3d877SAbhi.Singh read_size = bytes_expected; 24436e3d877SAbhi.Singh loop_index = *size - TPM_HEADER_SIZE; 24536e3d877SAbhi.Singh } 24636e3d877SAbhi.Singh 24736e3d877SAbhi.Singh err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, 24836e3d877SAbhi.Singh TPM_STAT_AVAIL, 249*6053ca4cSJenna May chip_data->timeout_msec_a, 25036e3d877SAbhi.Singh status); 25136e3d877SAbhi.Singh if (err < 0) { 25236e3d877SAbhi.Singh return err; 25336e3d877SAbhi.Singh } 25436e3d877SAbhi.Singh 25536e3d877SAbhi.Singh while (*size < read_size) { 25636e3d877SAbhi.Singh err = tpm2_fifo_get_burstcount(chip_data, &burstcnt); 25736e3d877SAbhi.Singh if (err < 0) { 25836e3d877SAbhi.Singh ERROR("%s: TPM burst count error\n", __func__); 25936e3d877SAbhi.Singh return err; 26036e3d877SAbhi.Singh } 26136e3d877SAbhi.Singh 26236e3d877SAbhi.Singh for (; burstcnt > 0U && loop_index < read_size; 26336e3d877SAbhi.Singh burstcnt--, loop_index++, (*size)++) { 26436e3d877SAbhi.Singh err = tpm2_fifo_read_byte( 26536e3d877SAbhi.Singh tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, 26636e3d877SAbhi.Singh (void *)&read_data[loop_index]); 26736e3d877SAbhi.Singh if (err < 0) { 26836e3d877SAbhi.Singh return err; 26936e3d877SAbhi.Singh } 27036e3d877SAbhi.Singh } 27136e3d877SAbhi.Singh } 27236e3d877SAbhi.Singh 27336e3d877SAbhi.Singh return TPM_SUCCESS; 27436e3d877SAbhi.Singh } 27536e3d877SAbhi.Singh 27636e3d877SAbhi.Singh static int tpm2_fifo_receive(struct tpm_chip_data *chip_data, tpm_cmd *buf) 27736e3d877SAbhi.Singh { 27836e3d877SAbhi.Singh uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); 27936e3d877SAbhi.Singh int size = 0, bytes_expected, err; 28036e3d877SAbhi.Singh uint8_t status; 28136e3d877SAbhi.Singh 28236e3d877SAbhi.Singh err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, TPM_READ_HEADER); 28336e3d877SAbhi.Singh if (err < 0) { 28436e3d877SAbhi.Singh return err; 28536e3d877SAbhi.Singh } 28636e3d877SAbhi.Singh 28736e3d877SAbhi.Singh bytes_expected = be32toh(buf->header.cmd_size); 28836e3d877SAbhi.Singh if (bytes_expected > sizeof(*buf)) { 28936e3d877SAbhi.Singh ERROR("%s: tpm response buffer cannot store expected response\n", __func__); 29036e3d877SAbhi.Singh return TPM_INVALID_PARAM; 29136e3d877SAbhi.Singh } 29236e3d877SAbhi.Singh 29336e3d877SAbhi.Singh if (size == bytes_expected) { 29436e3d877SAbhi.Singh return size; 29536e3d877SAbhi.Singh } 29636e3d877SAbhi.Singh 29736e3d877SAbhi.Singh err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, bytes_expected); 29836e3d877SAbhi.Singh if (err < 0) { 29936e3d877SAbhi.Singh return err; 30036e3d877SAbhi.Singh } 30136e3d877SAbhi.Singh 30236e3d877SAbhi.Singh if (size < bytes_expected) { 30336e3d877SAbhi.Singh ERROR("%s: response buffer size is less than expected\n", __func__); 30436e3d877SAbhi.Singh return TPM_ERR_RESPONSE; 30536e3d877SAbhi.Singh } 30636e3d877SAbhi.Singh 30736e3d877SAbhi.Singh return TPM_SUCCESS; 30836e3d877SAbhi.Singh } 30936e3d877SAbhi.Singh 31036e3d877SAbhi.Singh static interface_ops_t fifo_ops = { 31136e3d877SAbhi.Singh .get_info = tpm2_get_info, 31236e3d877SAbhi.Singh .send = tpm2_fifo_send, 31336e3d877SAbhi.Singh .receive = tpm2_fifo_receive, 31436e3d877SAbhi.Singh .request_access = tpm2_fifo_request_access, 31536e3d877SAbhi.Singh .release_locality = tpm2_fifo_release_locality, 31636e3d877SAbhi.Singh }; 31736e3d877SAbhi.Singh 31836e3d877SAbhi.Singh struct interface_ops * 31936e3d877SAbhi.Singh tpm_interface_getops(struct tpm_chip_data *chip_data, uint8_t locality) 32036e3d877SAbhi.Singh { 32136e3d877SAbhi.Singh return &fifo_ops; 32236e3d877SAbhi.Singh } 323