diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/Kconfig | 32 | ||||
| -rw-r--r-- | drivers/spi/apple_spi.c | 3 | ||||
| -rw-r--r-- | drivers/spi/cadence_qspi.c | 16 | ||||
| -rw-r--r-- | drivers/spi/designware_spi.c | 12 | ||||
| -rw-r--r-- | drivers/spi/fsl_espi.c | 6 | ||||
| -rw-r--r-- | drivers/spi/fsl_qspi.c | 9 | ||||
| -rw-r--r-- | drivers/spi/mtk_snor.c | 193 | ||||
| -rw-r--r-- | drivers/spi/sandbox_spi.c | 17 | ||||
| -rw-r--r-- | drivers/spi/spi-aspeed-smc.c | 219 | ||||
| -rw-r--r-- | drivers/spi/spi-sunxi.c | 9 | ||||
| -rw-r--r-- | drivers/spi/spi-uclass.c | 22 | ||||
| -rw-r--r-- | drivers/spi/stm32_spi.c | 71 |
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", }, { } }; |
