summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig32
-rw-r--r--drivers/spi/apple_spi.c3
-rw-r--r--drivers/spi/cadence_qspi.c16
-rw-r--r--drivers/spi/designware_spi.c12
-rw-r--r--drivers/spi/fsl_espi.c6
-rw-r--r--drivers/spi/fsl_qspi.c9
-rw-r--r--drivers/spi/mtk_snor.c193
-rw-r--r--drivers/spi/sandbox_spi.c17
-rw-r--r--drivers/spi/spi-aspeed-smc.c219
-rw-r--r--drivers/spi/spi-sunxi.c9
-rw-r--r--drivers/spi/spi-uclass.c22
-rw-r--r--drivers/spi/stm32_spi.c71
12 files changed, 436 insertions, 173 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4ff17617d99..007ad5e7733 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -2,10 +2,10 @@ menuconfig SPI
bool "SPI Support"
help
The "Serial Peripheral Interface" is a low level synchronous
- protocol. Chips that support SPI can have data transfer rates
- up to several tens of Mbit/sec. Chips are addressed with a
- controller and a chipselect. Most SPI slaves don't support
- dynamic device discovery; some are even write-only or read-only.
+ protocol. Chips that support SPI can have data transfer rates
+ up to several tens of Mbit/sec. Chips are addressed with a
+ controller and a chipselect. Most SPI slaves don't support
+ dynamic device discovery; some are even write-only or read-only.
SPI is widely used by microcontrollers to talk with sensors,
eeprom and flash memory, codecs and various other controller
@@ -200,11 +200,11 @@ config CADENCE_XSPI
by using the Auto Command work mode.
config CF_SPI
- bool "ColdFire SPI driver"
- depends on M68K
- help
- Enable the ColdFire SPI driver. This driver can be used on
- some m68k SoCs.
+ bool "ColdFire SPI driver"
+ depends on M68K
+ help
+ Enable the ColdFire SPI driver. This driver can be used on
+ some m68k SoCs.
config CV1800B_SPIF
bool "Sophgo cv1800b SPI Flash Controller driver"
@@ -352,7 +352,7 @@ config MTK_SNOR
select DEVRES
help
Enable the Mediatek SPINOR controller driver. This driver has
- better read/write performance with NOR.
+ better read/write performance with NOR.
config MTK_SNFI_SPI
bool "Mediatek SPI memory controller driver"
@@ -544,11 +544,11 @@ config SPI_SIFIVE
config SOFT_SPI
bool "Soft SPI driver"
help
- Enable Soft SPI driver. This driver is to use GPIO simulate
- the SPI protocol.
+ Enable Soft SPI driver. This driver is to use GPIO simulate
+ the SPI protocol.
config SPI_SN_F_OSPI
- tristate "Socionext F_OSPI SPI flash controller"
+ bool "Socionext F_OSPI SPI flash controller"
select SPI_MEM
help
This enables support for the Socionext F_OSPI controller
@@ -565,7 +565,7 @@ config SPI_SUNXI
config STM32_OSPI
bool "STM32MP2 OSPI driver"
- depends on STM32MP25X && STM32_OMM
+ depends on (STM32MP23X || STM32MP25X) && STM32_OMM
help
Enable the STM32MP2 Octo-SPI (OSPI) driver. This driver can be
used to access the SPI NOR flash chips on platforms embedding
@@ -673,8 +673,8 @@ config ZYNQMP_GQSPI
config SPI_STACKED_PARALLEL
bool "Enable support for stacked or parallel memories"
help
- Enable support for stacked/or parallel memories. This functionality
- may appear on Xilinx hardware. By default this is disabled.
+ Enable support for stacked/or parallel memories. This functionality
+ may appear on Xilinx hardware. By default this is disabled.
endif # if DM_SPI
diff --git a/drivers/spi/apple_spi.c b/drivers/spi/apple_spi.c
index 5f94e9f7a74..acc87ea7bdc 100644
--- a/drivers/spi/apple_spi.c
+++ b/drivers/spi/apple_spi.c
@@ -228,7 +228,7 @@ static int apple_spi_set_mode(struct udevice *bus, uint mode)
return 0;
}
-struct dm_spi_ops apple_spi_ops = {
+static const struct dm_spi_ops apple_spi_ops = {
.xfer = apple_spi_xfer,
.set_speed = apple_spi_set_speed,
.set_mode = apple_spi_set_mode,
@@ -270,6 +270,7 @@ static int apple_spi_probe(struct udevice *dev)
}
static const struct udevice_id apple_spi_of_match[] = {
+ { .compatible = "apple,t8103-spi" },
{ .compatible = "apple,spi" },
{ /* sentinel */ }
};
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 2a4a49c5f1c..984d4a39ded 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -31,6 +31,8 @@
#define CQSPI_DISABLE_STIG_MODE BIT(0)
#define CQSPI_DMA_MODE BIT(1)
+#define CQSPI_RESET_DELAY_US 10
+
__weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
@@ -256,19 +258,9 @@ static int cadence_spi_probe(struct udevice *bus)
priv->resets = devm_reset_bulk_get_optional(bus);
if (priv->resets) {
- /* Assert all OSPI reset lines */
- ret = reset_assert_bulk(priv->resets);
- if (ret) {
- dev_err(bus, "Failed to assert OSPI reset: %d\n", ret);
- return ret;
- }
-
- udelay(10);
-
- /* Deassert all OSPI reset lines */
- ret = reset_deassert_bulk(priv->resets);
+ ret = reset_reset_bulk(priv->resets, CQSPI_RESET_DELAY_US);
if (ret) {
- dev_err(bus, "Failed to deassert OSPI reset: %d\n", ret);
+ dev_err(bus, "Failed to reset OSPI: %d\n", ret);
return ret;
}
}
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index b520c727900..0df9a4f5abd 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -497,6 +497,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
{
struct udevice *bus = dev->parent;
struct dw_spi_priv *priv = dev_get_priv(bus);
+ struct spi_slave *slave = dev_get_parent_priv(dev);
const u8 *tx = dout;
u8 *rx = din;
int ret = 0;
@@ -504,6 +505,17 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
u32 val;
u32 cs;
+ /* Get bits_per_word from slave (set by device driver) */
+ if (slave->bits_per_word >= 4 && slave->bits_per_word <= priv->max_xfer) {
+ priv->bits_per_word = slave->bits_per_word;
+ } else if (slave->bits_per_word == 0) {
+ priv->bits_per_word = 8; /* Default if not set */
+ } else {
+ dev_warn(dev, "Invalid bits_per_word %u, using 8\n",
+ slave->bits_per_word);
+ priv->bits_per_word = 8;
+ }
+
/* spi core configured to do 8 bit transfers */
if (bitlen % 8) {
dev_err(dev, "Non byte aligned SPI transfer.\n");
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index 117e36376b7..c5bc603b5c0 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -216,13 +216,13 @@ int espi_xfer(struct fsl_spi_slave *fsl, uint cs, unsigned int bitlen,
break;
case SPI_XFER_BEGIN | SPI_XFER_END:
len = data_len;
- buffer = (unsigned char *)malloc(len * 2);
+ buffer = (unsigned char *)malloc(len);
if (!buffer) {
debug("SF: Failed to malloc memory.\n");
return 1;
}
memcpy(buffer, data_out, len);
- rx_offset = len;
+ rx_offset = 0;
cmd_len = 0;
break;
}
@@ -275,7 +275,7 @@ int espi_xfer(struct fsl_spi_slave *fsl, uint cs, unsigned int bitlen,
}
}
if (data_in) {
- memcpy(data_in, buffer + rx_offset, tran_len);
+ memcpy(data_in, buffer + 2 * cmd_len, tran_len);
if (*buffer == 0x0b) {
data_in += tran_len;
data_len -= tran_len;
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c
index 65ab3e306d7..5692510a558 100644
--- a/drivers/spi/fsl_qspi.c
+++ b/drivers/spi/fsl_qspi.c
@@ -267,6 +267,14 @@ static const struct fsl_qspi_devtype_data ls2080a_data = {
.little_endian = true,
};
+static const struct fsl_qspi_devtype_data spacemit_k1_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_256,
+ .ahb_buf_size = SZ_128,
+ .quirks = QUADSPI_QUIRK_TKT253890,
+ .little_endian = true,
+};
+
struct fsl_qspi {
struct udevice *dev;
void __iomem *iobase;
@@ -870,6 +878,7 @@ static const struct udevice_id fsl_qspi_ids[] = {
{ .compatible = "fsl,ls1021a-qspi", .data = (ulong)&ls1021a_data, },
{ .compatible = "fsl,ls1088a-qspi", .data = (ulong)&ls2080a_data, },
{ .compatible = "fsl,ls2080a-qspi", .data = (ulong)&ls2080a_data, },
+ { .compatible = "spacemit,k1-qspi", .data = (ulong)&spacemit_k1_data, },
{ }
};
diff --git a/drivers/spi/mtk_snor.c b/drivers/spi/mtk_snor.c
index f202b2f49f5..40fc1826db6 100644
--- a/drivers/spi/mtk_snor.c
+++ b/drivers/spi/mtk_snor.c
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
-//
-// Mediatek SPI-NOR controller driver
-//
-// Copyright (C) 2020 SkyLake Huang <[email protected]>
-//
-// Some parts are based on drivers/spi/spi-mtk-nor.c of linux version
+/*
+ * Mediatek SPI-NOR controller driver
+ *
+ * Copyright (C) 2020 SkyLake Huang <[email protected]>
+ *
+ * Some parts are based on drivers/spi/spi-mtk-nor.c of linux version
+ */
#include <clk.h>
#include <cpu_func.h>
@@ -89,22 +90,33 @@
#define MTK_NOR_REG_DMA_END_DADR 0x724
#define MTK_NOR_PRG_MAX_SIZE 6
-// Reading DMA src/dst addresses have to be 16-byte aligned
+#define MTK_NOR_PRG_CNT_MAX 56
+/* Reading DMA src/dst addresses have to be 16-byte aligned */
#define MTK_NOR_DMA_ALIGN 16
#define MTK_NOR_DMA_ALIGN_MASK (MTK_NOR_DMA_ALIGN - 1)
-// and we allocate a bounce buffer if destination address isn't aligned.
+/* and we allocate a bounce buffer if destination address isn't aligned. */
#define MTK_NOR_BOUNCE_BUF_SIZE PAGE_SIZE
-// Buffered page program can do one 128-byte transfer
+/* Buffered page program can do one 128-byte transfer */
#define MTK_NOR_PP_SIZE 128
#define CLK_TO_US(priv, clkcnt) DIV_ROUND_UP(clkcnt, (priv)->spi_freq / 1000000)
#define MTK_NOR_UNLOCK_ALL 0x0
+struct mtk_snor_caps {
+ /*
+ * Some new SoCs modify the timing of fetching registers' values and IDs
+ * of NOR flash, they need a extra_bit which can add more clock cycles
+ * for fetching data.
+ */
+ u8 extra_bit;
+};
+
struct mtk_snor_priv {
struct device *dev;
void __iomem *base;
+ const struct mtk_snor_caps *caps;
u8 *buffer;
struct clk spi_clk;
struct clk ctlr_clk;
@@ -168,8 +180,8 @@ static int mtk_snor_adjust_op_size(struct spi_slave *slave,
return 0;
if (op->addr.nbytes == 3 || op->addr.nbytes == 4) {
- if (op->data.dir == SPI_MEM_DATA_IN) { //&&
- // limit size to prevent timeout calculation overflow
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ /* limit size to prevent timeout calculation overflow */
if (op->data.nbytes > 0x400000)
op->data.nbytes = 0x400000;
if (op->addr.val & MTK_NOR_DMA_ALIGN_MASK ||
@@ -262,6 +274,7 @@ static int mtk_snor_read_bounce(struct mtk_snor_priv *priv,
{
unsigned int rdlen;
int ret;
+ dma_addr_t bounce_dma;
if (op->data.nbytes & MTK_NOR_DMA_ALIGN_MASK)
rdlen = (op->data.nbytes + MTK_NOR_DMA_ALIGN) &
@@ -269,11 +282,21 @@ static int mtk_snor_read_bounce(struct mtk_snor_priv *priv,
else
rdlen = op->data.nbytes;
- ret = mtk_snor_dma_exec(priv, op->addr.val, rdlen,
- (dma_addr_t)priv->buffer);
+ /* Map bounce buffer for DMA */
+ bounce_dma = dma_map_single(priv->buffer, rdlen, DMA_FROM_DEVICE);
+ if (dma_mapping_error(priv->dev, bounce_dma)) {
+ dev_err(priv->dev, "bounce buffer dma map failed\n");
+ return -EINVAL;
+ }
- if (!ret)
+ ret = mtk_snor_dma_exec(priv, op->addr.val, rdlen, bounce_dma);
+ /* Ensure DMA writes are visible to CPU and copy the requested bytes */
+ if (!ret) {
+ /* Synchronize cached data to CPU visible memory if needed */
memcpy(op->data.buf.in, priv->buffer, op->data.nbytes);
+ }
+ /* Unmap bounce buffer regardless of success/failure */
+ dma_unmap_single(bounce_dma, rdlen, DMA_FROM_DEVICE);
return ret;
}
@@ -361,8 +384,12 @@ static int mtk_snor_pp_buffered(struct mtk_snor_priv *priv,
buf[i];
writel(val, priv->base + MTK_NOR_REG_PP_DATA);
}
- mtk_snor_cmd_exec(priv, MTK_NOR_CMD_WRITE,
- (op->data.nbytes + 5) * BITS_PER_BYTE);
+
+ ret = mtk_snor_cmd_exec(priv, MTK_NOR_CMD_WRITE,
+ (op->data.nbytes + 5) * BITS_PER_BYTE);
+ if (ret)
+ return ret;
+
return mtk_snor_write_buffer_disable(priv);
}
@@ -382,50 +409,83 @@ static int mtk_snor_pp_unbuffered(struct mtk_snor_priv *priv,
static int mtk_snor_cmd_program(struct mtk_snor_priv *priv,
const struct spi_mem_op *op)
{
- u32 tx_len = 0;
- u32 trx_len = 0;
+ int rx_len = 0;
int reg_offset = MTK_NOR_REG_PRGDATA_MAX;
+ int tx_len, prg_len;
+ int i, ret;
void __iomem *reg;
- u8 *txbuf;
- int tx_cnt = 0;
- u8 *rxbuf = op->data.buf.in;
- int i = 0;
+ u8 val;
+
+ tx_len = op->cmd.nbytes + op->addr.nbytes;
- tx_len = 1 + op->addr.nbytes + op->dummy.nbytes;
- trx_len = tx_len + op->data.nbytes;
+ /* count dummy bytes only if we need to write data after it */
if (op->data.dir == SPI_MEM_DATA_OUT)
- tx_len += op->data.nbytes;
+ tx_len += op->dummy.nbytes + op->data.nbytes;
+ else if (op->data.dir == SPI_MEM_DATA_IN)
+ rx_len = op->data.nbytes;
- txbuf = kmalloc_array(tx_len, sizeof(u8), GFP_KERNEL);
- memset(txbuf, 0x0, tx_len * sizeof(u8));
+ prg_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes +
+ op->data.nbytes;
- /* Join all bytes to be transferred */
- txbuf[tx_cnt] = op->cmd.opcode;
- tx_cnt++;
- for (i = op->addr.nbytes; i > 0; i--, tx_cnt++)
- txbuf[tx_cnt] = ((u8 *)&op->addr.val)[i - 1];
- for (i = op->dummy.nbytes; i > 0; i--, tx_cnt++)
- txbuf[tx_cnt] = 0x0;
- if (op->data.dir == SPI_MEM_DATA_OUT)
- for (i = op->data.nbytes; i > 0; i--, tx_cnt++)
- txbuf[tx_cnt] = ((u8 *)op->data.buf.out)[i - 1];
+ /*
+ * An invalid op may reach here if the caller calls exec_op without
+ * adjust_op_size. return -EINVAL instead of -ENOTSUPP so that
+ * spi-mem won't try this op again with generic spi transfers.
+ */
+ if ((tx_len > MTK_NOR_REG_PRGDATA_MAX + 1) ||
+ (rx_len > MTK_NOR_REG_SHIFT_MAX + 1) ||
+ (prg_len > MTK_NOR_PRG_CNT_MAX / 8))
+ return -EINVAL;
- for (i = MTK_NOR_REG_PRGDATA_MAX; i >= 0; i--)
- writeb(0, priv->base + MTK_NOR_REG_PRGDATA(i));
+ /* fill tx data */
- for (i = 0; i < tx_len; i++, reg_offset--)
- writeb(txbuf[i], priv->base + MTK_NOR_REG_PRGDATA(reg_offset));
+ for (i = op->cmd.nbytes; i > 0; i--, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ val = (op->cmd.opcode >> ((i - 1) * BITS_PER_BYTE)) & 0xff;
+ writeb(val, reg);
+ }
- kfree(txbuf);
+ for (i = op->addr.nbytes; i > 0; i--, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ val = (op->addr.val >> ((i - 1) * BITS_PER_BYTE)) & 0xff;
+ writeb(val, reg);
+ }
- writel(trx_len * BITS_PER_BYTE, priv->base + MTK_NOR_REG_PRG_CNT);
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ for (i = 0; i < op->dummy.nbytes; i++, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ writeb(0, reg);
+ }
- mtk_snor_cmd_exec(priv, MTK_NOR_CMD_PROGRAM, trx_len * BITS_PER_BYTE);
+ for (i = 0; i < op->data.nbytes; i++, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ writeb(((const u8 *)(op->data.buf.out))[i], reg);
+ }
+ }
- reg_offset = op->data.nbytes - 1;
- for (i = 0; i < op->data.nbytes; i++, reg_offset--) {
- reg = priv->base + MTK_NOR_REG_SHIFT(reg_offset);
- rxbuf[i] = readb(reg);
+ for (; reg_offset >= 0; reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ writeb(0, reg);
+ }
+
+ /* trigger op */
+ if (rx_len)
+ writel(prg_len * BITS_PER_BYTE + priv->caps->extra_bit,
+ priv->base + MTK_NOR_REG_PRG_CNT);
+ else
+ writel(prg_len * BITS_PER_BYTE, priv->base + MTK_NOR_REG_PRG_CNT);
+
+ ret = mtk_snor_cmd_exec(priv, MTK_NOR_CMD_PROGRAM, prg_len * BITS_PER_BYTE);
+ if (ret)
+ return ret;
+
+ /* fetch read data */
+ reg_offset = 0;
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ for (i = op->data.nbytes - 1; i >= 0; i--, reg_offset++) {
+ reg = priv->base + MTK_NOR_REG_SHIFT(reg_offset);
+ ((u8 *)(op->data.buf.in))[i] = readb(reg);
+ }
}
return 0;
@@ -467,12 +527,13 @@ static int mtk_snor_probe(struct udevice *bus)
struct mtk_snor_priv *priv = dev_get_priv(bus);
u8 *buffer;
int ret;
- u32 reg;
priv->base = devfdt_get_addr_ptr(bus);
if (!priv->base)
return -EINVAL;
+ priv->caps = (const void *)dev_get_driver_data(bus);
+
ret = clk_get_by_name(bus, "spi", &priv->spi_clk);
if (ret < 0)
return ret;
@@ -496,7 +557,8 @@ static int mtk_snor_probe(struct udevice *bus)
priv->spi_freq = clk_get_rate(&priv->spi_clk);
printf("spi frequency: %d Hz\n", priv->spi_freq);
- /* With this setting, we issue one command at a time to
+ /*
+ * With this setting, we issue one command at a time to
* accommodate to SPI-mem framework.
*/
writel(MTK_NOR_ENABLE_SF_CMD, priv->base + MTK_NOR_REG_WP);
@@ -504,24 +566,13 @@ static int mtk_snor_probe(struct udevice *bus)
mtk_snor_rmw(priv, MTK_NOR_REG_CFG3,
MTK_NOR_DISABLE_WREN | MTK_NOR_DISABLE_SR_POLL, 0);
- /* Unlock all blocks using write status command.
- * SPI-MEM hasn't implemented unlock procedure on MXIC devices.
- * We may remove this later.
- */
- writel(2 * BITS_PER_BYTE, priv->base + MTK_NOR_REG_PRG_CNT);
- writel(MTK_NOR_UNLOCK_ALL, priv->base + MTK_NOR_REG_PRGDATA(5));
- writel(MTK_NOR_IRQ_WRSR, priv->base + MTK_NOR_REG_IRQ_EN);
- writel(MTK_NOR_CMD_WRSR, priv->base + MTK_NOR_REG_CMD);
- ret = readl_poll_timeout(priv->base + MTK_NOR_REG_IRQ_STAT, reg,
- !(reg & MTK_NOR_IRQ_WRSR),
- ((3 * BITS_PER_BYTE) + 1) * 200);
-
return 0;
}
static int mtk_snor_set_speed(struct udevice *bus, uint speed)
{
- /* MTK's SNOR controller does not have a bus clock divider.
+ /*
+ * MTK's SNOR controller does not have a bus clock divider.
* We setup maximum bus clock in dts.
*/
@@ -530,8 +581,7 @@ static int mtk_snor_set_speed(struct udevice *bus, uint speed)
static int mtk_snor_set_mode(struct udevice *bus, uint mode)
{
- /* We set up mode later for each transmission.
- */
+ /* We set up mode later for each transmission. */
return 0;
}
@@ -547,8 +597,19 @@ static const struct dm_spi_ops mtk_snor_ops = {
.set_mode = mtk_snor_set_mode,
};
+static const struct mtk_snor_caps mtk_snor_caps_default = {
+ .extra_bit = 0,
+};
+
+static const struct mtk_snor_caps mtk_snor_caps_extra_bit = {
+ .extra_bit = 1,
+};
+
static const struct udevice_id mtk_snor_ids[] = {
- { .compatible = "mediatek,mtk-snor" },
+ { .compatible = "mediatek,mtk-snor", .data = (ulong)&mtk_snor_caps_default },
+ { .compatible = "mediatek,mt8188-nor", .data = (ulong)&mtk_snor_caps_extra_bit },
+ { .compatible = "mediatek,mt8189-nor", .data = (ulong)&mtk_snor_caps_extra_bit },
+ { .compatible = "mediatek,mt8195-nor", .data = (ulong)&mtk_snor_caps_default },
{}
};
diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c
index 4cc016138b1..3ee97d67f4a 100644
--- a/drivers/spi/sandbox_spi.c
+++ b/drivers/spi/sandbox_spi.c
@@ -61,6 +61,22 @@ uint sandbox_spi_get_mode(struct udevice *dev)
return priv->mode;
}
+uint sandbox_spi_get_wordlen(struct udevice *dev)
+{
+ struct spi_slave *slave = dev_get_parent_priv(dev);
+
+ return slave->wordlen;
+}
+
+static int sandbox_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
+{
+ struct spi_slave *slave = dev_get_parent_priv(dev);
+
+ slave->wordlen = wordlen;
+
+ return 0;
+}
+
static int sandbox_spi_xfer(struct udevice *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -158,6 +174,7 @@ static const struct dm_spi_ops sandbox_spi_ops = {
.set_mode = sandbox_spi_set_mode,
.cs_info = sandbox_cs_info,
.get_mmap = sandbox_spi_get_mmap,
+ .set_wordlen = sandbox_spi_set_wordlen,
};
static const struct udevice_id sandbox_spi_ids[] = {
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index ca29cfd7c88..0186b01ad9a 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -53,18 +53,20 @@ struct aspeed_spi_regs {
u32 dma_len; /* 0x8c DMA Length Register */
u32 dma_checksum; /* 0x90 Checksum Calculation Result */
u32 timings[ASPEED_SPI_MAX_CS]; /* 0x94 Read Timing Compensation */
+ u32 _reserved3[83]; /* 0xA8 - 0x1F0 */
+ u32 val_kept_wdt; /* 0x1F4 Value Kept WDT */
};
struct aspeed_spi_plat {
u8 max_cs;
- void __iomem *ahb_base; /* AHB address base for all flash devices. */
+ uintptr_t ahb_base; /* AHB address base for all flash devices. */
fdt_size_t ahb_sz; /* Overall AHB window size for all flash device. */
u32 hclk_rate; /* AHB clock rate */
};
struct aspeed_spi_flash {
- void __iomem *ahb_base;
- u32 ahb_decoded_sz;
+ uintptr_t ahb_base;
+ size_t ahb_decoded_sz;
u32 ce_ctrl_user;
u32 ce_ctrl_read;
u32 max_freq;
@@ -84,9 +86,9 @@ struct aspeed_spi_info {
u32 min_decoded_sz;
u32 clk_ctrl_mask;
void (*set_4byte)(struct udevice *bus, u32 cs);
- u32 (*segment_start)(struct udevice *bus, u32 reg);
- u32 (*segment_end)(struct udevice *bus, u32 reg);
- u32 (*segment_reg)(u32 start, u32 end);
+ uintptr_t (*segment_start)(struct udevice *bus, u32 reg);
+ uintptr_t (*segment_end)(struct udevice *bus, u32 reg);
+ u32 (*segment_reg)(uintptr_t start, uintptr_t end);
int (*adjust_decoded_sz)(struct udevice *bus);
u32 (*get_clk_setting)(struct udevice *dev, uint hz);
};
@@ -118,30 +120,30 @@ static u32 aspeed_spi_get_io_mode(u32 bus_width)
}
}
-static u32 ast2400_spi_segment_start(struct udevice *bus, u32 reg)
+static uintptr_t ast2400_spi_segment_start(struct udevice *bus, u32 reg)
{
struct aspeed_spi_plat *plat = dev_get_plat(bus);
- u32 start_offset = ((reg >> 16) & 0xff) << 23;
+ uintptr_t start_offset = ((reg >> 16) & 0xff) << 23;
if (start_offset == 0)
- return (u32)plat->ahb_base;
+ return plat->ahb_base;
- return (u32)plat->ahb_base + start_offset;
+ return plat->ahb_base + start_offset;
}
-static u32 ast2400_spi_segment_end(struct udevice *bus, u32 reg)
+static uintptr_t ast2400_spi_segment_end(struct udevice *bus, u32 reg)
{
struct aspeed_spi_plat *plat = dev_get_plat(bus);
- u32 end_offset = ((reg >> 24) & 0xff) << 23;
+ uintptr_t end_offset = ((reg >> 24) & 0xff) << 23;
/* Meaningless end_offset, set to physical ahb base. */
if (end_offset == 0)
- return (u32)plat->ahb_base;
+ return plat->ahb_base;
- return (u32)plat->ahb_base + end_offset;
+ return plat->ahb_base + end_offset;
}
-static u32 ast2400_spi_segment_reg(u32 start, u32 end)
+static u32 ast2400_spi_segment_reg(uintptr_t start, uintptr_t end)
{
if (start == end)
return 0;
@@ -206,30 +208,30 @@ static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz)
return hclk_div;
}
-static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg)
+static uintptr_t ast2500_spi_segment_start(struct udevice *bus, u32 reg)
{
struct aspeed_spi_plat *plat = dev_get_plat(bus);
- u32 start_offset = ((reg >> 16) & 0xff) << 23;
+ uintptr_t start_offset = ((reg >> 16) & 0xff) << 23;
if (start_offset == 0)
- return (u32)plat->ahb_base;
+ return plat->ahb_base;
- return (u32)plat->ahb_base + start_offset;
+ return plat->ahb_base + start_offset;
}
-static u32 ast2500_spi_segment_end(struct udevice *bus, u32 reg)
+static uintptr_t ast2500_spi_segment_end(struct udevice *bus, u32 reg)
{
struct aspeed_spi_plat *plat = dev_get_plat(bus);
- u32 end_offset = ((reg >> 24) & 0xff) << 23;
+ uintptr_t end_offset = ((reg >> 24) & 0xff) << 23;
/* Meaningless end_offset, set to physical ahb base. */
if (end_offset == 0)
- return (u32)plat->ahb_base;
+ return plat->ahb_base;
- return (u32)plat->ahb_base + end_offset;
+ return plat->ahb_base + end_offset;
}
-static u32 ast2500_spi_segment_reg(u32 start, u32 end)
+static u32 ast2500_spi_segment_reg(uintptr_t start, uintptr_t end)
{
if (start == end)
return 0;
@@ -346,30 +348,30 @@ end:
return hclk_div;
}
-static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg)
+static uintptr_t ast2600_spi_segment_start(struct udevice *bus, u32 reg)
{
struct aspeed_spi_plat *plat = dev_get_plat(bus);
- u32 start_offset = (reg << 16) & 0x0ff00000;
+ uintptr_t start_offset = (reg << 16) & 0x0ff00000;
if (start_offset == 0)
- return (u32)plat->ahb_base;
+ return plat->ahb_base;
- return (u32)plat->ahb_base + start_offset;
+ return plat->ahb_base + start_offset;
}
-static u32 ast2600_spi_segment_end(struct udevice *bus, u32 reg)
+static uintptr_t ast2600_spi_segment_end(struct udevice *bus, u32 reg)
{
struct aspeed_spi_plat *plat = dev_get_plat(bus);
- u32 end_offset = reg & 0x0ff00000;
+ uintptr_t end_offset = reg & 0x0ff00000;
/* Meaningless end_offset, set to physical ahb base. */
if (end_offset == 0)
- return (u32)plat->ahb_base;
+ return plat->ahb_base;
- return (u32)plat->ahb_base + end_offset + 0x100000;
+ return plat->ahb_base + end_offset + 0x100000;
}
-static u32 ast2600_spi_segment_reg(u32 start, u32 end)
+static u32 ast2600_spi_segment_reg(uintptr_t start, uintptr_t end)
{
if (start == end)
return 0;
@@ -473,6 +475,70 @@ static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz)
return hclk_div;
}
+static uintptr_t ast2700_spi_segment_start(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ uintptr_t start_offset = (reg & 0x0000ffff) << 16;
+
+ if (start_offset == 0)
+ return plat->ahb_base;
+
+ return plat->ahb_base + start_offset;
+}
+
+static uintptr_t ast2700_spi_segment_end(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ uintptr_t end_offset = reg & 0xffff0000;
+
+ /* Meaningless end_offset, set to physical ahb base. */
+ if (end_offset == 0)
+ return plat->ahb_base;
+
+ return plat->ahb_base + end_offset;
+}
+
+static u32 ast2700_spi_segment_reg(uintptr_t start, uintptr_t end)
+{
+ if (start == end)
+ return 0;
+
+ return (((start >> 16) & 0x7fff) | ((end + 1) & 0x7fff0000));
+}
+
+static void ast2700_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 reg_val;
+
+ reg_val = readl(&priv->regs->ctrl);
+ reg_val |= 0x11 << cs;
+ writel(reg_val, &priv->regs->ctrl);
+
+ reg_val = readl(&priv->regs->val_kept_wdt);
+ reg_val |= (0x11 << 4) << cs;
+ writel(reg_val, &priv->regs->val_kept_wdt);
+}
+
+static int ast2700_adjust_decoded_size(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct aspeed_spi_flash *flashes = &priv->flashes[0];
+ int ret;
+ int cs;
+
+ /* Close unused CS. */
+ for (cs = priv->num_cs; cs < plat->max_cs; cs++)
+ flashes[cs].ahb_decoded_sz = 0;
+
+ ret = aspeed_spi_trim_decoded_size(bus);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
/*
* As the flash size grows up, we need to trim some decoded
* size if needed for the sake of conforming the maximum
@@ -512,12 +578,12 @@ static int aspeed_spi_trim_decoded_size(struct udevice *bus)
return 0;
}
-static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf,
+static int aspeed_spi_read_from_ahb(uintptr_t ahb_base, void *buf,
size_t len)
{
size_t offset = 0;
- if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) &&
+ if (IS_ALIGNED(ahb_base, sizeof(uintptr_t)) &&
IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
readsl(ahb_base, buf, len >> 2);
offset = len & ~0x3;
@@ -529,12 +595,12 @@ static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf,
return 0;
}
-static int aspeed_spi_write_to_ahb(void __iomem *ahb_base, const void *buf,
+static int aspeed_spi_write_to_ahb(uintptr_t ahb_base, const void *buf,
size_t len)
{
size_t offset = 0;
- if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) &&
+ if (IS_ALIGNED(ahb_base, sizeof(uintptr_t)) &&
IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
writesl(ahb_base, buf, len >> 2);
offset = len & ~0x3;
@@ -589,7 +655,7 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
struct aspeed_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
u32 cs = slave_plat->cs[0];
- u32 ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+ uintptr_t ce_ctrl_reg = (uintptr_t)&priv->regs->ce_ctrl[cs];
u32 ce_ctrl_val;
struct aspeed_spi_flash *flash = &priv->flashes[cs];
u8 dummy_data[16] = {0};
@@ -602,7 +668,7 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
op->data.nbytes, op->data.buswidth);
if (priv->info == &ast2400_spi_info)
- ce_ctrl_reg = (u32)&priv->regs->ctrl;
+ ce_ctrl_reg = (uintptr_t)&priv->regs->ctrl;
/*
* Set controller to 4-byte address mode
@@ -670,7 +736,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
u32 i;
u32 cs = slave_plat->cs[0];
u32 cmd_io_conf;
- u32 ce_ctrl_reg;
+ uintptr_t ce_ctrl_reg;
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
/*
@@ -681,9 +747,9 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
return -EOPNOTSUPP;
}
- ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+ ce_ctrl_reg = (uintptr_t)&priv->regs->ce_ctrl[cs];
if (info == &ast2400_spi_info)
- ce_ctrl_reg = (u32)&priv->regs->ctrl;
+ ce_ctrl_reg = (uintptr_t)&priv->regs->ctrl;
if (desc->info.length > 0x1000000)
priv->info->set_4byte(bus, cs);
@@ -693,7 +759,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
priv->flashes[cs].ahb_decoded_sz = desc->info.length;
for (i = 0; i < priv->num_cs; i++) {
- dev_dbg(dev, "cs: %d, sz: 0x%x\n", i,
+ dev_dbg(dev, "cs: %d, sz: 0x%zx\n", i,
priv->flashes[cs].ahb_decoded_sz);
}
@@ -728,7 +794,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
u32 cs = slave_plat->cs[0];
int ret;
- dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%x\n",
+ dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%zx\n",
desc->info.op_tmpl.cmd.opcode, offs, len);
if (priv->flashes[cs].ahb_decoded_sz < offs + len ||
@@ -738,7 +804,10 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
if (ret != 0)
return 0;
} else {
- memcpy_fromio(buf, priv->flashes[cs].ahb_base + offs, len);
+ memcpy_fromio(buf,
+ (void __iomem *)(priv->flashes[cs].ahb_base +
+ (uintptr_t)offs),
+ len);
}
return len;
@@ -783,19 +852,19 @@ static void aspeed_spi_decoded_range_set(struct udevice *bus)
struct aspeed_spi_plat *plat = dev_get_plat(bus);
struct aspeed_spi_priv *priv = dev_get_priv(bus);
u32 decoded_reg_val;
- u32 start_addr, end_addr;
+ uintptr_t start_addr, end_addr;
u32 cs;
for (cs = 0; cs < plat->max_cs; cs++) {
- start_addr = (u32)priv->flashes[cs].ahb_base;
- end_addr = (u32)priv->flashes[cs].ahb_base +
+ start_addr = priv->flashes[cs].ahb_base;
+ end_addr = priv->flashes[cs].ahb_base +
priv->flashes[cs].ahb_decoded_sz;
decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
writel(decoded_reg_val, &priv->regs->segment_addr[cs]);
- dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
+ dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%lx, end: 0x%lx\n",
cs, decoded_reg_val, start_addr, end_addr);
}
}
@@ -851,13 +920,13 @@ static int aspeed_spi_decoded_ranges_sanity(struct udevice *bus)
* address base are monotonic increasing with CE#.
*/
for (cs = plat->max_cs - 1; cs > 0; cs--) {
- if ((u32)priv->flashes[cs].ahb_base != 0 &&
- (u32)priv->flashes[cs].ahb_base <
- (u32)priv->flashes[cs - 1].ahb_base +
+ if (priv->flashes[cs].ahb_base != 0 &&
+ priv->flashes[cs].ahb_base <
+ priv->flashes[cs - 1].ahb_base +
priv->flashes[cs - 1].ahb_decoded_sz) {
- dev_err(bus, "decoded range overlay 0x%08x 0x%08x\n",
- (u32)priv->flashes[cs].ahb_base,
- (u32)priv->flashes[cs - 1].ahb_base);
+ dev_err(bus, "decoded range overlay 0x%08lx 0x%08lx\n",
+ priv->flashes[cs].ahb_base,
+ priv->flashes[cs - 1].ahb_base);
return -EINVAL;
}
}
@@ -895,14 +964,13 @@ static int aspeed_spi_read_fixed_decoded_ranges(struct udevice *bus)
return ret;
for (i = 0; i < count; i++) {
- priv->flashes[ranges[i].cs].ahb_base =
- (void __iomem *)ranges[i].ahb_base;
+ priv->flashes[ranges[i].cs].ahb_base = ranges[i].ahb_base;
priv->flashes[ranges[i].cs].ahb_decoded_sz =
ranges[i].sz;
}
for (i = 0; i < plat->max_cs; i++) {
- dev_dbg(bus, "ahb_base: 0x%p, size: 0x%08x\n",
+ dev_dbg(bus, "ahb_base: 0x%lx, size: 0x%08zx\n",
priv->flashes[i].ahb_base,
priv->flashes[i].ahb_decoded_sz);
}
@@ -1063,6 +1131,32 @@ static const struct aspeed_spi_info ast2600_spi_info = {
.get_clk_setting = ast2600_get_clk_setting,
};
+static const struct aspeed_spi_info ast2700_fmc_info = {
+ .io_mode_mask = 0xf0000000,
+ .max_bus_width = 4,
+ .min_decoded_sz = 0x10000,
+ .clk_ctrl_mask = 0x0f000f00,
+ .set_4byte = ast2700_spi_chip_set_4byte,
+ .segment_start = ast2700_spi_segment_start,
+ .segment_end = ast2700_spi_segment_end,
+ .segment_reg = ast2700_spi_segment_reg,
+ .adjust_decoded_sz = ast2700_adjust_decoded_size,
+ .get_clk_setting = ast2600_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2700_spi_info = {
+ .io_mode_mask = 0xf0000000,
+ .max_bus_width = 4,
+ .min_decoded_sz = 0x10000,
+ .clk_ctrl_mask = 0x0f000f00,
+ .set_4byte = ast2700_spi_chip_set_4byte,
+ .segment_start = ast2700_spi_segment_start,
+ .segment_end = ast2700_spi_segment_end,
+ .segment_reg = ast2700_spi_segment_reg,
+ .adjust_decoded_sz = ast2700_adjust_decoded_size,
+ .get_clk_setting = ast2600_get_clk_setting,
+};
+
static int aspeed_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
@@ -1129,7 +1223,8 @@ static int apseed_spi_of_to_plat(struct udevice *bus)
return -EINVAL;
}
- plat->ahb_base = devfdt_get_addr_size_index_ptr(bus, 1, &plat->ahb_sz);
+ plat->ahb_base =
+ (uintptr_t)devfdt_get_addr_size_index_ptr(bus, 1, &plat->ahb_sz);
if (!plat->ahb_base) {
dev_err(bus, "wrong AHB base\n");
return -EINVAL;
@@ -1147,8 +1242,8 @@ static int apseed_spi_of_to_plat(struct udevice *bus)
plat->hclk_rate = clk_get_rate(&hclk);
- dev_dbg(bus, "ctrl_base = 0x%x, ahb_base = 0x%p, size = 0x%llx\n",
- (u32)priv->regs, plat->ahb_base, (fdt64_t)plat->ahb_sz);
+ dev_dbg(bus, "ctrl_base = 0x%p, ahb_base = 0x%lx, size = 0x%llx\n",
+ priv->regs, plat->ahb_base, (fdt64_t)plat->ahb_sz);
dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
plat->hclk_rate / 1000000, plat->max_cs);
@@ -1199,6 +1294,8 @@ static const struct udevice_id aspeed_spi_ids[] = {
{ .compatible = "aspeed,ast2500-spi", .data = (ulong)&ast2500_spi_info, },
{ .compatible = "aspeed,ast2600-fmc", .data = (ulong)&ast2600_fmc_info, },
{ .compatible = "aspeed,ast2600-spi", .data = (ulong)&ast2600_spi_info, },
+ { .compatible = "aspeed,ast2700-fmc", .data = (ulong)&ast2700_fmc_info, },
+ { .compatible = "aspeed,ast2700-spi", .data = (ulong)&ast2700_spi_info, },
{ }
};
diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c
index 0bdc112d249..08b603f04a2 100644
--- a/drivers/spi/spi-sunxi.c
+++ b/drivers/spi/spi-sunxi.c
@@ -344,7 +344,7 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
struct sun4i_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 len = bitlen / 8;
+ u32 rst, val, len = bitlen / 8;
u8 nbytes;
int ret;
@@ -360,8 +360,11 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
sun4i_spi_set_cs(bus, slave_plat->cs[0], true);
/* Reset FIFOs */
- setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) |
- SPI_BIT(priv, SPI_FCR_TF_RST));
+ rst = SPI_BIT(priv, SPI_FCR_RF_RST) | SPI_BIT(priv, SPI_FCR_TF_RST);
+ setbits_le32(SPI_REG(priv, SPI_FCR), rst);
+ ret = readl_poll_timeout(SPI_REG(priv, SPI_FCR), val, !(rst & val), 20);
+ if (ret)
+ return -EBUSY;
while (len) {
/* Setup the transfer now... */
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 6b7ad47c22d..120565df149 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -88,6 +88,20 @@ void dm_spi_release_bus(struct udevice *dev)
ops->release_bus(dev);
}
+int dm_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
+{
+ struct udevice *bus = dev->parent;
+ struct dm_spi_ops *ops = spi_get_ops(bus);
+
+ if (bus->uclass->uc_drv->id != UCLASS_SPI)
+ return -EOPNOTSUPP;
+
+ if (!ops->set_wordlen)
+ return -ENOSYS;
+
+ return ops->set_wordlen(dev, wordlen);
+}
+
int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -141,6 +155,11 @@ int spi_set_speed(struct spi_slave *slave, uint hz)
return ret;
}
+int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
+{
+ return dm_spi_set_wordlen(slave->dev, wordlen);
+}
+
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -245,6 +264,7 @@ static int spi_child_post_bind(struct udevice *dev)
}
plat->mode = mode;
+ plat->wordlen = SPI_DEFAULT_WORDLEN;
return 0;
}
@@ -277,7 +297,7 @@ static int spi_child_pre_probe(struct udevice *dev)
slave->max_hz = plat->max_hz;
slave->mode = plat->mode;
- slave->wordlen = SPI_DEFAULT_WORDLEN;
+ slave->wordlen = plat->wordlen;
return 0;
}
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
index a1f31cf653c..c93a749cd5b 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");
@@ -245,9 +250,7 @@ static void stm32_spi_stopxfer(struct udevice *dev)
dev_dbg(dev, "\n");
- cr1 = readl(base + STM32_SPI_CR1);
-
- if (!(cr1 & SPI_CR1_SPE))
+ if (!stm32_spi_is_enabled(base))
return;
/* Wait on EOT or suspend the flow */
@@ -381,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 - %ld]\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,18 +435,28 @@ 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;
priv->tx_buf = dout;
priv->rx_buf = din;
- priv->tx_len = priv->tx_buf ? bitlen / 8 : 0;
- priv->rx_len = priv->rx_buf ? bitlen / 8 : 0;
+ 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 +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]))
@@ -630,10 +679,12 @@ 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,
};
static const struct udevice_id stm32_spi_ids[] = {
+ { .compatible = "st,stm32mp25-spi", },
{ .compatible = "st,stm32h7-spi", },
{ }
};