spi: qcom-geni: Fix cs_change handling on the last transfer
TPM TIS SPI probe fails with: tpm_tis_spi: probe of spi11.0 failed with error -110 TPM TIS SPI sets cs_change=1 on single-transfer messages to keep CS asserted across the header, wait-state, and data phases of a transaction. CS deassertion between these phases violates the TCG SPI flow control specification. This bug was introduced by commitb99181cdf9("spi-geni-qcom: remove manual CS control"), which replaced manual CS control with automatic CS control via the FRAGMENTATION bit. The FRAGMENTATION bit controls CS behavior after a transfer: when set to 1, CS remains asserted; when cleared to 0, CS is deasserted. The commit correctly sets FRAGMENTATION for non-last transfers with cs_change=0 to keep CS asserted between chained transfers, but misses the case where cs_change=1 is set on the last transfer. When cs_change=1 on the last transfer, the client requests CS to remain asserted after the message completes, so FRAGMENTATION must be set to 1 in this case as well. Fix setup_se_xfer() to set FRAGMENTATION when cs_change=1 on the last transfer. Also fix the same missing case in setup_gsi_xfer() and correct it to write 1 instead of the raw bitmask FRAGMENTATION (value 4) to peripheral.fragmentation. This field is a 1-bit boolean consumed by gpi_create_spi_tre() via u32_encode_bits(..., TRE_SPI_GO_FRAG). Writing 4 to a 1-bit field causes u32_encode_bits() to mask it to 0, silently disabling the FRAGMENTATION bit in the GPI TRE regardless of the cs_change logic. Fixes:b99181cdf9("spi-geni-qcom: remove manual CS control") Cc: stable@vger.kernel.org Reviewed-by: Jonathan Marek <jonathan@marek.ca> Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com> Link: https://patch.msgid.link/20260609-fix-spi-fragmentation-bit-logic-v2-1-e18efc255563@oss.qualcomm.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
79378db6a8
commit
5ac5ec8473
1 changed files with 19 additions and 8 deletions
|
|
@ -440,10 +440,15 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!xfer->cs_change) {
|
||||
if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
|
||||
peripheral.fragmentation = FRAGMENTATION;
|
||||
}
|
||||
/*
|
||||
* Set fragmentation to keep CS asserted after this transfer when:
|
||||
* - non-last transfer with cs_change=0: keep CS asserted between chained transfers
|
||||
* - last transfer with cs_change=1: keep CS asserted after the message
|
||||
* (e.g. TPM TIS SPI uses cs_change=1 on single-transfer messages to
|
||||
* keep CS asserted across header, wait-state and data phases)
|
||||
*/
|
||||
peripheral.fragmentation = list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers) ?
|
||||
xfer->cs_change : !xfer->cs_change;
|
||||
|
||||
if (peripheral.cmd & SPI_RX) {
|
||||
dmaengine_slave_config(mas->rx, &config);
|
||||
|
|
@ -849,10 +854,16 @@ static int setup_se_xfer(struct spi_transfer *xfer,
|
|||
mas->cur_xfer_mode = GENI_SE_DMA;
|
||||
geni_se_select_mode(se, mas->cur_xfer_mode);
|
||||
|
||||
if (!xfer->cs_change) {
|
||||
if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
|
||||
m_params = FRAGMENTATION;
|
||||
}
|
||||
/*
|
||||
* Set FRAGMENTATION to keep CS asserted after this transfer when:
|
||||
* - non-last transfer with cs_change=0: keep CS asserted between chained transfers
|
||||
* - last transfer with cs_change=1: keep CS asserted after the message
|
||||
* (e.g. TPM TIS SPI uses cs_change=1 on single-transfer messages to
|
||||
* keep CS asserted across header, wait-state and data phases)
|
||||
*/
|
||||
if (list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers) ?
|
||||
xfer->cs_change : !xfer->cs_change)
|
||||
m_params = FRAGMENTATION;
|
||||
|
||||
/*
|
||||
* Lock around right before we start the transfer since our
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue