From 3b40a91dbc026fecb2a4d6476d9db3cf61a6e23e Mon Sep 17 00:00:00 2001 From: Pete Dietl Date: Sun, 1 Jun 2025 13:35:21 -0700 Subject: [PATCH] drivers: spi: sam0: Handle 32-bit length extension The sam0 SPI driver does not ensure that it clears the 32-bit extension option during init. The 32-bit extension option, which comprises of a field in the CTRLC register and the LENGTH register enables better bus utilization by allowing 32-bit writes to the SPI DATA register (as opposed to the usual 8-bit writes). The driver breaks down if this option is enabled by causing each intended byte of output to become four bytes. We fix this by explicitly disabling the 32-bit extension option in init. Signed-off-by: Pete Dietl --- drivers/spi/spi_sam0.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi_sam0.c b/drivers/spi/spi_sam0.c index 6dd2d64fad5..9cb5bf51d0c 100644 --- a/drivers/spi/spi_sam0.c +++ b/drivers/spi/spi_sam0.c @@ -77,6 +77,10 @@ static int spi_sam0_configure(const struct device *dev, SercomSpi *regs = cfg->regs; SERCOM_SPI_CTRLA_Type ctrla = {.reg = 0}; SERCOM_SPI_CTRLB_Type ctrlb = {.reg = 0}; +#ifdef SERCOM_SPI_CTRLC_MASK + SERCOM_SPI_CTRLC_Type ctrlc = {.reg = 0}; + SERCOM_SPI_LENGTH_Type length = {.reg = 0}; +#endif int div; if (spi_context_configured(&data->ctx, config)) { @@ -129,18 +133,36 @@ static int spi_sam0_configure(const struct device *dev, div = (SOC_ATMEL_SAM0_GCLK0_FREQ_HZ / config->frequency) / 2U - 1; div = CLAMP(div, 0, UINT8_MAX); +#ifdef SERCOM_SPI_CTRLC_MASK + /* LENGTH.LEN must only be enabled when CTRLC.bit.DATA32B is enabled. + * Since we are about to explicitly disable it, we need to clear the LENGTH register. + */ + length.reg = SERCOM_SPI_LENGTH_RESETVALUE; + + /* Disable inter-character spacing and the 32-bit read/write extension */ + ctrlc.reg = SERCOM_SPI_CTRLC_RESETVALUE; +#endif + /* Update the configuration only if it has changed */ - if (regs->CTRLA.reg != ctrla.reg || regs->CTRLB.reg != ctrlb.reg || - regs->BAUD.reg != div) { + if (regs->CTRLA.reg != ctrla.reg || regs->CTRLB.reg != ctrlb.reg || regs->BAUD.reg != div +#ifdef SERCOM_SPI_CTRLC_MASK + || regs->LENGTH.reg != length.reg || regs->CTRLC.reg != ctrlc.reg +#endif + ) { regs->CTRLA.bit.ENABLE = 0; wait_synchronization(regs); - regs->CTRLB = ctrlb; wait_synchronization(regs); regs->BAUD.reg = div; wait_synchronization(regs); regs->CTRLA = ctrla; wait_synchronization(regs); +#ifdef SERCOM_SPI_CTRLC_MASK + regs->LENGTH = length; + wait_synchronization(regs); + /* Although CTRLC is not write-synchronized, it is enabled-protected */ + regs->CTRLC = ctrlc; +#endif } data->ctx.config = config;