[PATCH] spi: spi-fsl-lpspi: Handle clock polarity and phase

Marek Vasut posted 1 patch 1 week, 3 days ago
drivers/spi/spi-fsl-lpspi.c | 54 ++++++++++++++++++++++++++++++++++---
1 file changed, 50 insertions(+), 4 deletions(-)
[PATCH] spi: spi-fsl-lpspi: Handle clock polarity and phase
Posted by Marek Vasut 1 week, 3 days ago
The LPSPI driver currently does not support setting SPI bus clock
polarity and phase, add support for it.

It is important to configure correct initial clock polarity and phase
before the GPIO chipselect toggles, otherwise a chip attached to the
bus might recognize the first change of clock signal as the first
clock cycle and get confused.

In order to set up the correct polarity and phase on the clock signal
before the GPIO chipselects get configured by the SPI core, the
controller has to be briefly brought up in fsl_lpspi_prepare_message().
The fsl_lpspi_prepare_message() behaves like a zero-length transfer
which always uses PIO and never DMA, and which leaves the clock signal
in the correct state at the end of such transfer, which happens before
the GPIO chipselect toggles.

Signed-off-by: Marek Vasut <marex@nabladev.com>
---
Cc: Fedor Ross <fedor.ross@ifm.com>
Cc: Frank Li <Frank.Li@nxp.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: imx@lists.linux.dev
Cc: linux-kernel@vger.kernel.org
Cc: linux-spi@vger.kernel.org
---
 drivers/spi/spi-fsl-lpspi.c | 54 ++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 6c692568bdf58..b361c1bb3e431 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -281,7 +281,8 @@ static void fsl_lpspi_read_rx_fifo(struct fsl_lpspi_data *fsl_lpspi)
 		fsl_lpspi->rx(fsl_lpspi);
 }
 
-static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi)
+static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi,
+			      struct spi_device *spi)
 {
 	u32 temp = 0;
 
@@ -303,6 +304,13 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi)
 				temp |= TCR_CONTC;
 		}
 	}
+
+	if (spi->mode & SPI_CPOL)
+		temp |= TCR_CPOL;
+
+	if (spi->mode & SPI_CPHA)
+		temp |= TCR_CPHA;
+
 	writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
 
 	dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp);
@@ -488,12 +496,45 @@ static int fsl_lpspi_setup_transfer(struct spi_controller *controller,
 
 	fsl_lpspi->watermark = min(fsl_lpspi->txfifosize, t->len);
 
+	return fsl_lpspi_config(fsl_lpspi);
+}
+
+static int fsl_lpspi_prepare_message(struct spi_controller *controller,
+				     struct spi_message *msg)
+{
+	struct fsl_lpspi_data *fsl_lpspi =
+				spi_controller_get_devdata(controller);
+	struct spi_device *spi = msg->spi;
+	struct spi_transfer *t;
+	int ret;
+
+	t = list_first_entry_or_null(&msg->transfers, struct spi_transfer,
+				     transfer_list);
+	if (!t)
+		return 0;
+
+	fsl_lpspi->is_first_byte = true;
+	fsl_lpspi->usedma = false;
+	ret = fsl_lpspi_setup_transfer(controller, spi, t);
+
 	if (fsl_lpspi_can_dma(controller, spi, t))
 		fsl_lpspi->usedma = true;
 	else
 		fsl_lpspi->usedma = false;
 
-	return fsl_lpspi_config(fsl_lpspi);
+	if (ret < 0)
+		return ret;
+
+	fsl_lpspi_set_cmd(fsl_lpspi, spi);
+
+	/* No IRQs */
+	writel(0, fsl_lpspi->base + IMX7ULP_IER);
+
+	/* Controller disable, clear FIFOs, clear status */
+	writel(CR_RRF | CR_RTF, fsl_lpspi->base + IMX7ULP_CR);
+	writel(SR_CLEAR_MASK, fsl_lpspi->base + IMX7ULP_SR);
+
+	return 0;
 }
 
 static int fsl_lpspi_target_abort(struct spi_controller *controller)
@@ -753,14 +794,18 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller,
 					spi_controller_get_devdata(controller);
 	int ret;
 
-	fsl_lpspi->is_first_byte = true;
+	if (fsl_lpspi_can_dma(controller, spi, t))
+		fsl_lpspi->usedma = true;
+	else
+		fsl_lpspi->usedma = false;
+
 	ret = fsl_lpspi_setup_transfer(controller, spi, t);
 	if (ret < 0)
 		return ret;
 
 	t->effective_speed_hz = fsl_lpspi->config.effective_speed_hz;
 
-	fsl_lpspi_set_cmd(fsl_lpspi);
+	fsl_lpspi_set_cmd(fsl_lpspi, spi);
 	fsl_lpspi->is_first_byte = false;
 
 	if (fsl_lpspi->usedma)
@@ -944,6 +989,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
 	}
 
 	controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
+	controller->prepare_message = fsl_lpspi_prepare_message;
 	controller->transfer_one = fsl_lpspi_transfer_one;
 	controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware;
 	controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware;
-- 
2.51.0
Re: [PATCH] spi: spi-fsl-lpspi: Handle clock polarity and phase
Posted by Mark Brown 1 week, 2 days ago
On Tue, 27 Jan 2026 23:23:22 +0100, Marek Vasut wrote:
> The LPSPI driver currently does not support setting SPI bus clock
> polarity and phase, add support for it.
> 
> It is important to configure correct initial clock polarity and phase
> before the GPIO chipselect toggles, otherwise a chip attached to the
> bus might recognize the first change of clock signal as the first
> clock cycle and get confused.
> 
> [...]

Applied to

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

Thanks!

[1/1] spi: spi-fsl-lpspi: Handle clock polarity and phase
      commit: 7ae4d097b752d77c41dd26068d92ef34e9c66be9

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
Re: [PATCH] spi: spi-fsl-lpspi: Handle clock polarity and phase
Posted by Frank Li 1 week, 2 days ago
On Tue, Jan 27, 2026 at 11:23:22PM +0100, Marek Vasut wrote:
> The LPSPI driver currently does not support setting SPI bus clock
> polarity and phase, add support for it.
>
> It is important to configure correct initial clock polarity and phase
> before the GPIO chipselect toggles, otherwise a chip attached to the
> bus might recognize the first change of clock signal as the first
> clock cycle and get confused.
>
> In order to set up the correct polarity and phase on the clock signal
> before the GPIO chipselects get configured by the SPI core, the
> controller has to be briefly brought up in fsl_lpspi_prepare_message().
> The fsl_lpspi_prepare_message() behaves like a zero-length transfer
> which always uses PIO and never DMA, and which leaves the clock signal
> in the correct state at the end of such transfer, which happens before
> the GPIO chipselect toggles.
>
> Signed-off-by: Marek Vasut <marex@nabladev.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> ---
> Cc: Fedor Ross <fedor.ross@ifm.com>
> Cc: Frank Li <Frank.Li@nxp.com>
> Cc: Mark Brown <broonie@kernel.org>
> Cc: imx@lists.linux.dev
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-spi@vger.kernel.org
> ---
>  drivers/spi/spi-fsl-lpspi.c | 54 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 50 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
> index 6c692568bdf58..b361c1bb3e431 100644
> --- a/drivers/spi/spi-fsl-lpspi.c
> +++ b/drivers/spi/spi-fsl-lpspi.c
> @@ -281,7 +281,8 @@ static void fsl_lpspi_read_rx_fifo(struct fsl_lpspi_data *fsl_lpspi)
>  		fsl_lpspi->rx(fsl_lpspi);
>  }
>
> -static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi)
> +static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi,
> +			      struct spi_device *spi)
>  {
>  	u32 temp = 0;
>
> @@ -303,6 +304,13 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi)
>  				temp |= TCR_CONTC;
>  		}
>  	}
> +
> +	if (spi->mode & SPI_CPOL)
> +		temp |= TCR_CPOL;
> +
> +	if (spi->mode & SPI_CPHA)
> +		temp |= TCR_CPHA;
> +
>  	writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
>
>  	dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp);
> @@ -488,12 +496,45 @@ static int fsl_lpspi_setup_transfer(struct spi_controller *controller,
>
>  	fsl_lpspi->watermark = min(fsl_lpspi->txfifosize, t->len);
>
> +	return fsl_lpspi_config(fsl_lpspi);
> +}
> +
> +static int fsl_lpspi_prepare_message(struct spi_controller *controller,
> +				     struct spi_message *msg)
> +{
> +	struct fsl_lpspi_data *fsl_lpspi =
> +				spi_controller_get_devdata(controller);
> +	struct spi_device *spi = msg->spi;
> +	struct spi_transfer *t;
> +	int ret;
> +
> +	t = list_first_entry_or_null(&msg->transfers, struct spi_transfer,
> +				     transfer_list);
> +	if (!t)
> +		return 0;
> +
> +	fsl_lpspi->is_first_byte = true;
> +	fsl_lpspi->usedma = false;
> +	ret = fsl_lpspi_setup_transfer(controller, spi, t);
> +
>  	if (fsl_lpspi_can_dma(controller, spi, t))
>  		fsl_lpspi->usedma = true;
>  	else
>  		fsl_lpspi->usedma = false;
>
> -	return fsl_lpspi_config(fsl_lpspi);
> +	if (ret < 0)
> +		return ret;
> +
> +	fsl_lpspi_set_cmd(fsl_lpspi, spi);
> +
> +	/* No IRQs */
> +	writel(0, fsl_lpspi->base + IMX7ULP_IER);
> +
> +	/* Controller disable, clear FIFOs, clear status */
> +	writel(CR_RRF | CR_RTF, fsl_lpspi->base + IMX7ULP_CR);
> +	writel(SR_CLEAR_MASK, fsl_lpspi->base + IMX7ULP_SR);
> +
> +	return 0;
>  }
>
>  static int fsl_lpspi_target_abort(struct spi_controller *controller)
> @@ -753,14 +794,18 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller,
>  					spi_controller_get_devdata(controller);
>  	int ret;
>
> -	fsl_lpspi->is_first_byte = true;
> +	if (fsl_lpspi_can_dma(controller, spi, t))
> +		fsl_lpspi->usedma = true;
> +	else
> +		fsl_lpspi->usedma = false;
> +
>  	ret = fsl_lpspi_setup_transfer(controller, spi, t);
>  	if (ret < 0)
>  		return ret;
>
>  	t->effective_speed_hz = fsl_lpspi->config.effective_speed_hz;
>
> -	fsl_lpspi_set_cmd(fsl_lpspi);
> +	fsl_lpspi_set_cmd(fsl_lpspi, spi);
>  	fsl_lpspi->is_first_byte = false;
>
>  	if (fsl_lpspi->usedma)
> @@ -944,6 +989,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
>  	}
>
>  	controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
> +	controller->prepare_message = fsl_lpspi_prepare_message;
>  	controller->transfer_one = fsl_lpspi_transfer_one;
>  	controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware;
>  	controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware;
> --
> 2.51.0
>