[T5000] SPI3 connect to TPM fail

This behavior is not expected. What kind of scope probes are you using? Can you use active probes?

The “Pin Direction” and “Req. Initial State” entries should not be left blank. Set DIN to input and all others to output and set the initial states to match the POR pin states.

Hi Chris,

This behavior is not expected. What kind of scope probes are you using? Can you use active probes?

I don’t have active probes.

The “Pin Direction” and “Req. Initial State” entries should not be left blank. Set DIN to input and all others to output and set the initial states to match the POR pin states.

The pinmux settings are modified as follows:


tegra264-mb1-bct-pinmux-p3834-xxxx-p4071-0000.txt (89.5 KB)

kernel diff :
kernel_diff.txt (7.4 KB)

kernel dts:
tegra264-p4071-0000+p3834-0008-nv-common.txt (2.9 KB)

dmesg:
dmesg.txt (135.3 KB)

Even after making the above changes, the host is still unable to communicate properly with the TPM.
Could you please advise me on how to configure the SPI-related functions? Based on the results from the manufacturer’s LA analyzer, it appears that the host pulled the CS pin high too early, causing the communication to fail.

Thanks.

CS pin went high too early in comparison to what, exactly?

Hi Chris,

Please reference as follow pictures:

Coldboot:

Rebind: Execute : echo spi2.0 > /sys/bus/spi/drivers/tpm_tis_spi/bind

It went high too early in comparison to the Data Read phase. The CS pin went high immediately after receiving the READY signal (MISO = 0x01), without waiting for the host to actually clock out and read the data.

Thanks.

If the current issue is specific to CS, please apply the following patch for SPI driver to check if it could help.

diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 68a0d89a3510..de5e0af29002 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -872,16 +872,34 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
 		if (spi_get_csgpiod(spi, 0))
 			gpiod_set_value(spi_get_csgpiod(spi, 0), 1);
 
-		if (is_single_xfer && !(t->cs_change)) {
-			tspi->use_hw_based_cs = true;
-			command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
+		/*
+		 * CS mode selection:
+		 * - Single transfer: use transfer's cs_change flag.
+		 * - Multi-transfer: use pre-determined mode from
+		 *   pre-scan in tegra_spi_transfer_one_message().
+		 */
+		if (is_single_xfer) {
+			if (!t->cs_change) {
+				tspi->use_hw_based_cs = true;
+				command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
+			} else {
+				tspi->use_hw_based_cs = false;
+				command1 |= SPI_CS_SW_HW;
+				if (spi->mode & SPI_CS_HIGH)
+					command1 |= SPI_CS_SW_VAL;
+				else
+					command1 &= ~SPI_CS_SW_VAL;
+			}
 		} else {
-			tspi->use_hw_based_cs = false;
-			command1 |= SPI_CS_SW_HW;
-			if (spi->mode & SPI_CS_HIGH)
-				command1 |= SPI_CS_SW_VAL;
-			else
-				command1 &= ~SPI_CS_SW_VAL;
+			if (tspi->use_hw_based_cs) {
+				command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
+			} else {
+				command1 |= SPI_CS_SW_HW;
+				if (spi->mode & SPI_CS_HIGH)
+					command1 |= SPI_CS_SW_VAL;
+				else
+					command1 &= ~SPI_CS_SW_VAL;
+			}
 		}
 
 		if (!tspi->prod_list) {
@@ -1089,6 +1107,27 @@ static int tegra_spi_transfer_one_message(struct spi_controller *host,
 	msg->actual_length = 0;
 
 	single_xfer = list_is_singular(&msg->transfers);
+
+	/*
+	 * For multi-transfer messages, pre-scan to determine CS mode.
+	 * Per SPI framework, cs_change=0 (default) on intermediate transfers
+	 * means keep CS asserted. Use hardware CS for this, as it maintains
+	 * CS automatically between transfers. Only use software CS when all
+	 * transfers have cs_change=1, explicitly requesting CS toggling.
+	 */
+	if (!single_xfer) {
+		struct spi_transfer *tmp;
+		bool need_cs_hold = false;
+
+		list_for_each_entry(tmp, &msg->transfers, transfer_list) {
+			if (!tmp->cs_change) {
+				need_cs_hold = true;
+				break;
+			}
+		}
+		tspi->use_hw_based_cs = need_cs_hold;
+	}
+
 	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
 		u32 cmd1;
 
@@ -1155,6 +1194,8 @@ static int tegra_spi_transfer_one_message(struct spi_controller *host,
 		} else if (xfer->cs_change) {
 			tegra_spi_transfer_end(spi);
 			spi_transfer_delay_exec(xfer);
+		} else {
+			spi_transfer_delay_exec(xfer);
 		}
 
 	}

Hi Kevin,

I applied the following patch to the SPI driver, but the TPM driver probe failed again.

kernel_diff.txt (8.9 KB)
dmesg.txt (135.3 KB)

ch0 is clk, ch1 is cs, ch2 is mosi, and ch3 is miso. After CS is pulled to low and MOSI outputs 83d40f00, wait 845.8 μs, then pull MISO high, and wait another 293.84 μs.

Could it be that after receiving data from MISO 01, the clock signal wasn’t sent, causing the TPM to stop transmitting subsequent data?
According to the TPM specification, once the CS is pulled high, the current transaction is immediately terminated, resulting in the subsequent data not being read at all.

Thanks.

From the waveform you shared, both CS and SPI-CLK output the expected signal.
Could you also check with your vendor for this issue? as I believe SPI is working as expected.

Hi Kevin,

Could you also check with your vendor for this issue? as I believe SPI is working as expected.

Yes, I have already confirmed the waveforms of these analyzers with the TPM vendor, and the waveforms from the LA analyzer do not appear to comply with the TCG TPM SPI specification.

[Header] → [Wait States (00…01)] → [Data]

The SPI communication with the TPM 2.0 chip fails during the transition from the Flow Control (Wait State) Phase to the Data Phase.
Although the TPM correctly asserts the “Ready Bit” (0x01) on the MISO line, the SPI controller de-asserts the Chip Select (CS) signal immediately after the flow control message is completed.

Due to a software processing delay between the two spi_sync() calls, the CS remains High for a duration that causes the TPM internal state machine to reset/abort the transaction. Consequently, no data is returned during the subsequent Data Phase (MISO remains 0xFF or High-Z).

Thanks.

For the CS related issue, I believe the patch I shared in [T5000] SPI3 connect to TPM fail - #28 by KevinFFF should help.

Please check if you have applied it correctly.
Or, you can apply the following one instead.

Hi Kevin,

I actually already tested this patch on [T5000] SPI3 connect to TPM fail - #30 by pomelo_hsieh.

I also applied the following patch separately to test it: Jetson orin nano SPI Speed not changing - #9 by KevinFFF. However, the situation remains the same — it still cannot communicate with the TPM device. Although the ‘ready’ bit is received from the MISO, subsequent reception from the MISO still fails. This is because the CS pin is pulled high, resetting the TPM’s current transmission.

Thanks.

Do you mean the issue is that SPI controller ignores the ready bit from MISO and de-assert the CS pin which causes the transaction failed?

Could you update the logic of state machine in TPM to resolve the current issue?

Hi Kevin,

Based on my experiments (with the [T5000] SPI3 connect to TPM fail - #28 by KevinFFF patch applied), once the TPM has sent the ready bit, tpm_tis_spi_transfer_full sends cs_change=1 to the SPI host controller driver upon receiving the final data packet.
This causes ‘command1’ to change in ‘tegra_spi_setup_transfer_one’, and ‘tegra_spi_start_transfer_one’ writes ‘command1’ to the register. I’m not sure whether this step pulls the CS pin high or whether this is done in the ‘tegra_spi_transfer_end’ function.

[    5.793160] tpm_tis_spi spi2.0: TPM_DBG: tpm_tis_core_init +++
[    5.806188] TPM_DBG: tegra_spi_transfer_one_message tegra_spi_setup_transfer_one Command1: 0x40600007
[    5.819490] TPM_DBG: Last xfer with cs_change=1, KEEPING CS LOW. len=4
[    5.832692] TPM_DBG: tegra_spi_transfer_one_message tegra_spi_setup_transfer_one Command1: 0x40600007
[    5.846882] TPM_DBG: Last xfer with cs_change=1, KEEPING CS LOW. len=1
[    5.858808] TPM_DBG: TPM Ready bit received: 0x01

[    5.869913] TPM_DBG: tegra_spi_transfer_one_message tegra_spi_setup_transfer_one Command1: 0x40400007
[    5.884447] TPM_DBG: Last xfer with cs_change=0, calling transfer_end. len=4
[    5.896866] TPM_DBG: tegra_spi_transfer_end +++
[    5.896870] TPM_DBG: tegra_spi_transfer_end ---
[    5.913289] TPM_DBG: Data Phase Read 4 bytes: 
[    5.929355] TPM_DBG: KERN_CONT ff 
[    5.940440] TPM_DBG: KERN_CONT ff 
[    5.942972] TPM_DBG: KERN_CONT ff 
[    5.952441] TPM_DBG: KERN_CONT ff 
[    5.955634] TPM_DBG: KERN_CONT
[    5.965666] TPM_DBG: Vendor ID: 0xffff, Device ID: 0xffff

Please refer to debug_tpm_patch.txt and dmesg.txt.

debug_tpm_patch.txt (13.2 KB)
dmesg.txt (16.1 KB)

Thanks.

Hi pomelo_hsieh,

Do you have an Orin or Xavier available to verify if a similar issue occurs on JetPack 5.x?

I will investigate this issue with our internal team, as the previously shared patch was intended to resolve the CS issue.

In the meantime, could you please verify if the same behavior is observed on the devkit? Or SPI1?

Hi Kevin,

Do you have an Orin or Xavier available to verify if a similar issue occurs on JetPack 5.x?

Yes, I have tested the Jetpack 6.2.1 SDK with Linux kernel 5.15 on the Orin NX and Orin NANO SOM, and the hardware SPI1 connection to the NPCT750 TPM works properly.

Based on the TPM module , it works on Orin NX, Orin nano, DGX Spark, Fails only on AGX Thor.

Platform TPM Module
AGX Thor NPCT760 (Fail)
Orin NX / Orin Nano NPCT750 (OK)
DGX Spark NPCT760 (OK)

dts setting :

git log spi-tegra114.c information :

In the meantime, could you please verify if the same behavior is observed on the devkit? Or SPI1?

The Thor Dev Kit does not have an SPI pin conveniently accessible for connecting to the TPM for testing, but I can try connecting to the Thor SPI1 interface on a custom board.
I suspect I’m not the only one encountering this issue. See [T5000] SPI3 connect to TPM fail - #15 by jack_lan

Does the AGX Thor platform have a validated reference design for discrete TPM (SPI TPM)?
If dTPM is supported:

  1. Is there a recommended SPI instance (SPIx)?
  2. Are there any pinmux or timing constraints specific to TPM integration?

Thanks.

Yes, there’s no expansion header on the devkit for SPI pins and we don’t have such TPM module(NPCT760) to verify them locally.

Have you checked if AGX Thor can work with NPCT750?
It could help to clarify if the issue is specific to TPM module.(i.e. NPCT760)

You can also try porting the SPI driver(spi-tegra-114.c) from Orin Nano with JP6.x to AGX Thor with JP7.1 to clarify if the issue is actually caused from SPI driver.

Please also run the following commands on your board and share extracted_proc.dts for further check.

$ sudo dtc -I fs -O dts -o extracted_proc.dts /proc/device-tree

Hi @pomelo_hsieh,

Could you apply the following patch and check if it could help for your case?
0001-spi-fix-and-debug-for-SPI3-CS-issue-in-topic-363582.patch (7.1 KB)

If not, please provide the full dmesg and device tree for further check.

Could you apply the following patch and check if it could help for your case?
0001-spi-fix-and-debug-for-SPI3-CS-issue-in-topic-363582.patch (7.1 KB)

Hi Kevin,

After applying the patch from [T5000] SPI3 connect to TPM fail - #41 by KevinFFF and running the tests, the system successfully recognised the NPCT760 device and retrieved the relevant information.

root@tegra-ubuntu:~# ls /dev/tpm*
/dev/tpm0  /dev/tpm1  /dev/tpmrm0  /dev/tpmrm1
root@tegra-ubuntu:~# ls -l /sys/class/tpm/
total 0
lrwxrwxrwx 1 root root 0 May 13 01:50 tpm0 -> ../../devices/platform/bus@0/810c440000.spi/spi_master/spi2/spi2.0/tpm/tpm0
lrwxrwxrwx 1 root root 0 May 13 01:50 tpm1 -> ../../devices/platform/firmware:ftpm/tpm/tpm1
root@tegra-ubuntu:~# tpm2_getcap properties-fixed
TPM2_PT_FAMILY_INDICATOR:
  raw: 0x322E3000
  value: "2.0"
TPM2_PT_LEVEL:
  raw: 0
TPM2_PT_REVISION:
  raw: 0x9F
  value: 1.59
TPM2_PT_DAY_OF_YEAR:
  raw: 0x9
TPM2_PT_YEAR:
  raw: 0x7E7
TPM2_PT_MANUFACTURER:
  raw: 0x4E544300
  value: "NTC"
TPM2_PT_VENDOR_STRING_1:
  raw: 0x4E504354
  value: "NPCT"
TPM2_PT_VENDOR_STRING_2:
  raw: 0x37357800
  value: "75x"
TPM2_PT_VENDOR_STRING_3:
  raw: 0x2221233E
  value: ""!#>"
TPM2_PT_VENDOR_STRING_4:
  raw: 0x726C7300
  value: "rls"
TPM2_PT_VENDOR_TPM_TYPE:
  raw: 0x0
TPM2_PT_FIRMWARE_VERSION_1:
  raw: 0x70002
TPM2_PT_FIRMWARE_VERSION_2:
  raw: 0x40001
TPM2_PT_INPUT_BUFFER:
  raw: 0x400
TPM2_PT_HR_TRANSIENT_MIN:
  raw: 0x3
TPM2_PT_HR_PERSISTENT_MIN:
  raw: 0x7
TPM2_PT_HR_LOADED_MIN:
  raw: 0x5
TPM2_PT_ACTIVE_SESSIONS_MAX:
  raw: 0x40
TPM2_PT_PCR_COUNT:
  raw: 0x18
TPM2_PT_PCR_SELECT_MIN:
  raw: 0x3
TPM2_PT_CONTEXT_GAP_MAX:
  raw: 0xFFFF
TPM2_PT_NV_COUNTERS_MAX:
  raw: 0x0
TPM2_PT_NV_INDEX_MAX:
  raw: 0x800
TPM2_PT_MEMORY:
  raw: 0x6
TPM2_PT_CLOCK_UPDATE:
  raw: 0x400000
TPM2_PT_CONTEXT_HASH:
  raw: 0xC
TPM2_PT_CONTEXT_SYM:
  raw: 0x6
TPM2_PT_CONTEXT_SYM_SIZE:
  raw: 0x100
TPM2_PT_ORDERLY_COUNT:
  raw: 0xFF
TPM2_PT_MAX_COMMAND_SIZE:
  raw: 0x800
TPM2_PT_MAX_RESPONSE_SIZE:
  raw: 0x800
TPM2_PT_MAX_DIGEST:
  raw: 0x30
TPM2_PT_MAX_OBJECT_CONTEXT:
  raw: 0x614
TPM2_PT_MAX_SESSION_CONTEXT:
  raw: 0x148
TPM2_PT_PS_FAMILY_INDICATOR:
  raw: 0x1
TPM2_PT_PS_LEVEL:
  raw: 0x0
TPM2_PT_PS_REVISION:
  raw: 0x105
TPM2_PT_PS_DAY_OF_YEAR:
  raw: 0x0
TPM2_PT_PS_YEAR:
  raw: 0x0
TPM2_PT_SPLIT_MAX:
  raw: 0x80
TPM2_PT_TOTAL_COMMANDS:
  raw: 0x71
TPM2_PT_LIBRARY_COMMANDS:
  raw: 0x68
TPM2_PT_VENDOR_COMMANDS:
  raw: 0x9
TPM2_PT_NV_BUFFER_MAX:
  raw: 0x4B0
TPM2_PT_MODES:
  raw: 0x7
  value: TPMA_MODES_FIPS_140_2
  value: TPMA_MODES_RESERVED1 (these bits shouldn't be set)

Thanks.

Hi pomelo_hsieh,

Good to hear that the patch helps for your case.
Could you also share the full dmesg for reference?
Thanks.

Could you also share the full dmesg for reference?

Hi Kevin,

Would you like to see the dmesg output with the spi_tegra114_debug_cs flag enabled, or is the standard dmesg output sufficient?

Thanks.

Yes, we would like to check the log with debug flag enabled.