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 <[email protected]>

---

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,
 };
 
-- 
2.43.0

Reply via email to