TPS25751: Embedded Controller (EC) to Load a Patch Bundle Directly to the TPS25751. Device accepted the patch bundle and not switching from PTCH to APP mode.

Part Number: TPS25751

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:

  1. Removed the jumper J16 to disconnect the EEPROM, ensuring that the PD controller does not load the patch from the EEPROM.
  2. Connected J2 to USB-C power
  3. Connected J6 pin 15(I2Ct_SCL) to STM32 I2C_SCL
  4. Connected J6 pin 17(I2Ct_SDA) to STM32 I2C_SDA
  5. Connected J6 pin 19(I2Ct_IRQ) to STM32 GPIO input
  6. Connected J6 pin 21(GND) to STM32 GND

Firmware steps to load the patch bundle onto TPS257251:

  1. Read the MODE(addr: 0x03) register for the mode string: It is reading as "PTCH" as there is no EEPROM.
  2. Wait for the I2Ct_IRQ line to go low.
  3. Read the interrupt event(addr: 0x14) register and check if bit 81 is set to ensure the device is ready to patch.
  4. 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:
    1. Ready to Patch(bit: 81)
    2. Patch loaded(bit: 80)
    3. CMD1 complete(bit: 30)
  5. Write the interrupt clear(addr: 0x18) register with data: "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" to clear the interrupts.
  6. Send "PBMs" command:
    1. 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]".
      1. 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".
    2. Write the command(addr: 0x08) register with the command "PBMs".
    3. Wait for the I2Ct_IRQ line to go low.
    4. Read the interrupt event(addr: 0x14) register and check if bit 30 is set to confirm that the command completed successfully.
    5. 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.
    6. 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.
  7. Upload the patch bundle onto the device.
    1. 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.
  8. Write the interrupt clear(addr: 0x18) with data: "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" to clear the interrupts.
  9. Send "PBMc" command:
    1. There is no input data for the "PBMc" command. So, I wrote nothing to the data(addr: 0x09) register.
    2. Write the command(addr: 0x08) register with the command "PBMc".
    3. Wait for the I2Ct_IRQ line to go low.
    4. Read the interrupt event(addr: 0x14) register and check if bit 30 is set to confirm that the command completed successfully..
    5. 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.
    6. 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.
  10. Read the interrupt event(addr: 0x14) register and check for bit 80 set to make sure the patch is loaded to the device.
    1. This step is always failing as bit 80 remains unset.
  11. Read the MODE(addr: 0x03) register for the mode string in polling mode.
    1. 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, &reg, 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:

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