This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TPS65981: Modify the "Application Customization String Table" from Software

Part Number: TPS65981
Other Parts Discussed in Thread: TPS65982

Hi

We have a design with a tps65981.

In the firmware we have configured we have initially disabled PortInfo (111b) in register 0x28 (System Configuration Register).

Later on when the main application software starts up we enable the port, this seems to work fine.

However, I would like to update the "Application Customization String table" before enabling the port.

For instance the SerialNumber which is unique per unit.

In the manual "TPS65981, TPS65982, and TPS65986 Host Interface Technical Reference Manual (SLVUAN1A Feb 2017)" there is a lot of references to the string table but I can't find where it is located ?

Regards,

Martin

  • Hi Martin,

    The String tables are also included in register 0x28. Bytes 11-15 of register 0x28 are used for the Billboard strings. You can modify these strings during run-time through a host controller to meet your system needs. 

    Thank you,
    Eric

  • Hi Eric and thank you.

    Maybe I didn't state my question properly. I would like to alter the actual strings, not only the index/pointer to the strings.

    The way I understand the setting in register 0x28 is as pointers to a string-table.

    How for instance can I set a unique "SerialNumber string", enabled by bit 0 in byte 11 of register 0x28 ?

    Regards,

    Martin

  • Hi Martin,

    Byte 11 is referring to the string pointers. However, bytes 12 through 15 in register 0x28 are for the billboard strings. You can modify bytes 12 through 15 during run-time to change the billboards strings.

    I hope this helps.

    Thank you,
    Eric

  • Hi Eric and thank you.

    Sorry for my late follow up on this.

    What I would like to achieve is to have a unique serial number on each unit produced.

    And was hoping this was possible after the tps65981 had booted up and loaded the firmware from the spi flash.

    I can find the strings in the firmware binary (in the userconfig area (0x826 and onwards).

    I have tried to alter these during a firmware update myself but I assume the tps65981 don't like it due to some sort of CRC on the userconfig data.

    So I am basically trying to "hot-patch" the firmware binary during programming of the flash.

    If you look at the image below: 

    On the left side of the image is the serial number string set to "TBD-MB-SERIAL-NUMBER" starting at 0x826.

    I thought I could just change that string during programming of the flash.

    However, when I change the serial number in the "Application Customization Tool" it also changed some data at 0x804. Maybe a CRC ?

    On the left side I have changed the serial number to "XBD-MB-SERIAL-NUMBER", now the data at 0x804 also changed.

    Is there a way I can change the serial number string and update the magic number at 0x804 accordingly in SW during firmware update.

    I have tried to generate CRCs on different parts of the firmware binary but can't figure out how to ? Or what region is used ?

    Best regards,

    Martin

  • Hi

    From my post yesterday:

    The sentence:

    On the left side I have changed the serial number to "XBD-MB-SERIAL-NUMBER", now the data at 0x804 also changed.

    Should read:

    On the right side I have changed the serial number to "XBD-MB-SERIAL-NUMBER", now the data at 0x804 also changed.

    Martin

  • Hi Martin,

    You should just be able to update the billboard strings using the App Customization GUI in the general settings tab. Then once you create the FW image, it should load onto the TPS65981. I would not recommend updating the actual full flash binary image manually. Using the App Customization GUI would be the correct way to approach this.

    Thank you,
    Eric

  • Hi Eric.

    No, we can not use the App Customization GUI to generate a new firmware binary for each and every new physical unit we are going to produce of a product.

    I can't see that the iSerialNumber entry makes any sense if it can not be unique per unit.

    So my questions are:

    Q1: Is there a way the tps65981 can be configured with a unique iSerialNumber run-time ?

    Q2: Can you provide more information about the structure of the User Configuration area of the firmware binary, address 0x800 to 0xc00 ?

    Best regards,

    Martin

  • Hi Martin,

    Q1: The string table can be updated over I2C during run-time. 

    Q2: There is some information on the flash organization in section 3 of the following app note: 

    Thank you,
    Eric

  • Hi Eric,

    I'm sorry but I can't find any detailed information about the how to update a serialnumber in the firmware binary in the app note provided.

    I have however after a lots of trials and errors been able to figure out how to calculate the crc in the userconfig-area of the firmware.

    See my post from 22. Oct:

    The userconfig-area starts at 0x800.

    The actual length (u16) of the userconfig-area is found in bytes 0x802 -> 0x803: len = 0x341.

    The crc (u32) is at 0x804 -> 0x807.

    The crc is calculated over 0x800 -> 0x803 and 0x80c -> 0xb41.

    (for some reason is the four bytes between 0x808 -> 0x80b not a part of the crc).

    I can now patch the firmware during programming with a serialnumber per unit and re-calculate the crc.

    I attach a code example in case someone else see the need for this.

    Best regards,

    Martin

    #include <linux/crc32.h>
    
    #define tps_le_u8_buf_to_cpu_32(b) le32_to_cpu(*(u32*)(b));
    #define tps_le_u8_buf_to_cpu_16(b) le16_to_cpu(*(u16*)(b));
    
    static u32 tps65981_u32_swapp_bits(u32 val)
    {
    	int k;
    	u32 output = 0;
    	const u32 mask = 0x1;
    
    	for (k = 0; k < 32; k++) {
    		output <<= 1;
    		output |= (val & mask);
    		val >>= 1;
    	}
    
    	return output;
    }
    
    static u32 tps65981_binary_crc32(struct tps65981_state *state, const u8 *data, u32 len)
    {
    	u32 crc = crc32(~0, data, len);
    	return tps65981_u32_swapp_bits(crc);
    }
    
    static u32 tps65981_flash_calc_userconfig_crc(struct tps65981_state *state, u8 *userConfigBuf, u32 userConfigMaxSize)
    {
    	u8 *buf;
    	u16 userConfigLen;
    	u32 crc;
    
    	buf = kzalloc(userConfigMaxSize, GFP_KERNEL);
    	if (buf == NULL) {
    		tps65981_err("%s: failed to alloc size %u\n", __func__, userConfigMaxSize);
    		return -ENOMEM;
    	}
    	memcpy(buf, userConfigBuf, userConfigMaxSize);
    
    	userConfigLen = tps_le_u8_buf_to_cpu_16(buf + 2);
    
    	buf[0x8] = buf[0x0];
    	buf[0x9] = buf[0x1];
    	buf[0xa] = buf[0x2];
    	buf[0xb] = buf[0x3];
    
    	crc = tps65981_binary_crc32(state, buf + 0x8, userConfigLen - 0x8);
    
    	tps65981_dbg(1, flash_debug, "%s: 0x%08x\n", __func__, crc);
    
    	kfree(buf);
    
    	return crc;
    }
    
    int tps65981_flash_check_fw_userconfig_crc(struct tps65981_state *state, u8 *buf)
    {
    	u8 *tpsHeader;
    	u32 bootConfigSize;
    	u32 userConfigStart;
    	u32 userConfigMaxSize;
    	u16 userConfigLen;
    	u32 userConfigCRC;
    	u32 calcCRC;
    
    	tps65981_dbg(1, flash_debug, "%s:\n", __func__);
    
    	tpsHeader = buf;
    
    	bootConfigSize = tps_le_u8_buf_to_cpu_32(tpsHeader + 0x8);
    	userConfigStart = tps_le_u8_buf_to_cpu_32(tpsHeader + 0x48);
    	userConfigMaxSize = tps_le_u8_buf_to_cpu_32(tpsHeader + 0x44);
    	userConfigLen = tps_le_u8_buf_to_cpu_16(tpsHeader + userConfigStart + 2);
    	userConfigCRC = tps_le_u8_buf_to_cpu_32(tpsHeader + userConfigStart + 4);
    
    	/*
    	 * User config data: typically from 0x800 -> 0xc00 in firmware header
    	 * actual length:  bytes: 2 -> 3
    	 * userconfig crc: bytes: 4 -> 7
    	 * crc is calculated over bytes 0 -> 3 and 8 -> actual length
    	 */
    
    	tps65981_dbg(1, flash_debug, "%s: bootconfig size: 0x%08x\n", __func__, bootConfigSize);
    	tps65981_dbg(1, flash_debug, "%s: userconfig area: 0x%08x:0x%08x\n", __func__, userConfigStart, userConfigMaxSize);
    	tps65981_dbg(1, flash_debug, "%s: userconfig len: 0x%04x\n", __func__, userConfigLen);
    	tps65981_dbg(1, flash_debug, "%s: userconfig crc: 0x%04x\n", __func__, userConfigCRC);
    
    	if ((userConfigStart + userConfigMaxSize) > bootConfigSize) {
    		tps65981_err("%s: error userconfig area/size\n", __func__);
    		return -EFAULT;
    	}
    
    	if (userConfigLen > userConfigMaxSize || userConfigLen < 0x8) {
    		tps65981_err("%s: error userconfig len: 0x%04x\n", __func__, userConfigLen);
    		return -EFAULT;
    	}
    
    	calcCRC = tps65981_flash_calc_userconfig_crc(state, tpsHeader + userConfigStart, userConfigMaxSize);
    
    	if (calcCRC != userConfigCRC) {
    		tps65981_err("%s: error crc: 0x%08x != 0x%08x\n", __func__, calcCRC, userConfigCRC);
    		return -EFAULT;
    	}
    
    	return 0;
    }