/* tpm_io_espressif.c * * Copyright (C) 2006-2024 wolfSSL Inc. * * This file is part of wolfTPM. * * wolfTPM is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * wolfTPM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ /* This example shows IO interfaces for Microchip micro-controllers using * MPLAB X and Harmony */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "tpm_io.h" /*****************************************************************************/ /* --- BEGIN IO Callback Logic -- */ /*****************************************************************************/ /* Included via tpm_io.c if WOLFTPM_INCLUDE_IO_FILE is defined */ #ifdef WOLFTPM_INCLUDE_IO_FILE #ifdef WOLFSSL_ESPIDF /* Espressif */ #include "sdkconfig.h" #define TAG "TPM_IO" #ifdef WOLFTPM_I2C #define I2C_READ_WAIT_TICKS (I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS) #define I2C_WRITE_WAIT_TICKS (I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS) /* To use I2C in wolfTPM, be sure the component cmake COMPONENT_REQUIRES * variable includes "driver" (without quotes) for idf_component_register(). * * See: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/i2c.html */ #if ESP_IDF_VERSION_MAJOR >= 5 && ESP_IDF_VERSION_MINOR > 0 /* TODO we are forcing legacy mode, even though using v5.2 */ #define WOLFSSL_USE_LEGACY_I2C 1 #else #define WOLFSSL_USE_LEGACY_I2C 1 #endif #if WOLFSSL_USE_LEGACY_I2C /* Legacy Espressif I2C libraries * * "The legacy driver can't coexist with the new driver. Include i2c.h to * use the legacy driver or the other two headers to use the new driver. * Please keep in mind that the legacy driver is now deprecated and * will be removed in future." */ #include #else #include #include #endif #ifndef CONFIG_SOC_I2C_SUPPORTED #error "It appears I2C is not supported. Please check sdkconfig." #endif /* GPIO number used for I2C master clock */ #ifdef CONFIG_I2C_MASTER_SCL /* Yellow wire Clock */ #define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL #else /* There should have been a Kconfig.projbuild file in the ./main * directory to set I2C parameters in the sdkconfig project file. */ #error "Could not find CONFIG_I2C_MASTER_SCL definition." #endif /* GPIO number used for I2C master data */ #ifdef CONFIG_I2C_MASTER_SDA /* Orange wire */ #define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA #else /* There should have been a Kconfig.projbuild file in the ./main * directory to set I2C parameters in the sdkconfig project file. */ #error "Could not find CONFIG_I2C_MASTER_SDA definition." #endif /* I2C master i2c port number, * the number of i2c peripheral interfaces available will depend on the chip */ #ifndef I2C_MASTER_NUM #define I2C_MASTER_NUM 0 #endif /* I2C master clock frequency * Typically, an I2C slave device has a 7-bit address or 10-bit address. * ESP32 supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which * can go up to 100KHz and 400KHz respectively. * * The clock frequency of SCL in master mode * should not be larger than 400 KHz. */ #ifndef I2C_MASTER_FREQ_HZ #define I2C_MASTER_FREQ_HZ 100000 #endif /* I2C master doesn't need buffer, so disabled: */ #define I2C_MASTER_TX_BUF_DISABLE 0 /* I2C master doesn't need buffer, so disabled: */ #define I2C_MASTER_RX_BUF_DISABLE 0 /* Wait timeout, in milliseconds. Note: -1 means wait forever. */ #ifndef I2C_MASTER_TIMEOUT_MS #define I2C_MASTER_TIMEOUT_MS 25000 #endif /* Infineon 9673 I2C at 0x2e */ #define TPM2_INFINEON_9673_ADDR 0x2e /* I2C test sensor is an LM75 temperature sensor at 0x48 */ #define LM75_SENSOR_ADDR 0x48 #define DELETE_I2C_ON_ERROR 0 /* Number of milliseconds to wait between write and read, * used in esp_tpm_register_read() */ #define WRITE_TO_READ_GUARD_TIME 2 /* Number of milliseconds to wait after read. * used in esp_tpm_register_read() */ #define POST_READ_GUARD_TIME 2 /* Number of milliseconds to wait after standard write. * (see also write-then-read in esp_tpm_register_read, above) */ #define POST_WRITE_GUARD_TIME 2 /* Number of milliseconds to wait after read failure. */ #define READ_RETRY_DELAY_TIME 2 /* Number of milliseconds to wait after write failure. */ #define WRITE_RETRY_DELAY_TIME 2 /* Observed to have a value of 180 in i2c.c, rounded up for safety */ #define I2C_TRANS_BUF_MINIMUM_SIZE 255 #if 0 #define TPM2_I2C_ADDR LM75_SENSOR_ADDR #else #define TPM2_I2C_ADDR TPM2_INFINEON_9673_ADDR #endif #ifndef TPM_I2C_TRIES #define TPM_I2C_TRIES 10 #endif static int _is_initialized_i2c = FALSE; #ifdef DEBUG_WOLFSSL_VERBOSE static esp_err_t show_binary(byte* theVar, size_t dataSz) { char hex_buffer[(dataSz * 2) + 2]; word32 i; ESP_LOGI(TAG, "*********************************************************"); for (i = 0; i < dataSz; i++) { snprintf(&hex_buffer[i * 2], 3, "%02X", (unsigned char)theVar[i]); } ESP_LOGI("TAG", "%s", hex_buffer); ESP_LOGI(TAG, "*********************************************************"); return ESP_OK; } #endif /* ESP-IDF I2C Master Initialization. Returns ESP result code. */ static esp_err_t esp_i2c_master_init(void) { #if WOLFSSL_USE_LEGACY_I2C i2c_config_t conf = { 0 }; int i2c_master_port = I2C_MASTER_NUM; esp_err_t ret = ESP_OK; /* I2C port number, can be I2C_NUM_0 ~ (I2C_NUM_MAX-1). */ if (I2C_MASTER_NUM >= I2C_NUM_MAX) { ESP_LOGW(TAG, "Warning: I2C_MASTER_NUM value %d exceeds (I2C_NUM_MAX-1)" " %d ", I2C_MASTER_NUM, I2C_NUM_MAX); } ESP_LOGI(TAG, "esp_i2c_master_init"); ESP_LOGI(TAG, "I2C_MASTER_FREQ_HZ = %d", (int)I2C_MASTER_FREQ_HZ); ESP_LOGI(TAG, "I2C_READ_WAIT_TICKS = %d", (int)I2C_READ_WAIT_TICKS); ESP_LOGI(TAG, "I2C_WRITE_WAIT_TICKS = %d", (int)I2C_WRITE_WAIT_TICKS); ESP_LOGI(TAG, "I2C_MASTER_TIMEOUT_MS = %d", (int)I2C_MASTER_TIMEOUT_MS); ESP_LOGI(TAG, "I2C_MASTER_NUM = %d", (int)I2C_MASTER_NUM); ESP_LOGI(TAG, "I2C_MASTER_SCL_IO = %d", (int)I2C_MASTER_SCL_IO); ESP_LOGI(TAG, "I2C_MASTER_SDA_IO = %d", (int)I2C_MASTER_SDA_IO); conf.mode = I2C_MODE_MASTER; conf.sda_io_num = I2C_MASTER_SDA_IO; conf.scl_io_num = I2C_MASTER_SCL_IO; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; conf.master.clk_speed = I2C_MASTER_FREQ_HZ; ret = i2c_param_config(i2c_master_port, &conf); #else esp_err_t ret = ESP_FAIL; ESP_LOGE(TAG, "TODO Need to implement non-legacy ESP-IDF I2C library"); #endif if (ret == ESP_OK) { ret = i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); } if (ret == ESP_OK) { ESP_LOGI(TAG, "i2c driver install success"); _is_initialized_i2c = TRUE; } else { ESP_LOGE(TAG, "Failed to initialize i2c. Error code: %d", ret); } return ret; } static esp_err_t i2c_master_delete(void) { ESP_LOGI(TAG, "i2c_master_delete"); ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM)); _is_initialized_i2c = FALSE; return ESP_OK; } /* Espressif HAL I2C */ static esp_err_t esp_tpm_register_read(uint32_t reg, uint8_t *data, size_t len) { int ret; int timeout = TPM_I2C_TRIES; int loops = 0; byte buf[1]; /* TIS layer should never provide a buffer larger than this, * but double check for good coding practice */ if (len > MAX_SPI_FRAMESIZE) { return BAD_FUNC_ARG; } buf[0] = (reg & 0xFF); /* convert to simple 8-bit address for I2C */ /* The I2C takes about 80us to wake up and will NAK until it is ready */ do { /* Write address to read from - retry until ack */ ret = i2c_master_write_to_device(I2C_MASTER_NUM, TPM2_I2C_ADDR, buf, sizeof(buf), I2C_WRITE_WAIT_TICKS); if (ret != ESP_OK) { XSLEEP_MS(WRITE_RETRY_DELAY_TIME); } } while (ret != ESP_OK && --timeout > 0); /* For read we always need this guard time. * (success wake or real read) */ XSLEEP_MS(WRITE_TO_READ_GUARD_TIME); /* guard time - should be min 250us */ if (ret == ESP_OK) { timeout = TPM_I2C_TRIES; do { loops++; ret = i2c_master_read_from_device(I2C_MASTER_NUM, TPM2_I2C_ADDR, data, len, I2C_READ_WAIT_TICKS); if (ret != ESP_OK) { /* If we're not immediately successful, this may be a * long-running transaction. Thus wait an increasingly * longer amount of time for each retry. */ XSLEEP_MS(READ_RETRY_DELAY_TIME + (loops * 4)); } } while ((ret != ESP_OK) && (--timeout > 0)); } XSLEEP_MS(POST_READ_GUARD_TIME); /* guard time - should be 250us */ if (ret == ESP_OK) { #ifdef DEBUG_WOLFSSL_VERBOSE ESP_LOGI(TAG, "Success! i2c_master_read_from_device. loops = %d", loops); show_binary(data, len); #endif } else { if (ret == ESP_ERR_TIMEOUT) { ESP_LOGE(TAG, "ERROR: esp_tpm_register_read ESP_ERR_TIMEOUT"); } else { ESP_LOGE(TAG, "ERROR: tpm_register_read error = %d", ret); } if (DELETE_I2C_ON_ERROR) { i2c_master_delete(); } } return ret; } /* TPM Interface Write. Returns ESP-IDF result code (not TPM) */ static esp_err_t esp_tpm_register_write(uint32_t reg, uint8_t* data, size_t len) { byte buf[MAX_SPI_FRAMESIZE + 1]; int timeout = TPM_I2C_TRIES; int result = ESP_FAIL; /* TIS layer should never provide a buffer larger than this, * but double check for good coding practice */ if (len > MAX_SPI_FRAMESIZE) { return BAD_FUNC_ARG; } /* Build packet with TPM register and data */ buf[0] = (reg & 0xFF); /* convert to simple 8-bit address for I2C */ XMEMCPY(buf + 1, data, len); #ifdef DEBUG_WOLFSSL_VERBOSE ESP_LOGI(TAG, "TPM will write %d bytes:", len); show_binary(data, len); #endif /* The I2C takes about 80us to wake up and will NAK until it is ready */ do { result = i2c_master_write_to_device(I2C_MASTER_NUM, TPM2_I2C_ADDR, buf, len + 1, I2C_WRITE_WAIT_TICKS); if (result != ESP_OK) { XSLEEP_MS(WRITE_RETRY_DELAY_TIME); } } while (result != ESP_OK && --timeout > 0); XSLEEP_MS(POST_WRITE_GUARD_TIME); /* guard time - should be 250us */ if (result == ESP_OK) { ESP_LOGV(TAG, "Success! tpm_register_write wrote %d bytes after " "%d attempts", len, (TPM_I2C_TRIES - timeout)); } else { ESP_LOGE(TAG, "ERROR: tpm_register_write failed with code = %d after " "%d attempts", result, (TPM_I2C_TRIES - timeout)); if (DELETE_I2C_ON_ERROR) { i2c_master_delete(); } } return result; } /* TPM Interface Read. Returns TPM result code (not ESP) */ static int tpm_ifx_i2c_read(void* userCtx, word32 reg, byte* data, int len) { int ret; ret = esp_tpm_register_read(reg, data, len); /* returns ESP error code */ if (ret == ESP_OK) { ESP_LOGV(TAG, "Read device 0x%x success.\n", TPM2_I2C_ADDR); ret = TPM_RC_SUCCESS; } else { ESP_LOGE(TAG, "Read device 0x%x fail. Error = %d\n", TPM2_I2C_ADDR, ret); ret = TPM_RC_FAILURE; } return ret; } /* TPM Interface Write. Returns TPM result code (not ESP) */ static int tpm_ifx_i2c_write(void* userCtx, word32 reg, byte* data, int len) { int ret; ret = esp_tpm_register_write(reg, data, len); /* returns ESP error code */ if (ret == ESP_OK) { /* WARNING: an ESP_LOG message here may at times interfere with the * write-then-read timing, causing errors. Enable with caution: */ /* ESP_LOGI(TAG, "Write device 0x%x success 0x%x len = %d\n", TPM2_I2C_ADDR, (word32)data, len); */ ret = TPM_RC_SUCCESS; } else { ESP_LOGE(TAG, "Write device 0x%x fail. Error = %d\n", TPM2_I2C_ADDR, ret); ret = TPM_RC_FAILURE; } return ret; } int TPM2_IoCb_Espressif_I2C(TPM2_CTX* ctx, int isRead, word32 addr, byte* buf, word16 size, void* userCtx) { int ret = TPM_RC_FAILURE; if (userCtx == NULL) { ESP_LOGE(TAG, "userCtx cannot be null"); } else { if (_is_initialized_i2c) { ESP_LOGV(TAG, "I2C already initialized"); ret = ESP_OK; } else { ret = esp_i2c_master_init(); /* ESP return code, not TPM */ } if (ret == ESP_OK) { if (isRead) { ret = tpm_ifx_i2c_read(userCtx, addr, buf, size); } else { ret = tpm_ifx_i2c_write(userCtx, addr, buf, size); } } else { ESP_LOGE(TAG, "I2C Failed to initialize. Error: %d", ret); ret = TPM_RC_FAILURE; } } (void)ctx; return ret; } /* TPM2_IoCb_Espressif_I2C */ /* end WOLFTPM_I2C */ #else /* If not I2C, it must be SPI */ /* TODO implement SPI */ #ifndef TPM2_SPI_HZ /* Use the max speed by default * See tpm2_types.h for chip specific max values */ #define TPM2_SPI_HZ TPM2_SPI_MAX_HZ #endif #ifdef WOLFTPM_CHECK_WAIT_STATE #error SPI check wait state logic not supported #endif #error TPM2 SPI support on this platform not supported yet #endif #endif /* WOLFSSL_ESPIDF */ #endif /* WOLFTPM_INCLUDE_IO_FILE */ /******************************************************************************/ /* --- END IO Callback Logic -- */ /******************************************************************************/