| Message ID | 20260221094252.3103034-7-dario.binacchi@amarulasolutions.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
On 2/21/26 10:42, 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> > --- > > (no changes since v1) > > drivers/spi/stm32_spi.c | 62 +++++++++++++++++++++++++++++++++++++---- > 1 file changed, 57 insertions(+), 5 deletions(-) > > diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c > index adba97915cd3..39ea69c68174 100644 > --- a/drivers/spi/stm32_spi.c > +++ b/drivers/spi/stm32_spi.c > @@ -192,6 +192,11 @@ static void stm32_spi_read_rxfifo(struct udevice *bus) > log_debug("%d bytes left\n", priv->rx_len); > } > > +static bool stm32_spi_is_enabled(void __iomem *base) > +{ > + return !!(readl(base + STM32_SPI_CR1) & SPI_CR1_SPE); > +} > + In stm32_spi_stopxfer(), we first check if SPI is enabled, stm32_spi_is_enabled() can be used to replace the existing code: static void stm32_spi_stopxfer(struct udevice *dev) { struct stm32_spi_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base; u32 cr1, sr; int ret; dev_dbg(dev, "\n"); cr1 = readl(base + STM32_SPI_CR1); if (!(cr1 & SPI_CR1_SPE)) return; > static int stm32_spi_enable(void __iomem *base) > { > log_debug("\n"); > @@ -381,6 +386,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) > { > @@ -394,11 +437,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; > > @@ -406,6 +457,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) > @@ -567,9 +620,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])) > @@ -630,6 +681,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, > }; > To unsubscribe from this group and stop receiving emails from it, send an email to linux-amarula+unsubscribe@amarulasolutions.com.
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c index adba97915cd3..39ea69c68174 100644 --- a/drivers/spi/stm32_spi.c +++ b/drivers/spi/stm32_spi.c @@ -192,6 +192,11 @@ static void stm32_spi_read_rxfifo(struct udevice *bus) log_debug("%d bytes left\n", priv->rx_len); } +static bool stm32_spi_is_enabled(void __iomem *base) +{ + return !!(readl(base + STM32_SPI_CR1) & SPI_CR1_SPE); +} + static int stm32_spi_enable(void __iomem *base) { log_debug("\n"); @@ -381,6 +386,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) { @@ -394,11 +437,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; @@ -406,6 +457,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) @@ -567,9 +620,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])) @@ -630,6 +681,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, };
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> --- (no changes since v1) drivers/spi/stm32_spi.c | 62 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-)