[v3,7/9] spi: stm32: add support for bits-per-word setting

Message ID 20260225161851.2475274-8-dario.binacchi@amarulasolutions.com
State New
Headers show
Series
  • video: support Rocktech RK050HR345-CT106A panel
Related show

Commit Message

Dario Binacchi Feb. 25, 2026, 4:16 p.m. UTC
Implement the set_wordlen operation to allow dynamic bus width
configuration. This is required for peripherals with non-standard
requirements, such as display panels that need 9-bit word transfers
during the initialization and setup phase.

Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>

---

Changes in v3:
- Move the stm32_spi_is_enabled() implementation in the previous patch

 drivers/spi/stm32_spi.c | 57 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 52 insertions(+), 5 deletions(-)

Comments

Patrice CHOTARD Feb. 26, 2026, 7:58 a.m. UTC | #1
On 2/25/26 17:16, Dario Binacchi wrote:
> Implement the set_wordlen operation to allow dynamic bus width
> configuration. This is required for peripherals with non-standard
> requirements, such as display panels that need 9-bit word transfers
> during the initialization and setup phase.
> 
> Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
> 
> ---
> 
> Changes in v3:
> - Move the stm32_spi_is_enabled() implementation in the previous patch
> 
>  drivers/spi/stm32_spi.c | 57 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 52 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
> index 70eb8735e03d..9d095b280a70 100644
> --- a/drivers/spi/stm32_spi.c
> +++ b/drivers/spi/stm32_spi.c
> @@ -384,6 +384,44 @@ static int stm32_spi_set_speed(struct udevice *bus, uint hz)
>  	return 0;
>  }
>  
> +static int _stm32_spi_set_wordlen(struct udevice *bus, unsigned int wordlen)
> +{
> +	struct stm32_spi_priv *priv = dev_get_priv(bus);
> +	struct stm32_spi_plat *plat = dev_get_plat(bus);
> +	void __iomem *base = plat->base;
> +	bool spi_enabled;
> +
> +	if ((wordlen - 1) < SPI_CFG1_DSIZE_MIN ||
> +	    (wordlen - 1) > SPI_CFG1_DSIZE) {
> +		dev_err(bus, "Cannot set wordlen to %u [%d - %d]\n",
> +			wordlen, SPI_CFG1_DSIZE_MIN + 1,
> +			SPI_CFG1_DSIZE + 1);
> +		return -EINVAL;
> +	}
> +
> +	spi_enabled = stm32_spi_is_enabled(plat->base);
> +	if (spi_enabled)
> +		stm32_spi_disable(plat->base);
> +
> +	dev_dbg(bus, "bits_per_word=%d\n", wordlen);
> +
> +	priv->cur_bpw = wordlen;
> +	clrsetbits_le32(base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
> +			priv->cur_bpw - 1);
> +
> +	if (spi_enabled)
> +		stm32_spi_enable(plat->base);
> +
> +	return 0;
> +}
> +
> +static int stm32_spi_set_wordlen(struct udevice *slave, unsigned int wordlen)
> +{
> +	struct udevice *bus = dev_get_parent(slave);
> +
> +	return _stm32_spi_set_wordlen(bus, wordlen);
> +}
> +
>  static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
>  			  const void *dout, void *din, unsigned long flags)
>  {
> @@ -397,11 +435,19 @@ static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
>  	u32 xferlen;
>  	u32 mode;
>  	int xfer_status = 0;
> +	int nb_words;
>  
>  	xferlen = bitlen / 8;
>  
> -	if (xferlen <= SPI_CR2_TSIZE)
> -		writel(xferlen, base + STM32_SPI_CR2);
> +	if (priv->cur_bpw <= 8)
> +		nb_words = xferlen;
> +	else if (priv->cur_bpw <= 16)
> +		nb_words = DIV_ROUND_UP(xferlen * 8, 16);
> +	else
> +		nb_words = DIV_ROUND_UP(xferlen * 8, 32);
> +
> +	if (nb_words <= SPI_CR2_TSIZE)
> +		writel(nb_words, base + STM32_SPI_CR2);
>  	else
>  		return -EMSGSIZE;
>  
> @@ -409,6 +455,8 @@ static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
>  	priv->rx_buf = din;
>  	priv->tx_len = priv->tx_buf ? xferlen : 0;
>  	priv->rx_len = priv->rx_buf ? xferlen : 0;
> +	dev_dbg(bus, "bitlen: %d, xferlen: %d, nb_words: %d\n",
> +		bitlen, xferlen, nb_words);
>  
>  	mode = SPI_FULL_DUPLEX;
>  	if (!priv->tx_buf)
> @@ -570,9 +618,7 @@ static int stm32_spi_probe(struct udevice *dev)
>  	priv->fifo_size = stm32_spi_get_fifo_size(dev);
>  	priv->cur_mode = SPI_FULL_DUPLEX;
>  	priv->cur_xferlen = 0;
> -	priv->cur_bpw = SPI_DEFAULT_WORDLEN;
> -	clrsetbits_le32(base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
> -			priv->cur_bpw - 1);
> +	_stm32_spi_set_wordlen(dev, SPI_DEFAULT_WORDLEN);
>  
>  	for (i = 0; i < ARRAY_SIZE(plat->cs_gpios); i++) {
>  		if (!dm_gpio_is_valid(&plat->cs_gpios[i]))
> @@ -633,6 +679,7 @@ static const struct dm_spi_ops stm32_spi_ops = {
>  	.release_bus	= stm32_spi_release_bus,
>  	.set_mode	= stm32_spi_set_mode,
>  	.set_speed	= stm32_spi_set_speed,
> +	.set_wordlen    = stm32_spi_set_wordlen,
>  	.xfer		= stm32_spi_xfer,
>  };
>  
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>

Thanks
Patrice

To unsubscribe from this group and stop receiving emails from it, send an email to linux-amarula+unsubscribe@amarulasolutions.com.

Patch

diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
index 70eb8735e03d..9d095b280a70 100644
--- a/drivers/spi/stm32_spi.c
+++ b/drivers/spi/stm32_spi.c
@@ -384,6 +384,44 @@  static int stm32_spi_set_speed(struct udevice *bus, uint hz)
 	return 0;
 }
 
+static int _stm32_spi_set_wordlen(struct udevice *bus, unsigned int wordlen)
+{
+	struct stm32_spi_priv *priv = dev_get_priv(bus);
+	struct stm32_spi_plat *plat = dev_get_plat(bus);
+	void __iomem *base = plat->base;
+	bool spi_enabled;
+
+	if ((wordlen - 1) < SPI_CFG1_DSIZE_MIN ||
+	    (wordlen - 1) > SPI_CFG1_DSIZE) {
+		dev_err(bus, "Cannot set wordlen to %u [%d - %d]\n",
+			wordlen, SPI_CFG1_DSIZE_MIN + 1,
+			SPI_CFG1_DSIZE + 1);
+		return -EINVAL;
+	}
+
+	spi_enabled = stm32_spi_is_enabled(plat->base);
+	if (spi_enabled)
+		stm32_spi_disable(plat->base);
+
+	dev_dbg(bus, "bits_per_word=%d\n", wordlen);
+
+	priv->cur_bpw = wordlen;
+	clrsetbits_le32(base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
+			priv->cur_bpw - 1);
+
+	if (spi_enabled)
+		stm32_spi_enable(plat->base);
+
+	return 0;
+}
+
+static int stm32_spi_set_wordlen(struct udevice *slave, unsigned int wordlen)
+{
+	struct udevice *bus = dev_get_parent(slave);
+
+	return _stm32_spi_set_wordlen(bus, wordlen);
+}
+
 static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
 			  const void *dout, void *din, unsigned long flags)
 {
@@ -397,11 +435,19 @@  static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
 	u32 xferlen;
 	u32 mode;
 	int xfer_status = 0;
+	int nb_words;
 
 	xferlen = bitlen / 8;
 
-	if (xferlen <= SPI_CR2_TSIZE)
-		writel(xferlen, base + STM32_SPI_CR2);
+	if (priv->cur_bpw <= 8)
+		nb_words = xferlen;
+	else if (priv->cur_bpw <= 16)
+		nb_words = DIV_ROUND_UP(xferlen * 8, 16);
+	else
+		nb_words = DIV_ROUND_UP(xferlen * 8, 32);
+
+	if (nb_words <= SPI_CR2_TSIZE)
+		writel(nb_words, base + STM32_SPI_CR2);
 	else
 		return -EMSGSIZE;
 
@@ -409,6 +455,8 @@  static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
 	priv->rx_buf = din;
 	priv->tx_len = priv->tx_buf ? xferlen : 0;
 	priv->rx_len = priv->rx_buf ? xferlen : 0;
+	dev_dbg(bus, "bitlen: %d, xferlen: %d, nb_words: %d\n",
+		bitlen, xferlen, nb_words);
 
 	mode = SPI_FULL_DUPLEX;
 	if (!priv->tx_buf)
@@ -570,9 +618,7 @@  static int stm32_spi_probe(struct udevice *dev)
 	priv->fifo_size = stm32_spi_get_fifo_size(dev);
 	priv->cur_mode = SPI_FULL_DUPLEX;
 	priv->cur_xferlen = 0;
-	priv->cur_bpw = SPI_DEFAULT_WORDLEN;
-	clrsetbits_le32(base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
-			priv->cur_bpw - 1);
+	_stm32_spi_set_wordlen(dev, SPI_DEFAULT_WORDLEN);
 
 	for (i = 0; i < ARRAY_SIZE(plat->cs_gpios); i++) {
 		if (!dm_gpio_is_valid(&plat->cs_gpios[i]))
@@ -633,6 +679,7 @@  static const struct dm_spi_ops stm32_spi_ops = {
 	.release_bus	= stm32_spi_release_bus,
 	.set_mode	= stm32_spi_set_mode,
 	.set_speed	= stm32_spi_set_speed,
+	.set_wordlen    = stm32_spi_set_wordlen,
 	.xfer		= stm32_spi_xfer,
 };