summaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-stm32-qspi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-04 19:36:53 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-04 19:36:53 -0700
commit2bca25eaeba6190efbfcb38ed169bd7ee43b5aaf (patch)
tree93e087ddb705c95c3e8f4570c95859eb5f2c4ca0 /drivers/spi/spi-stm32-qspi.c
parentd40c874573145b4af3b3b6205f3741b498697623 (diff)
parent8e9204cddcc3fea9affcfa411715ba4f66e97587 (diff)
downloadlinux-2bca25eaeba6190efbfcb38ed169bd7ee43b5aaf.tar.bz2
Merge tag 'spi-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown: "With the exception of some refactoring to fix long standing issues where we weren't handling cache syncs properly for messages which had PIO and DMA transfers going to the same page correctly there has been no work on the core this time around, and it's also been quite a quiet release for the drivers too: - Fix cache syncs for cases where we have DMA and PIO transfers in the same message going to the same page - Update the fsl_spi driver to use transfer_one() rather than a custom transfer function - Support for configuring transfer speeds with the AMD SPI controller - Support for a second chip select and 64K erase on Intel SPI - Support for Microchip coreQSPI, Nuvoton NPCM845, NXP i.MX93, and Rockchip RK3128 and RK3588" * tag 'spi-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (73 commits) spi: Ensure that sg_table won't be used after being freed spi: spi-gxp: Use devm_platform_ioremap_resource() spi: s3c64xx: Fix large transfers with DMA spi: Split transfers larger than max size spi: Fix cache corruption due to DMA/PIO overlap spi: Save current RX and TX DMA devices spi: mt65xx: Add dma max segment size declaration spi: migrate mt7621 text bindings to YAML spi: renesas,sh-msiof: Add r8a779g0 support spi: spi-fsl-qspi: Use devm_platform_ioremap_resource_byname() spi: spi-fsl-lpspi: Use devm_platform_get_and_ioremap_resource() spi: spi-fsl-dspi: Use devm_platform_get_and_ioremap_resource() spi/omap100k:Fix PM disable depth imbalance in omap1_spi100k_probe spi: dw: Fix PM disable depth imbalance in dw_spi_bt1_probe spi: cadence-quadspi: Fix PM disable depth imbalance in cqspi_probe spi: s3c24xx: Switch to use devm_spi_alloc_master() spi: xilinx: Switch to use devm_spi_alloc_master() spi: img-spfi: using pm_runtime_resume_and_get instead of pm_runtime_get_sync spi: aspeed: Remove redundant dev_err call spi: spi-mpc52xx: switch to using gpiod API ...
Diffstat (limited to 'drivers/spi/spi-stm32-qspi.c')
-rw-r--r--drivers/spi/spi-stm32-qspi.c125
1 files changed, 116 insertions, 9 deletions
diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c
index f3fe92300639..9131660c1afb 100644
--- a/drivers/spi/spi-stm32-qspi.c
+++ b/drivers/spi/spi-stm32-qspi.c
@@ -15,6 +15,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
@@ -355,10 +356,10 @@ static int stm32_qspi_get_mode(u8 buswidth)
return buswidth;
}
-static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
+static int stm32_qspi_send(struct spi_device *spi, const struct spi_mem_op *op)
{
- struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master);
- struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select];
+ struct stm32_qspi *qspi = spi_controller_get_devdata(spi->master);
+ struct stm32_qspi_flash *flash = &qspi->flash[spi->chip_select];
u32 ccr, cr;
int timeout, err = 0, err_poll_status = 0;
@@ -465,7 +466,7 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op *
qspi->fmode = CCR_FMODE_APM;
qspi->status_timeout = timeout_ms;
- ret = stm32_qspi_send(mem, op);
+ ret = stm32_qspi_send(mem->spi, op);
mutex_unlock(&qspi->lock);
pm_runtime_mark_last_busy(qspi->dev);
@@ -489,7 +490,7 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
else
qspi->fmode = CCR_FMODE_INDW;
- ret = stm32_qspi_send(mem, op);
+ ret = stm32_qspi_send(mem->spi, op);
mutex_unlock(&qspi->lock);
pm_runtime_mark_last_busy(qspi->dev);
@@ -545,7 +546,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
else
qspi->fmode = CCR_FMODE_INDR;
- ret = stm32_qspi_send(desc->mem, &op);
+ ret = stm32_qspi_send(desc->mem->spi, &op);
mutex_unlock(&qspi->lock);
pm_runtime_mark_last_busy(qspi->dev);
@@ -554,12 +555,96 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
return ret ?: len;
}
+static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl,
+ struct spi_message *msg)
+{
+ struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl);
+ struct spi_transfer *transfer;
+ struct spi_device *spi = msg->spi;
+ struct spi_mem_op op;
+ int ret = 0;
+
+ if (!spi->cs_gpiod)
+ return -EOPNOTSUPP;
+
+ ret = pm_runtime_resume_and_get(qspi->dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&qspi->lock);
+
+ gpiod_set_value_cansleep(spi->cs_gpiod, true);
+
+ list_for_each_entry(transfer, &msg->transfers, transfer_list) {
+ u8 dummy_bytes = 0;
+
+ memset(&op, 0, sizeof(op));
+
+ dev_dbg(qspi->dev, "tx_buf:%p tx_nbits:%d rx_buf:%p rx_nbits:%d len:%d dummy_data:%d\n",
+ transfer->tx_buf, transfer->tx_nbits,
+ transfer->rx_buf, transfer->rx_nbits,
+ transfer->len, transfer->dummy_data);
+
+ /*
+ * QSPI hardware supports dummy bytes transfer.
+ * If current transfer is dummy byte, merge it with the next
+ * transfer in order to take into account QSPI block constraint
+ */
+ if (transfer->dummy_data) {
+ op.dummy.buswidth = transfer->tx_nbits;
+ op.dummy.nbytes = transfer->len;
+ dummy_bytes = transfer->len;
+
+ /* if happens, means that message is not correctly built */
+ if (list_is_last(&transfer->transfer_list, &msg->transfers)) {
+ ret = -EINVAL;
+ goto end_of_transfer;
+ }
+
+ transfer = list_next_entry(transfer, transfer_list);
+ }
+
+ op.data.nbytes = transfer->len;
+
+ if (transfer->rx_buf) {
+ qspi->fmode = CCR_FMODE_INDR;
+ op.data.buswidth = transfer->rx_nbits;
+ op.data.dir = SPI_MEM_DATA_IN;
+ op.data.buf.in = transfer->rx_buf;
+ } else {
+ qspi->fmode = CCR_FMODE_INDW;
+ op.data.buswidth = transfer->tx_nbits;
+ op.data.dir = SPI_MEM_DATA_OUT;
+ op.data.buf.out = transfer->tx_buf;
+ }
+
+ ret = stm32_qspi_send(spi, &op);
+ if (ret)
+ goto end_of_transfer;
+
+ msg->actual_length += transfer->len + dummy_bytes;
+ }
+
+end_of_transfer:
+ gpiod_set_value_cansleep(spi->cs_gpiod, false);
+
+ mutex_unlock(&qspi->lock);
+
+ msg->status = ret;
+ spi_finalize_current_message(ctrl);
+
+ pm_runtime_mark_last_busy(qspi->dev);
+ pm_runtime_put_autosuspend(qspi->dev);
+
+ return ret;
+}
+
static int stm32_qspi_setup(struct spi_device *spi)
{
struct spi_controller *ctrl = spi->master;
struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl);
struct stm32_qspi_flash *flash;
- u32 presc;
+ u32 presc, mode;
int ret;
if (ctrl->busy)
@@ -568,6 +653,16 @@ static int stm32_qspi_setup(struct spi_device *spi)
if (!spi->max_speed_hz)
return -EINVAL;
+ mode = spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL);
+ if ((mode == SPI_TX_OCTAL || mode == SPI_RX_OCTAL) ||
+ ((mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) &&
+ gpiod_count(qspi->dev, "cs") == -ENOENT)) {
+ dev_err(qspi->dev, "spi-rx-bus-width\\/spi-tx-bus-width\\/cs-gpios\n");
+ dev_err(qspi->dev, "configuration not supported\n");
+
+ return -EINVAL;
+ }
+
ret = pm_runtime_resume_and_get(qspi->dev);
if (ret < 0)
return ret;
@@ -580,6 +675,16 @@ static int stm32_qspi_setup(struct spi_device *spi)
mutex_lock(&qspi->lock);
qspi->cr_reg = CR_APMS | 3 << CR_FTHRES_SHIFT | CR_SSHIFT | CR_EN;
+
+ /*
+ * Dual flash mode is only enable in case SPI_TX_OCTAL and SPI_TX_OCTAL
+ * are both set in spi->mode and "cs-gpios" properties is found in DT
+ */
+ if (mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) {
+ qspi->cr_reg |= CR_DFM;
+ dev_dbg(qspi->dev, "Dual flash mode enable");
+ }
+
writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);
/* set dcr fsize to max address */
@@ -741,11 +846,13 @@ static int stm32_qspi_probe(struct platform_device *pdev)
mutex_init(&qspi->lock);
- ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD
- | SPI_TX_DUAL | SPI_TX_QUAD;
+ ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL
+ | SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_OCTAL;
ctrl->setup = stm32_qspi_setup;
ctrl->bus_num = -1;
ctrl->mem_ops = &stm32_qspi_mem_ops;
+ ctrl->use_gpio_descriptors = true;
+ ctrl->transfer_one_message = stm32_qspi_transfer_one_message;
ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP;
ctrl->dev.of_node = dev->of_node;