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 <petedietl@gmail.com>
This commit is contained in:
Pete Dietl 2025-06-01 13:35:21 -07:00 committed by Benjamin Cabé
commit 3b40a91dbc

View file

@ -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;