@@ -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> --- drivers/spi/stm32_spi.c | 62 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-)