diff options
Diffstat (limited to 'drivers/spi/stm32_spi.c')
-rw-r--r-- | drivers/spi/stm32_spi.c | 85 |
1 files changed, 67 insertions, 18 deletions
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c index 0cb04a968c..9ef405a788 100644 --- a/drivers/spi/stm32_spi.c +++ b/drivers/spi/stm32_spi.c @@ -11,6 +11,7 @@ #include <init.h> #include <errno.h> #include <linux/reset.h> +#include <linux/spi/spi-mem.h> #include <spi/spi.h> #include <linux/bitops.h> #include <clock.h> @@ -110,6 +111,24 @@ static inline struct stm32_spi_priv *to_stm32_spi_priv(struct spi_master *master return container_of(master, struct stm32_spi_priv, master); } +static int stm32_spi_get_bpw_mask(struct stm32_spi_priv *priv) +{ + u32 cfg1, max_bpw; + + /* + * The most significant bit at DSIZE bit field is reserved when the + * maximum data size of periperal instances is limited to 16-bit + */ + setbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE); + + cfg1 = readl(priv->base + STM32_SPI_CFG1); + max_bpw = FIELD_GET(SPI_CFG1_DSIZE, cfg1) + 1; + + dev_dbg(priv->master.dev, "%d-bit maximum data frame\n", max_bpw); + + return SPI_BPW_RANGE_MASK(4, max_bpw); +} + static void stm32_spi_write_txfifo(struct stm32_spi_priv *priv) { while ((priv->tx_len > 0) && @@ -189,7 +208,7 @@ static void stm32_spi_disable(struct stm32_spi_priv *priv) static void stm32_spi_stopxfer(struct stm32_spi_priv *priv) { - struct device_d *dev = priv->master.dev; + struct device *dev = priv->master.dev; u32 cr1, sr; int ret; @@ -260,19 +279,15 @@ static void stm32_spi_set_mode(struct stm32_spi_priv *priv, unsigned mode) static void stm32_spi_set_fthlv(struct stm32_spi_priv *priv, u32 xfer_len) { - u32 fthlv, half_fifo; + u32 fthlv, packet, bpw; /* data packet should not exceed 1/2 of fifo space */ - half_fifo = (priv->fifo_size / 2); - - /* data_packet should not exceed transfer length */ - fthlv = (half_fifo > xfer_len) ? xfer_len : half_fifo; + packet = clamp(xfer_len, 1U, priv->fifo_size / 2); /* align packet size with data registers access */ - fthlv -= (fthlv % 4); + bpw = DIV_ROUND_UP(priv->cur_bpw, 8); + fthlv = DIV_ROUND_UP(packet, bpw); - if (!fthlv) - fthlv = 1; clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_FTHLV, (fthlv - 1) << SPI_CFG1_FTHLV_SHIFT); } @@ -338,14 +353,22 @@ out: static int stm32_spi_transfer_one(struct stm32_spi_priv *priv, struct spi_transfer *t) { - struct device_d *dev = priv->master.dev; + struct device *dev = priv->master.dev; u32 sr; u32 ifcr = 0; u32 mode; int xfer_status = 0; + int nb_words; + + if (t->bits_per_word <= 8) + nb_words = t->len; + else if (t->bits_per_word <= 16) + nb_words = DIV_ROUND_UP(t->len * 8, 16); + else + nb_words = DIV_ROUND_UP(t->len * 8, 32); - if (t->len <= SPI_CR2_TSIZE) - writel(t->len, priv->base + STM32_SPI_CR2); + if (nb_words <= SPI_CR2_TSIZE) + writel(nb_words, priv->base + STM32_SPI_CR2); else return -EMSGSIZE; @@ -360,9 +383,11 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv, else if (!priv->rx_buf) mode = SPI_SIMPLEX_TX; - if (priv->cur_xferlen != t->len || priv->cur_mode != mode) { + if (priv->cur_xferlen != t->len || priv->cur_mode != mode || + priv->cur_bpw != t->bits_per_word) { priv->cur_mode = mode; priv->cur_xferlen = t->len; + priv->cur_bpw = t->bits_per_word; /* Disable the SPI hardware to unlock CFG1/CFG2 registers */ stm32_spi_disable(priv); @@ -372,6 +397,9 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv, stm32_spi_set_fthlv(priv, t->len); + clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE, + priv->cur_bpw - 1); + /* Enable the SPI hardware */ stm32_spi_enable(priv); } @@ -474,6 +502,24 @@ out: return ret; } +static int stm32_spi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + if (op->data.nbytes > SPI_CR2_TSIZE) + op->data.nbytes = SPI_CR2_TSIZE; + + return 0; +} + +static int stm32_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + return -ENOTSUPP; +} + +static const struct spi_controller_mem_ops stm32_spi_mem_ops = { + .adjust_op_size = stm32_spi_adjust_op_size, + .exec_op = stm32_spi_exec_op, +}; + static int stm32_spi_get_fifo_size(struct stm32_spi_priv *priv) { u32 count = 0; @@ -492,17 +538,17 @@ static int stm32_spi_get_fifo_size(struct stm32_spi_priv *priv) static void stm32_spi_dt_probe(struct stm32_spi_priv *priv) { - struct device_node *node = priv->master.dev->device_node; + struct device_node *node = priv->master.dev->of_node; int i; - priv->master.num_chipselect = of_gpio_named_count(node, "cs-gpios"); + priv->master.num_chipselect = of_gpio_count_csgpios(node); priv->cs_gpios = xzalloc(sizeof(u32) * priv->master.num_chipselect); for (i = 0; i < priv->master.num_chipselect; i++) priv->cs_gpios[i] = of_get_named_gpio(node, "cs-gpios", i); } -static int stm32_spi_probe(struct device_d *dev) +static int stm32_spi_probe(struct device *dev) { struct resource *iores; struct spi_master *master; @@ -522,6 +568,7 @@ static int stm32_spi_probe(struct device_d *dev) master->setup = stm32_spi_setup; master->transfer = stm32_spi_transfer; + master->mem_ops = &stm32_spi_mem_ops; master->bus_num = -1; stm32_spi_dt_probe(priv); @@ -540,6 +587,7 @@ static int stm32_spi_probe(struct device_d *dev) if (ret) return ret; + master->bits_per_word_mask = stm32_spi_get_bpw_mask(priv); priv->fifo_size = stm32_spi_get_fifo_size(priv); priv->cur_mode = SPI_FULL_DUPLEX; @@ -568,7 +616,7 @@ static int stm32_spi_probe(struct device_d *dev) return spi_register_master(master); } -static void stm32_spi_remove(struct device_d *dev) +static void stm32_spi_remove(struct device *dev) { struct stm32_spi_priv *priv = dev->priv; @@ -580,8 +628,9 @@ static const struct of_device_id stm32_spi_ids[] = { { .compatible = "st,stm32h7-spi", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, stm32_spi_ids); -static struct driver_d stm32_spi_driver = { +static struct driver stm32_spi_driver = { .name = "stm32_spi", .probe = stm32_spi_probe, .remove = stm32_spi_remove, |