2022-08-23 08:57:55

by Patrice CHOTARD

[permalink] [raw]
Subject: [PATCH v4 0/2] spi: stm32_qspi: use QSPI bus as 8 lines communication channel

From: Patrice Chotard <[email protected]>

The goal of this series is to allow to use QSPI bus as a 8 lines communication
channel for specific purpose.

The QSPI block offers the possibility to communicate with 2 flashes in
parrallel using the dual flash mode, 8 data lines are then used.
Usage of cs-gpios populated and spi-tx-bus-width / spi-rx-bus-width both set to 8,
is needed to enable dual flash mode.

The addition of the legacy transfer_one_message() spi callback is also needed
as currently the stm32-qspi driver only supports spi_controller_mem_ops API.


Patrice Chotard (2):
ARM: dts: stm32: Create separate pinmux for qspi cs pin in
stm32mp15-pinctrl.dtsi
spi: stm32_qspi: Add transfer_one_message() spi callback

arch/arm/boot/dts/stm32mp15-pinctrl.dtsi | 50 ++++++----
arch/arm/boot/dts/stm32mp157c-ev1.dts | 12 ++-
drivers/spi/spi-stm32-qspi.c | 118 +++++++++++++++++++++--
3 files changed, 152 insertions(+), 28 deletions(-)

--
2.25.1


2022-08-23 09:03:34

by Patrice CHOTARD

[permalink] [raw]
Subject: [PATCH v4 2/2] spi: stm32_qspi: Add transfer_one_message() spi callback

From: Patrice Chotard <[email protected]>

Add transfer_one_message() spi callback in order to use the QSPI interface
as a communication channel using up to 8 qspi lines (QSPI configured
in dual flash mode).
To enable this mode, both spi-rx-bus-width and spi-tx-bus-width must be
set to 8 and cs-qpios must be populated.

Signed-off-by: Patrice Chotard <[email protected]>
---
v4: _ remove useless cleanup() callback.

v3: _ add cleanup() callback.
_ rework the trigger to enable dual flash mode QSPI block feature:
_ spi-tx-bus-width and spi-rx-bus-width must be set to 8.
_ cs-gpios property must be populated.

v2: _ use parallel-memories property
_ set auto_runtime_pm to true
_ remove pm_runtime_*() usage in transfer_one_message() callback

drivers/spi/spi-stm32-qspi.c | 118 ++++++++++++++++++++++++++++++++---
1 file changed, 109 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c
index f3fe92300639..92459daca95f 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,87 @@ 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;
+
+ if (!spi->cs_gpiod)
+ return -EOPNOTSUPP;
+
+ 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))
+ 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);
+
+ 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 +644,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)) &&
+ of_gpio_named_count(qspi->dev->of_node, "cs-gpios") == -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 +666,17 @@ 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 (((spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL)) == (SPI_TX_OCTAL | SPI_RX_OCTAL)) &&
+ of_gpio_named_count(qspi->dev->of_node, "cs-gpios")) {
+ 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 +838,14 @@ 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->auto_runtime_pm = true;
ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP;
ctrl->dev.of_node = dev->of_node;

--
2.25.1

2022-08-23 19:03:32

by Mark Brown

[permalink] [raw]
Subject: Re: (subset) [PATCH v4 0/2] spi: stm32_qspi: use QSPI bus as 8 lines communication channel

On Tue, 23 Aug 2022 09:58:48 +0200, [email protected] wrote:
> From: Patrice Chotard <[email protected]>
>
> The goal of this series is to allow to use QSPI bus as a 8 lines communication
> channel for specific purpose.
>
> The QSPI block offers the possibility to communicate with 2 flashes in
> parrallel using the dual flash mode, 8 data lines are then used.
> Usage of cs-gpios populated and spi-tx-bus-width / spi-rx-bus-width both set to 8,
> is needed to enable dual flash mode.
>
> [...]

Applied to

https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next

Thanks!

[2/2] spi: stm32_qspi: Add transfer_one_message() spi callback
commit: b051161f44d414e736fa2b011245441bae9babd7

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark