Tool/software:
Hello,
I'm attempting to load the patch bundle from the Embedded Controller (STM32, in our case) onto TPS25751. I am using the TPS25751EVM and following the Application note: Using an Embedded Controller (EC) to Load a Patch Bundle Directly to the TPS25751 or TPS26750 for this task. However, the PD controller remains in PTCH mode even after I load the patch bundle.
Here are the steps I followed to load the patch bundle into the PD:
The patch bundle is generated using the USBCPD_Application_Customization_Tool V1.1.1 from the TI web portal.
STM32 and TPS25751 Setup:
- Removed the jumper J16 to disconnect the EEPROM, ensuring that the PD controller does not load the patch from the EEPROM.
- Connected J2 to USB-C power
- Connected J6 pin 15(I2Ct_SCL) to STM32 I2C_SCL
- Connected J6 pin 17(I2Ct_SDA) to STM32 I2C_SDA
- Connected J6 pin 19(I2Ct_IRQ) to STM32 GPIO input
- Connected J6 pin 21(GND) to STM32 GND
Firmware steps to load the patch bundle onto TPS257251:
- Read the MODE(addr: 0x03) register for the mode string: It is reading as "PTCH" as there is no EEPROM.
- Wait for the I2Ct_IRQ line to go low.
- Read the interrupt event(addr: 0x14) register and check if bit 81 is set to ensure the device is ready to patch.
- Write the interrupt mask(addr: 0x16) register with data: "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x03" to enable interrupt events for:
- Ready to Patch(bit: 81)
- Patch loaded(bit: 80)
- CMD1 complete(bit: 30)
- Write the interrupt clear(addr: 0x18) register with data: "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" to clear the interrupts.
- Send "PBMs" command:
- Write data(addr: 0x09) register with the command data: "[cmd data length][patch size byte 1],[patch size byte 0],\x0,\x0,[patch I2C address],[Timeout]".
- In this case, the patch size is 0x7F00, the patch I2C address is 0x30, and the timeout is 0x32. So the data is "\x06\x00\x7F\x00\x00\x30\x32".
- Write the command(addr: 0x08) register with the command "PBMs".
- Wait for the I2Ct_IRQ line to go low.
- Read the interrupt event(addr: 0x14) register and check if bit 30 is set to confirm that the command completed successfully.
- Read the command(addr: 0x08) register until the read returns zeros. The contents of this register are set to zero when the command is successful.
- Read the data(addr: 0x09) register until the read returns zeros. The contents of this register are set to zero when the command is successful.
- Write data(addr: 0x09) register with the command data: "[cmd data length][patch size byte 1],[patch size byte 0],\x0,\x0,[patch I2C address],[Timeout]".
- Upload the patch bundle onto the device.
- In this case, I wrote the patch bundle to the patch I2C address(in this case: 0x30 as provided in "PBMs" command. See 6.a.A) in chunks of 64 bytes.
- Write the interrupt clear(addr: 0x18) with data: "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" to clear the interrupts.
- Send "PBMc" command:
- There is no input data for the "PBMc" command. So, I wrote nothing to the data(addr: 0x09) register.
- Write the command(addr: 0x08) register with the command "PBMc".
- Wait for the I2Ct_IRQ line to go low.
- Read the interrupt event(addr: 0x14) register and check if bit 30 is set to confirm that the command completed successfully..
- Read the command(addr: 0x08) register until the read returns zeros. The contents of this register are set to zero when the command is successful.
- Read the data(addr: 0x09) register until the read returns zeros. The contents of this register are set to zero when the command is successful.
- Read the interrupt event(addr: 0x14) register and check for bit 80 set to make sure the patch is loaded to the device.
- This step is always failing as bit 80 remains unset.
- Read the MODE(addr: 0x03) register for the mode string in polling mode.
- Read in 10 10-millisecond intervals until the mode switches to "APP"
Note: I also tried the steps by skipping step 10 and went directly to polling the MODE register and waiting for the app mode. However, I ended up polling indefinitely, as the device remains in "PTCH" mode.
Here is the code for the reference:
// TPS25751 I2C Address - 7-bit address (0x42 >> 1)
#define TPS25751_I2C_ADDR (0x21)
#define EEPROM_I2C_ADDR (0x50)
#define PATCH_I2C_ADDR (0x30)
// TPS25751 Register Addresses
#define TPS25751_REG_MODE (0x03)
#define TPS25751_REG_CMD1 (0x08)
#define TPS25751_REG_DATA1 (0x09)
#define TPS25751_REG_INT_EVENT (0x14)
#define TPS25751_REG_INT_MASK (0x16)
#define TPS25751_REG_INT_CLEAR (0x18)
#define TPS25751_REG_STATUS (0x1A)
#define TPS25751_REG_BOOT_STATUS (0x2D)
// Patch bundle structure
typedef struct {
const uint8_t *data;
uint32_t size;
} tps25751_patch_t;
/**
* @brief Write block of data to TPS25751
*/
static HAL_StatusTypeDef tps25751_write_block(uint8_t reg, const uint8_t *data, uint16_t len)
{
HAL_StatusTypeDef status;
uint8_t tx_buffer[65]; // Max I2C block size + register address
if (len > 63) return HAL_ERROR; // I2C block size limitation
tx_buffer[0] = reg;
tx_buffer[1] = len;
memcpy(&tx_buffer[2], data, len);
status = HAL_I2C_Master_Transmit(&hi2c2, TPS25751_I2C_ADDR << 1, tx_buffer, len + 2, HAL_MAX_DELAY);
return status;
}
/**
* @brief Read block of data from TPS25751
*/
static HAL_StatusTypeDef tps25751_read_block(uint8_t reg, uint8_t *data, uint16_t len)
{
HAL_StatusTypeDef status;
status = HAL_I2C_Master_Transmit(&hi2c2, TPS25751_I2C_ADDR << 1, ®, 1, HAL_MAX_DELAY);
if (status != HAL_OK) return status;
return HAL_I2C_Master_Receive(&hi2c2, TPS25751_I2C_ADDR << 1, data, len+1, HAL_MAX_DELAY);
}
/**
* @brief Read PD mode
*/
static HAL_StatusTypeDef read_tps25751_status(I2C_HandleTypeDef *hi2c, uint8_t *data)
{
HAL_StatusTypeDef status;
status = HAL_I2C_IsDeviceReady(hi2c, TPS25751_I2C_ADDR << 1, 3, 100);
if (status != HAL_OK) {
return status;
}
status = tps25751_read_block(TPS25751_REG_MODE, data, 0x05);
if (status != HAL_OK) {
return status;
}
return status;
}
/**
* @brief Enter patch mode for TPS25751
*/
static HAL_StatusTypeDef tps25751_send_cmd(uint8_t *cmd, uint8_t *data, uint8_t len, uint8_t wait_for_ack)
{
HAL_StatusTypeDef status;
uint8_t ack[0x0F] = {0};
if (data != NULL && len > 0) {
while(1) {
status = tps25751_write_block(TPS25751_REG_DATA1, data, len);
if (status != HAL_OK) {
return status;
}
status = tps25751_read_block(TPS25751_REG_DATA1, ack, len);
if (status != HAL_OK) {
return status;
}
if(strncmp(data, ack+1, len) == 0) {
break;
}
}
}
status = tps25751_write_block(TPS25751_REG_CMD1, cmd, 0x04);
if (status != HAL_OK) {
return status;
}
// Wait for Interrupt
while (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) != GPIO_PIN_RESET);
// Read Interrupt event
status = tps25751_read_block(TPS25751_REG_INT_EVENT, ack, 0x0B);
if (status != HAL_OK) {
return status;
}
// Make sure the CMD is complete
if ((ack[4] & 0x40) != 0x40) {
// CMD failed
return HAL_ERROR;
}
if (wait_for_ack) {
ack[1] = 1;
while (ack[1] != 0) {
status = tps25751_read_block(TPS25751_REG_CMD1, ack, 0x05);
if (status != HAL_OK) {
return status;
}
osDelay(10);
}
}
return HAL_OK;
}
/**
* @brief Upload patch region to TPS25751
*/
static HAL_StatusTypeDef tps25751_upload_patch(const tps25751_patch_t * const patch)
{
HAL_StatusTypeDef status;
uint32_t bytes_sent = 0;
uint32_t block_size = 0;
uint8_t ack[0x7] = {0};
uint8_t patch_i2c_addr = 0x00;
// Use this for test pupose only
// patch_i2c_addr = find_patch_i2c_addr(patch->size);
patch_i2c_addr = PATCH_I2C_ADDR;
while (bytes_sent < patch->size) {
block_size = (patch->size - bytes_sent) > 64 ? 64 : (patch->size - bytes_sent);
status = HAL_I2C_Master_Transmit(&hi2c2, patch_i2c_addr << 1, &patch->data[bytes_sent], block_size, HAL_MAX_DELAY);
if (status != HAL_OK) {
return status;
}
bytes_sent += block_size;
// osDelay(10); // Small delay between blocks
}
return HAL_OK;
}
/**
* @brief Load patch bundle directly into PD via I2C
*/
static HAL_StatusTypeDef tps25751_load_config(const tps25751_patch_t * const patch)
{
HAL_StatusTypeDef status;
uint8_t ack[0xC] = {0};
uint8_t pbms_data[0x08] = {0};
uint8_t pd_status[5] = {0};
/****** STEP: 01 ******/
// Read boot status
status = read_tps25751_status(&hi2c2, pd_status);
if (strncmp(pd_status+1, "PTCH", 4) == 0) {
/****** STEP: 02 ******/
// Wait for IRQ low
while (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) != GPIO_PIN_RESET);
/****** STEP: 03 ******/
// Read Interrupt event
status = tps25751_read_block(TPS25751_REG_INT_EVENT, ack, 0x0B);
if (status != HAL_OK) {
return status;
}
// Make sure the PD is ready to patch
if ((ack[11] & 0x02) != 0x02) {
// Not ready to patch
return HAL_ERROR;
}
/****** STEP: 04 ******/
/**
* Mask interrupts enable interrupt on the following interrupts
* - Ready for Patch (BIT 81)
* - Patch Loaded (BIT 80)
* - CMD1 complete (BIT 30)
*/
tps25751_write_block(TPS25751_REG_INT_MASK, "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x03", 0x0B);
/****** STEP: 05 ******/
// Clear interrupts
tps25751_write_block(TPS25751_REG_INT_CLEAR, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0x0B);
/****** STEP: 06 ******/
// Send PBMs with PBMs data
pbms_data[0] = (patch->size & 0xFF); // Patch size byte 0
pbms_data[1] = ((patch->size >> 0x08) & 0xFF); // Patch size byte 1
pbms_data[4] = (PATCH_I2C_ADDR); // Patch I2C address
pbms_data[5] = 0x32; // Timeout
status = tps25751_send_cmd("PBMs", (uint8_t*)pbms_data, 6, 1);
if (status != HAL_OK) {
return status;
}
ack[1] = 1;
while (ack[1] != 0) {
status = tps25751_read_block(TPS25751_REG_DATA1, ack, 0x07);
if (status != HAL_OK) {
return status;
}
osDelay(10);
}
/****** STEP: 07 ******/
status = tps25751_upload_patch(patch);
if (status != HAL_OK) {
return status;
}
osDelay(10);
/****** STEP: 08 ******/
// Clear interrupts
tps25751_write_block(TPS25751_REG_INT_CLEAR, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0x0B);
/****** STEP: 09 ******/
status = tps25751_send_cmd("PBMc", NULL, 0, 1);
if (status != HAL_OK) {
return status;
}
ack[1] = 1;
while (ack[1] != 0) {
status = tps25751_read_block(TPS25751_REG_DATA1, ack, 0x07);
if (status != HAL_OK) {
return status;
}
osDelay(10);
}
/****** STEP: 10 ******/
// Read Interrupt event
status = tps25751_read_block(TPS25751_REG_INT_EVENT, ack, 0x0B);
if (status != HAL_OK) {
return status;
}
// Make sure the Patch is loaded successfully
if ((ack[11] & 0x01) != 0x01) {
// Not ready to patch
return HAL_ERROR;
}
/****** STEP: 11 ******/
while(1) {
status = read_tps25751_status(&hi2c2, pd_status);
if (strncmp(pd_status+1, "APP", 3) == 0) {
break;
}
osDelay(10);
}
}
return HAL_OK;
}
Here are the I2C logs from the logic analyzer:
Read mode register:
Write the interrupt mask(addr: 0x16) register with data: "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x03":
Write the interrupt clear(addr: 0x18) with data: "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF":
Send "PBMs" command:
PBMs data: "\x06\x00\x7F\x00\x00\x30\x32".
Read back the "PBMs" data:
Send "PBMs" command:
Read the interrupt event register:
Read command register:
Read data register:
Upload patch bundle:
Write the interrupt clear register with data: "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF":
Send "PBMc":
Read the interrupt event register:
Read command register:
Read data register:
Read the interrupt event register:
Read mode register:
It will be great if you could help me in this context. Please let me know if you need any further information regarding this issue.
Best regards,
Harsha