Linux SPI subsystem development
 help / color / mirror / Atom feed
* [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support
@ 2026-11-03  7:07 Binbin Zhou
  2026-11-03  7:07 ` [PATCH 1/8] spi: loongson: Fix incorrect SPI mode caching logic Binbin Zhou
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:07 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

Hi all:

This series fixes several long-standing bugs in the Loongson SPI
controller driver, cleans up the core driver, and adds interrupt-driven
transfers with a polling fallback.

The series is grouped as follows:

  Bug fixes (suitable for stable):
    1. Fix the SPI mode caching logic so mode bits can be cleared.
    2. Fix the chip-select idle level for SPI_CS_HIGH devices.
    3. Separate PM register state from runtime register usage.
    4. Fix the clock divider calculation with an explicit divider table.

  Robustness and cleanup:
    5. Support the ACPI "clock-frequency" property and validate the
       input clock rate during probe.
    6. Move private register definitions and the private controller
       structure into the core driver.
    7. Convert the driver to the regmap-mmio API.

  New feature:
    8. Add interrupt-driven transfers, keeping polling as a fallback
       when no IRQ is available.

The IRQ path also brings a significant performance improvement. On a
Loongson-2K3000 board, reading SPI flash with dd improved from about
50 kB/s in polling mode to about 382 kB/s with IRQ-driven transfers,
roughly 7.6x faster.

Binbin.
Thanks.

Binbin Zhou (8):
  spi: loongson: Fix incorrect SPI mode caching logic
  spi: loongson: Fix CS idle state for SPI_CS_HIGH devices
  spi: loongson: Make suspend/resume registers PM-specific
  spi: loongson: Fix clock divider calculation with explicit table
  spi: loongson: Support ACPI clock-frequency and add error handling
  spi: loongson: Move private definitions into the core driver
  spi: loongson: Convert to regmap API
  spi: loongson: Add interrupt support for SPI controller

 drivers/spi/spi-loongson-core.c | 566 ++++++++++++++++++++++++--------
 drivers/spi/spi-loongson-pci.c  |  25 +-
 drivers/spi/spi-loongson-plat.c |  17 +-
 drivers/spi/spi-loongson.h      |  48 +--
 4 files changed, 457 insertions(+), 199 deletions(-)


base-commit: 8ab8c079820e6134bc0d8c312244644afb66ae9c
-- 
2.52.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/8] spi: loongson: Fix incorrect SPI mode caching logic
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
@ 2026-11-03  7:07 ` Binbin Zhou
  2026-11-03  7:07 ` [PATCH 2/8] spi: loongson: Fix CS idle state for SPI_CS_HIGH devices Binbin Zhou
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:07 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

The current driver caches the SPI mode using bitwise OR:
    loongson_spi->mode |= spi->mode;

This is incorrect because once a mode bit (e.g., SPI_CPOL or SPI_CPHA)
is set, it can never be cleared. If a device switches its mode settings,
the cached state will not match the actual hardware configuration, causing
loongson_spi_update_state() to miscalculate when a reprogram is needed.

Fix this by directly assigning the new mode:
    loongson_spi->mode = spi->mode;

Fixes: 6c7a864007b6 ("spi: loongson: add bus driver for the loongson spi controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index 6ba7c56b1bdd..e95a9af94772 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -72,7 +72,7 @@ static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
 		val |= LOONGSON_SPI_SPCR_CPHA;
 
 	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val);
-	loongson_spi->mode |= spi->mode;
+	loongson_spi->mode = spi->mode;
 }
 
 static int loongson_spi_update_state(struct loongson_spi *loongson_spi,
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/8] spi: loongson: Fix CS idle state for SPI_CS_HIGH devices
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
  2026-11-03  7:07 ` [PATCH 1/8] spi: loongson: Fix incorrect SPI mode caching logic Binbin Zhou
@ 2026-11-03  7:07 ` Binbin Zhou
  2026-11-03  7:07 ` [PATCH 3/8] spi: loongson: Make suspend/resume registers PM-specific Binbin Zhou
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:07 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

In loongson_spi_setup(), the chip select (CS) is unconditionally driven
high (loongson_spi_set_cs(spi, true)) to set it to the idle state.

However, for devices that require active-high chip select
(SPI_CS_HIGH), driving the CS line high actually asserts the device
selection. This may cause the device to misinterpret bus traffic or
enter an unexpected state before the first transfer begins.

Fix this by using !(spi->mode & SPI_CS_HIGH) to ensure the CS pin is
driven to the correct deasserted (idle) physical level based on the
device's polarity configuration.

Fixes: 6c7a864007b6 ("spi: loongson: add bus driver for the loongson spi controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index e95a9af94772..deda754e214d 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -99,7 +99,7 @@ static int loongson_spi_setup(struct spi_device *spi)
 		return -EINVAL;
 
 	loongson_spi->hz = 0;
-	loongson_spi_set_cs(spi, true);
+	loongson_spi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
 
 	return 0;
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/8] spi: loongson: Make suspend/resume registers PM-specific
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
  2026-11-03  7:07 ` [PATCH 1/8] spi: loongson: Fix incorrect SPI mode caching logic Binbin Zhou
  2026-11-03  7:07 ` [PATCH 2/8] spi: loongson: Fix CS idle state for SPI_CS_HIGH devices Binbin Zhou
@ 2026-11-03  7:07 ` Binbin Zhou
  2026-11-04 10:02   ` Huacai Chen
  2026-11-03  7:08 ` [PATCH 4/8] spi: loongson: Fix clock divider calculation with explicit table Binbin Zhou
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:07 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

Currently, the driver reuses fields like spcr, sper, spsr, and para
within struct loongson_spi for multiple purposes (runtime setup,
polling, and PM state). This state overloading makes the code fragile.
For example, set_clk() modifies spcr/sper during normal operations,
which can corrupt the saved state right before a suspend.

To fix this, move these register caches into a dedicated struct
loongson_spi_suspend_store (lss) so they are exclusively used by the
PM callbacks. Other functions (set_clk, write_read_8bit, message
handling) are updated to use local variables instead of mutating the
global PM state.

Fixes: 6c7a864007b6 ("spi: loongson: add bus driver for the loongson spi controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 80 ++++++++++++++++++---------------
 drivers/spi/spi-loongson.h      | 14 +++---
 2 files changed, 52 insertions(+), 42 deletions(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index deda754e214d..9f3acf583984 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -40,22 +40,23 @@ static void loongson_spi_set_cs(struct spi_device *spi, bool en)
 
 static void loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
 {
-	unsigned char val;
-	unsigned int div, div_tmp;
 	static const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};
+	unsigned char val, spr, spre;
+	unsigned int div, div_tmp;
 
 	div = clamp_val(DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz), 2, 4096);
 	div_tmp = rdiv[fls(div - 1)];
-	loongson_spi->spcr = (div_tmp & GENMASK(1, 0)) >> 0;
-	loongson_spi->sper = (div_tmp & GENMASK(3, 2)) >> 2;
+
+	spr = (div_tmp & GENMASK(1, 0)) >> 0;
 	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
 	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val |
-			       loongson_spi->spcr);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val | spr);
+
+	spre = (div_tmp & GENMASK(3, 2)) >> 2;
 	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
 	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val |
-			       loongson_spi->sper);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val | spre);
+
 	loongson_spi->hz = hz;
 }
 
@@ -107,8 +108,9 @@ static int loongson_spi_setup(struct spi_device *spi)
 static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_buf,
 					u8 **rx_buf, unsigned int num)
 {
-	int ret;
 	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
+	unsigned char status;
+	int ret;
 
 	if (tx_buf && *tx_buf)
 		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
@@ -116,8 +118,8 @@ static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_bu
 		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, 0);
 
 	ret = readb_poll_timeout(loongson_spi->base + LOONGSON_SPI_SPSR_REG,
-				 loongson_spi->spsr, (loongson_spi->spsr &
-				 LOONGSON_SPI_SPSR_RFEMPTY) != LOONGSON_SPI_SPSR_RFEMPTY,
+				 status,
+				 !(status & LOONGSON_SPI_SPSR_RFEMPTY),
 				 1, USEC_PER_MSEC);
 
 	if (rx_buf && *rx_buf)
@@ -148,10 +150,11 @@ static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *
 static int loongson_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
 {
 	struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctlr);
+	unsigned char para;
 
-	loongson_spi->para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para &
-			       ~LOONGSON_SPI_PARA_MEM_EN);
+	para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
+			       para & ~LOONGSON_SPI_PARA_MEM_EN);
 
 	return 0;
 }
@@ -171,8 +174,12 @@ static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_dev
 static int loongson_spi_unprepare_message(struct spi_controller *ctrl, struct spi_message *m)
 {
 	struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctrl);
+	unsigned char para;
 
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para);
+	/* Restore the hardware SPI Flash read path */
+	para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
+			       para | LOONGSON_SPI_PARA_MEM_EN);
 
 	return 0;
 }
@@ -231,38 +238,37 @@ EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
 
 static int loongson_spi_suspend(struct device *dev)
 {
-	struct loongson_spi *loongson_spi;
-	struct spi_controller *controller;
-
-	controller = dev_get_drvdata(dev);
-	spi_controller_suspend(controller);
+	struct spi_controller *controller = dev_get_drvdata(dev);
+	struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
+	int ret;
 
-	loongson_spi = spi_controller_get_devdata(controller);
+	ret = spi_controller_suspend(controller);
+	if (ret)
+		return ret;
 
-	loongson_spi->spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
-	loongson_spi->sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
-	loongson_spi->spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
-	loongson_spi->para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
-	loongson_spi->sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
-	loongson_spi->timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
+	loongson_spi->lss.spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
+	loongson_spi->lss.sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
+	loongson_spi->lss.spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
+	loongson_spi->lss.para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
+	loongson_spi->lss.sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
+	loongson_spi->lss.timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
 
 	return 0;
 }
 
 static int loongson_spi_resume(struct device *dev)
 {
-	struct loongson_spi *loongson_spi;
-	struct spi_controller *controller;
+	struct spi_controller *controller = dev_get_drvdata(dev);
+	struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
 
-	controller = dev_get_drvdata(dev);
-	loongson_spi = spi_controller_get_devdata(controller);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->lss.spcr);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->lss.sper);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->lss.para);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->lss.sfcs);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->lss.timi);
 
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->spcr);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->sper);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG, loongson_spi->spsr);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->sfcs);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->timi);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG,
+			       LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
 
 	spi_controller_resume(controller);
 
diff --git a/drivers/spi/spi-loongson.h b/drivers/spi/spi-loongson.h
index 35f95b161842..b2e73fa12420 100644
--- a/drivers/spi/spi-loongson.h
+++ b/drivers/spi/spi-loongson.h
@@ -29,19 +29,23 @@
 struct device;
 struct spi_controller;
 
-struct loongson_spi {
-	struct	spi_controller	*controller;
-	void __iomem		*base;
-	int			cs_active;
-	unsigned int		hz;
+struct loongson_spi_suspend_store {
 	unsigned char		spcr;
 	unsigned char		sper;
 	unsigned char		spsr;
 	unsigned char		para;
 	unsigned char		sfcs;
 	unsigned char		timi;
+};
+
+struct loongson_spi {
+	struct	spi_controller	*controller;
+	void __iomem		*base;
+	int			cs_active;
+	unsigned int		hz;
 	unsigned int		mode;
 	u64			clk_rate;
+	struct loongson_spi_suspend_store lss;
 };
 
 int loongson_spi_init_controller(struct device *dev, void __iomem *reg);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/8] spi: loongson: Fix clock divider calculation with explicit table
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
                   ` (2 preceding siblings ...)
  2026-11-03  7:07 ` [PATCH 3/8] spi: loongson: Make suspend/resume registers PM-specific Binbin Zhou
@ 2026-11-03  7:08 ` Binbin Zhou
  2026-11-03  7:08 ` [PATCH 5/8] spi: loongson: Support ACPI clock-frequency and add error handling Binbin Zhou
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:08 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

loongson_spi_set_clk() previously converted the requested divider into
SPCR.SPR/SPER.SPRE using rdiv[] and fls(div - 1).  The rdiv[] mapping did
not match the actual divider encoding, so the programmed divider was
usually one step larger than requested, and the maximum divider could
index past the end of rdiv[].

For example:

- Requested divider 2: fls(2 - 1) = 1, rdiv[1] = 1, which encodes
  divider 4, so the output clock is half the requested rate.
- Requested divider 4: fls(4 - 1) = 2, rdiv[2] = 4, which encodes
  divider 8, again halving the output clock.
- Requested divider 8: fls(8 - 1) = 3, rdiv[3] = 2, which encodes
  divider 16.
- Requested divider 4096: div is clamped to 4096, fls(4095) = 12, and
  rdiv[12] is out of bounds because rdiv[] only has 12 entries.

Replace this with loongson_spi_div_table[], which explicitly maps each
supported divider from 2 to 4096 to its SPR/SPRE encoding, and select
the first entry whose divider is greater than or equal to the requested
value.  This ensures the actual SPI clock does not exceed the requested
rate.  Also make loongson_spi_set_clk() return an error and propagate it
to the caller, and validate hz and clk_rate before touching the hardware.

Fixes: 6c7a864007b6 ("spi: loongson: add bus driver for the loongson spi controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 68 ++++++++++++++++++++++++++-------
 drivers/spi/spi-loongson.h      |  3 ++
 2 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index 9f3acf583984..083157786790 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -16,6 +16,25 @@
 
 #include "spi-loongson.h"
 
+static const struct {
+	unsigned int	div;
+	unsigned char	spr;
+	unsigned char	spre;
+} loongson_spi_div_table[] = {
+	{   2, 0, 0 },
+	{   4, 1, 0 },
+	{   8, 0, 1 },
+	{  16, 2, 0 },
+	{  32, 3, 0 },
+	{  64, 1, 1 },
+	{ 128, 2, 1 },
+	{ 256, 3, 1 },
+	{ 512, 0, 2 },
+	{1024, 1, 2 },
+	{2048, 2, 2 },
+	{4096, 3, 2 },
+};
+
 static inline void loongson_spi_write_reg(struct loongson_spi *spi, unsigned char reg,
 					  unsigned char data)
 {
@@ -38,26 +57,40 @@ static void loongson_spi_set_cs(struct spi_device *spi, bool en)
 	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, val | cs);
 }
 
-static void loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
+static int loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
 {
-	static const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};
-	unsigned char val, spr, spre;
-	unsigned int div, div_tmp;
+	unsigned long div;
+	unsigned char val;
+	unsigned int i;
 
-	div = clamp_val(DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz), 2, 4096);
-	div_tmp = rdiv[fls(div - 1)];
+	if (!hz || !loongson_spi->clk_rate)
+		return -EINVAL;
+
+	div = DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz);
+	if (div < LOONGSON_SPI_DIV_MIN)
+		div = LOONGSON_SPI_DIV_MIN;
+
+	for (i = 0; i < ARRAY_SIZE(loongson_spi_div_table); i++) {
+		if (loongson_spi_div_table[i].div >= div)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(loongson_spi_div_table))
+		return -EINVAL;
 
-	spr = (div_tmp & GENMASK(1, 0)) >> 0;
 	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
 	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val | spr);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG,
+			       val | loongson_spi_div_table[i].spr);
 
-	spre = (div_tmp & GENMASK(3, 2)) >> 2;
 	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
 	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val | spre);
+	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG,
+			       val | loongson_spi_div_table[i].spre);
 
 	loongson_spi->hz = hz;
+
+	return 0;
 }
 
 static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
@@ -79,8 +112,13 @@ static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
 static int loongson_spi_update_state(struct loongson_spi *loongson_spi,
 				struct spi_device *spi, struct spi_transfer *t)
 {
-	if (t && loongson_spi->hz != t->speed_hz)
-		loongson_spi_set_clk(loongson_spi, t->speed_hz);
+	int ret;
+
+	if (t && loongson_spi->hz != t->speed_hz) {
+		ret = loongson_spi_set_clk(loongson_spi, t->speed_hz);
+		if (ret)
+			return ret;
+	}
 
 	if ((spi->mode ^ loongson_spi->mode) & SPI_MODE_X_MASK)
 		loongson_spi_set_mode(loongson_spi, spi);
@@ -163,8 +201,12 @@ static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_dev
 				     struct spi_transfer *xfer)
 {
 	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
+	int ret;
+
+	ret = loongson_spi_update_state(loongson_spi, spi, xfer);
+	if (ret)
+		return ret;
 
-	loongson_spi_update_state(loongson_spi, spi, xfer);
 	if (xfer->len)
 		return loongson_spi_write_read(spi, xfer);
 
diff --git a/drivers/spi/spi-loongson.h b/drivers/spi/spi-loongson.h
index b2e73fa12420..89efd4ce69d8 100644
--- a/drivers/spi/spi-loongson.h
+++ b/drivers/spi/spi-loongson.h
@@ -26,6 +26,9 @@
 #define	LOONGSON_SPI_SPSR_WCOL	BIT(6)
 #define	LOONGSON_SPI_SPSR_SPIF	BIT(7)
 
+#define LOONGSON_SPI_DIV_MIN		2
+#define LOONGSON_SPI_DIV_MAX		4096
+
 struct device;
 struct spi_controller;
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 5/8] spi: loongson: Support ACPI clock-frequency and add error handling
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
                   ` (3 preceding siblings ...)
  2026-11-03  7:08 ` [PATCH 4/8] spi: loongson: Fix clock divider calculation with explicit table Binbin Zhou
@ 2026-11-03  7:08 ` Binbin Zhou
  2026-11-03  7:08 ` [PATCH 6/8] spi: loongson: Move private definitions into the core driver Binbin Zhou
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:08 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

On platforms using ACPI (e.g., certain Loongson-2K3000 boards), the clock
framework might not be available, or clk_get_rate() may return 0. This
causes the driver to calculate an incorrect clock divider.

Introduce a helper function loongson_spi_get_clk_rate() to resolve the
input clock rate. It first attempts to read the rate via the clk framework.
If that fails or returns 0, it falls back to reading the standard
"clock-frequency" device property (which covers ACPI and DT).

Additionally, add proper error checking to ensure the driver fails to
probe gracefully if neither method provides a valid clock rate.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index 083157786790..91d7acbe4302 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -242,11 +242,32 @@ static void loongson_spi_reginit(struct loongson_spi *loongson_spi_dev)
 	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
 }
 
+static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
+{
+	unsigned long rate;
+	struct clk *clk;
+	u32 freq;
+
+	clk = devm_clk_get_optional(dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	rate = clk_get_rate(clk);
+	if (!rate && !device_property_read_u32(dev, "clock-frequency", &freq))
+		rate = freq;
+
+	if (!rate)
+		return -EINVAL;
+
+	*clk_rate = rate;
+	return 0;
+}
+
 int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
 {
 	struct spi_controller *controller;
 	struct loongson_spi *spi;
-	struct clk *clk;
+	int ret;
 
 	controller = devm_spi_alloc_host(dev, sizeof(struct loongson_spi));
 	if (controller == NULL)
@@ -265,11 +286,10 @@ int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
 	spi->base = regs;
 	spi->controller = controller;
 
-	clk = devm_clk_get_optional(dev, NULL);
-	if (IS_ERR(clk))
-		return dev_err_probe(dev, PTR_ERR(clk), "unable to get clock\n");
+	ret = loongson_spi_get_clk_rate(dev, &spi->clk_rate);
+	if (ret)
+		return dev_err_probe(dev, ret, "unable to get clock rate");
 
-	spi->clk_rate = clk_get_rate(clk);
 	loongson_spi_reginit(spi);
 
 	spi->mode = 0;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 6/8] spi: loongson: Move private definitions into the core driver
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
                   ` (4 preceding siblings ...)
  2026-11-03  7:08 ` [PATCH 5/8] spi: loongson: Support ACPI clock-frequency and add error handling Binbin Zhou
@ 2026-11-03  7:08 ` Binbin Zhou
  2026-11-04 10:04   ` Huacai Chen
  2026-11-03  7:08 ` [PATCH 7/8] spi: loongson: Convert to regmap API Binbin Zhou
  2026-11-03  7:08 ` [PATCH 8/8] spi: loongson: Add interrupt support for SPI controller Binbin Zhou
  7 siblings, 1 reply; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:08 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

The Loongson SPI register definitions and the private controller
structure are only used by the core driver.  Move them from the shared
header into spi-loongson-core.c and rename the structure to
loongson_spi_priv to make it clear that it is driver-private data.

The shared header now only contains the exported init function and the
PM ops declaration, which reduces the surface exposed to the PCI and
platform glue drivers.

While at it, update the copyright years and add MODULE_AUTHOR() entries
to the PCI and platform drivers.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 252 +++++++++++++++++++-------------
 drivers/spi/spi-loongson-pci.c  |  19 +--
 drivers/spi/spi-loongson-plat.c |  11 +-
 drivers/spi/spi-loongson.h      |  53 +------
 4 files changed, 173 insertions(+), 162 deletions(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index 91d7acbe4302..082248f29ae7 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -1,21 +1,71 @@
 // SPDX-License-Identifier: GPL-2.0+
-// Loongson SPI Support
-// Copyright (C) 2023 Loongson Technology Corporation Limited
+//
+// Loongson SPI Controller driver
+//
+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
 
 #include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/export.h>
 #include <linux/init.h>
-#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
-#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spi/spi.h>
 
 #include "spi-loongson.h"
 
+/* SPI controller register offsets */
+#define LOONGSON_SPI_SPCR_REG		0x00
+#define LOONGSON_SPI_SPSR_REG		0x01
+#define LOONGSON_SPI_FIFO_REG		0x02
+#define LOONGSON_SPI_SPER_REG		0x03
+#define LOONGSON_SPI_PARA_REG		0x04
+#define LOONGSON_SPI_SFCS_REG		0x05
+#define LOONGSON_SPI_TIMI_REG		0x06
+
+/* SPCR bits */
+#define LOONGSON_SPI_SPCR_SPR		GENMASK(1, 0)
+#define LOONGSON_SPI_SPCR_CPHA		BIT(2)
+#define LOONGSON_SPI_SPCR_CPOL		BIT(3)
+#define LOONGSON_SPI_SPCR_SPE		BIT(6)
+#define LOONGSON_SPI_SPCR_SPIE		BIT(7)
+
+/* SPSR bits */
+#define LOONGSON_SPI_SPSR_RFEMPTY	BIT(0)
+#define LOONGSON_SPI_SPSR_RFFULL	BIT(1)
+#define LOONGSON_SPI_SPSR_WFEMPTY	BIT(2)
+#define LOONGSON_SPI_SPSR_WFFULL	BIT(3)
+#define LOONGSON_SPI_SPSR_WCOL		BIT(6)
+#define LOONGSON_SPI_SPSR_SPIF		BIT(7)
+
+/* SPER bits */
+#define LOONGSON_SPI_SPER_SPRE		GENMASK(1, 0)
+
+/* SFC_PARAM bits */
+#define LOONGSON_SPI_PARA_MEM_EN	BIT(0)
+
+#define LOONGSON_SPI_DIV_MIN		2
+#define LOONGSON_SPI_DIV_MAX		4096
+
+struct loongson_spi_suspend_store {
+	unsigned char		spcr;
+	unsigned char		sper;
+	unsigned char		spsr;
+	unsigned char		para;
+	unsigned char		sfcs;
+	unsigned char		timi;
+};
+
+struct loongson_spi_priv {
+	struct spi_controller	*controller;
+	void __iomem		*base;
+	unsigned int		hz;
+	unsigned int		mode;
+	u64			clk_rate;
+	struct loongson_spi_suspend_store lss;
+};
+
 static const struct {
 	unsigned int	div;
 	unsigned char	spr;
@@ -35,38 +85,38 @@ static const struct {
 	{4096, 3, 2 },
 };
 
-static inline void loongson_spi_write_reg(struct loongson_spi *spi, unsigned char reg,
+static inline void loongson_spi_write_reg(struct loongson_spi_priv *priv, unsigned char reg,
 					  unsigned char data)
 {
-	writeb(data, spi->base + reg);
+	writeb(data, priv->base + reg);
 }
 
-static inline char loongson_spi_read_reg(struct loongson_spi *spi, unsigned char reg)
+static inline char loongson_spi_read_reg(struct loongson_spi_priv *priv, unsigned char reg)
 {
-	return readb(spi->base + reg);
+	return readb(priv->base + reg);
 }
 
 static void loongson_spi_set_cs(struct spi_device *spi, bool en)
 {
-	int cs;
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
 	unsigned char mask = (BIT(4) | BIT(0)) << spi_get_chipselect(spi, 0);
-	unsigned char val = en ? mask :  (BIT(0) << spi_get_chipselect(spi, 0));
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
+	unsigned char val = en ? mask : (BIT(0) << spi_get_chipselect(spi, 0));
+	int cs;
 
-	cs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG) & ~mask;
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, val | cs);
+	cs = loongson_spi_read_reg(priv, LOONGSON_SPI_SFCS_REG) & ~mask;
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SFCS_REG, val | cs);
 }
 
-static int loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
+static int loongson_spi_set_clk(struct loongson_spi_priv *priv, unsigned int hz)
 {
 	unsigned long div;
 	unsigned char val;
 	unsigned int i;
 
-	if (!hz || !loongson_spi->clk_rate)
+	if (!hz || !priv->clk_rate)
 		return -EINVAL;
 
-	div = DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz);
+	div = DIV_ROUND_UP_ULL(priv->clk_rate, hz);
 	if (div < LOONGSON_SPI_DIV_MIN)
 		div = LOONGSON_SPI_DIV_MIN;
 
@@ -78,106 +128,106 @@ static int loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int
 	if (i == ARRAY_SIZE(loongson_spi_div_table))
 		return -EINVAL;
 
-	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
+	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
 	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG,
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG,
 			       val | loongson_spi_div_table[i].spr);
 
-	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
+	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPER_REG);
 	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG,
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPER_REG,
 			       val | loongson_spi_div_table[i].spre);
 
-	loongson_spi->hz = hz;
+	priv->hz = hz;
 
 	return 0;
 }
 
-static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
+static void loongson_spi_set_mode(struct loongson_spi_priv *priv,
 				  struct spi_device *spi)
 {
 	unsigned char val;
 
-	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
+	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
 	val &= ~(LOONGSON_SPI_SPCR_CPOL | LOONGSON_SPI_SPCR_CPHA);
+
 	if (spi->mode & SPI_CPOL)
 		val |= LOONGSON_SPI_SPCR_CPOL;
+
 	if (spi->mode & SPI_CPHA)
 		val |= LOONGSON_SPI_SPCR_CPHA;
 
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val);
-	loongson_spi->mode = spi->mode;
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
+	priv->mode = spi->mode;
 }
 
-static int loongson_spi_update_state(struct loongson_spi *loongson_spi,
-				struct spi_device *spi, struct spi_transfer *t)
+static int loongson_spi_update_state(struct loongson_spi_priv *priv, struct spi_device *spi,
+				     struct spi_transfer *t)
 {
 	int ret;
 
-	if (t && loongson_spi->hz != t->speed_hz) {
-		ret = loongson_spi_set_clk(loongson_spi, t->speed_hz);
+	if (t && priv->hz != t->speed_hz) {
+		ret = loongson_spi_set_clk(priv, t->speed_hz);
 		if (ret)
 			return ret;
 	}
 
-	if ((spi->mode ^ loongson_spi->mode) & SPI_MODE_X_MASK)
-		loongson_spi_set_mode(loongson_spi, spi);
+	if ((spi->mode ^ priv->mode) & SPI_MODE_X_MASK)
+		loongson_spi_set_mode(priv, spi);
 
 	return 0;
 }
 
 static int loongson_spi_setup(struct spi_device *spi)
 {
-	struct loongson_spi *loongson_spi;
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
 
-	loongson_spi = spi_controller_get_devdata(spi->controller);
 	if (spi->bits_per_word % 8)
 		return -EINVAL;
 
 	if (spi_get_chipselect(spi, 0) >= spi->controller->num_chipselect)
 		return -EINVAL;
 
-	loongson_spi->hz = 0;
+	priv->hz = 0;
 	loongson_spi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
 
 	return 0;
 }
 
 static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_buf,
-					u8 **rx_buf, unsigned int num)
+					u8 **rx_buf)
 {
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
 	unsigned char status;
 	int ret;
 
 	if (tx_buf && *tx_buf)
-		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
+		loongson_spi_write_reg(priv, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
 	else
-		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, 0);
+		loongson_spi_write_reg(priv, LOONGSON_SPI_FIFO_REG, 0);
 
-	ret = readb_poll_timeout(loongson_spi->base + LOONGSON_SPI_SPSR_REG,
+	ret = readb_poll_timeout(priv->base + LOONGSON_SPI_SPSR_REG,
 				 status,
 				 !(status & LOONGSON_SPI_SPSR_RFEMPTY),
 				 1, USEC_PER_MSEC);
 
 	if (rx_buf && *rx_buf)
-		*(*rx_buf)++ = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_FIFO_REG);
+		*(*rx_buf)++ = loongson_spi_read_reg(priv, LOONGSON_SPI_FIFO_REG);
 	else
-		loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_FIFO_REG);
+		loongson_spi_read_reg(priv, LOONGSON_SPI_FIFO_REG);
 
 	return ret;
 }
 
 static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
 {
-	int ret;
-	unsigned int count;
+	unsigned int count = xfer->len;
 	const u8 *tx = xfer->tx_buf;
 	u8 *rx = xfer->rx_buf;
+	int ret;
 
-	count = xfer->len;
 	do {
-		ret = loongson_spi_write_read_8bit(spi, &tx, &rx, count);
+		ret = loongson_spi_write_read_8bit(spi, &tx, &rx);
 		if (ret)
 			break;
 	} while (--count);
@@ -185,25 +235,25 @@ static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *
 	return ret;
 }
 
-static int loongson_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
+static int loongson_spi_prepare_message(struct spi_controller *host, struct spi_message *m)
 {
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctlr);
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
 	unsigned char para;
 
-	para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
+	para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG,
 			       para & ~LOONGSON_SPI_PARA_MEM_EN);
 
 	return 0;
 }
 
-static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_device *spi,
+static int loongson_spi_transfer_one(struct spi_controller *host, struct spi_device *spi,
 				     struct spi_transfer *xfer)
 {
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
 	int ret;
 
-	ret = loongson_spi_update_state(loongson_spi, spi, xfer);
+	ret = loongson_spi_update_state(priv, spi, xfer);
 	if (ret)
 		return ret;
 
@@ -213,33 +263,33 @@ static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_dev
 	return 0;
 }
 
-static int loongson_spi_unprepare_message(struct spi_controller *ctrl, struct spi_message *m)
+static int loongson_spi_unprepare_message(struct spi_controller *host, struct spi_message *m)
 {
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctrl);
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
 	unsigned char para;
 
 	/* Restore the hardware SPI Flash read path */
-	para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
+	para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG,
 			       para | LOONGSON_SPI_PARA_MEM_EN);
 
 	return 0;
 }
 
-static void loongson_spi_reginit(struct loongson_spi *loongson_spi_dev)
+static void loongson_spi_reginit(struct loongson_spi_priv *priv)
 {
 	unsigned char val;
 
-	val = loongson_spi_read_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG);
+	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
 	val &= ~LOONGSON_SPI_SPCR_SPE;
-	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
 
-	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPSR_REG,
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPSR_REG,
 			       (LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL));
 
-	val = loongson_spi_read_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG);
+	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
 	val |= LOONGSON_SPI_SPCR_SPE;
-	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
 }
 
 static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
@@ -265,74 +315,73 @@ static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
 
 int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
 {
-	struct spi_controller *controller;
-	struct loongson_spi *spi;
+	struct loongson_spi_priv *priv;
+	struct spi_controller *host;
 	int ret;
 
-	controller = devm_spi_alloc_host(dev, sizeof(struct loongson_spi));
-	if (controller == NULL)
+	host = devm_spi_alloc_host(dev, sizeof(*priv));
+	if (!host)
 		return -ENOMEM;
 
-	controller->mode_bits = SPI_MODE_X_MASK | SPI_CS_HIGH;
-	controller->setup = loongson_spi_setup;
-	controller->prepare_message = loongson_spi_prepare_message;
-	controller->transfer_one = loongson_spi_transfer_one;
-	controller->unprepare_message = loongson_spi_unprepare_message;
-	controller->set_cs = loongson_spi_set_cs;
-	controller->num_chipselect = 4;
-	dev_set_drvdata(dev, controller);
+	host->mode_bits = SPI_MODE_X_MASK | SPI_CS_HIGH;
+	host->setup = loongson_spi_setup;
+	host->prepare_message = loongson_spi_prepare_message;
+	host->transfer_one = loongson_spi_transfer_one;
+	host->unprepare_message = loongson_spi_unprepare_message;
+	host->set_cs = loongson_spi_set_cs;
+	host->num_chipselect = 4;
+	dev_set_drvdata(dev, host);
 
-	spi = spi_controller_get_devdata(controller);
-	spi->base = regs;
-	spi->controller = controller;
+	priv = spi_controller_get_devdata(host);
+	priv->base = regs;
+	priv->controller = host;
 
-	ret = loongson_spi_get_clk_rate(dev, &spi->clk_rate);
+	ret = loongson_spi_get_clk_rate(dev, &priv->clk_rate);
 	if (ret)
 		return dev_err_probe(dev, ret, "unable to get clock rate");
 
-	loongson_spi_reginit(spi);
-
-	spi->mode = 0;
+	loongson_spi_reginit(priv);
+	priv->mode = 0;
 
-	return devm_spi_register_controller(dev, controller);
+	return devm_spi_register_controller(dev, host);
 }
 EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
 
 static int loongson_spi_suspend(struct device *dev)
 {
-	struct spi_controller *controller = dev_get_drvdata(dev);
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
+	struct spi_controller *host = dev_get_drvdata(dev);
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
 	int ret;
 
-	ret = spi_controller_suspend(controller);
+	ret = spi_controller_suspend(host);
 	if (ret)
 		return ret;
 
-	loongson_spi->lss.spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
-	loongson_spi->lss.sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
-	loongson_spi->lss.spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
-	loongson_spi->lss.para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
-	loongson_spi->lss.sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
-	loongson_spi->lss.timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
+	priv->lss.spcr = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
+	priv->lss.sper = loongson_spi_read_reg(priv, LOONGSON_SPI_SPER_REG);
+	priv->lss.spsr = loongson_spi_read_reg(priv, LOONGSON_SPI_SPSR_REG);
+	priv->lss.para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
+	priv->lss.sfcs = loongson_spi_read_reg(priv, LOONGSON_SPI_SFCS_REG);
+	priv->lss.timi = loongson_spi_read_reg(priv, LOONGSON_SPI_TIMI_REG);
 
 	return 0;
 }
 
 static int loongson_spi_resume(struct device *dev)
 {
-	struct spi_controller *controller = dev_get_drvdata(dev);
-	struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
+	struct spi_controller *host = dev_get_drvdata(dev);
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
 
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->lss.spcr);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->lss.sper);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->lss.para);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->lss.sfcs);
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->lss.timi);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, priv->lss.spcr);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPER_REG, priv->lss.sper);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG, priv->lss.para);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SFCS_REG, priv->lss.sfcs);
+	loongson_spi_write_reg(priv, LOONGSON_SPI_TIMI_REG, priv->lss.timi);
 
-	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG,
+	loongson_spi_write_reg(priv, LOONGSON_SPI_SPSR_REG,
 			       LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
 
-	spi_controller_resume(controller);
+	spi_controller_resume(host);
 
 	return 0;
 }
@@ -342,4 +391,5 @@ DEFINE_SIMPLE_DEV_PM_OPS(loongson_spi_dev_pm_ops, loongson_spi_suspend, loongson
 EXPORT_SYMBOL_NS_GPL(loongson_spi_dev_pm_ops, "SPI_LOONGSON_CORE");
 
 MODULE_DESCRIPTION("Loongson SPI core driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
 MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-loongson-pci.c b/drivers/spi/spi-loongson-pci.c
index ccc26efd88fe..ad90be956e1e 100644
--- a/drivers/spi/spi-loongson-pci.c
+++ b/drivers/spi/spi-loongson-pci.c
@@ -1,31 +1,31 @@
 // SPDX-License-Identifier: GPL-2.0+
+//
 // PCI interface driver for Loongson SPI Support
-// Copyright (C) 2023 Loongson Technology Corporation Limited
+//
+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
 
 #include <linux/pci.h>
 
 #include "spi-loongson.h"
 
-static int loongson_spi_pci_register(struct pci_dev *pdev,
-			const struct pci_device_id *ent)
+static int loongson_spi_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-	int ret;
-	void __iomem *reg_base;
 	struct device *dev = &pdev->dev;
-	int pci_bar = 0;
+	void __iomem *reg_base;
+	int pci_bar = 0, ret;
 
 	ret = pcim_enable_device(pdev);
 	if (ret < 0)
-		return dev_err_probe(dev, ret, "cannot enable pci device\n");
+		return dev_err_probe(dev, ret, "cannot enable pci device");
 
 	reg_base = pcim_iomap_region(pdev, pci_bar, pci_name(pdev));
 	ret = PTR_ERR_OR_ZERO(reg_base);
 	if (ret)
-		return dev_err_probe(dev, ret, "failed to request and remap memory\n");
+		return dev_err_probe(dev, ret, "failed to request and remap memory");
 
 	ret = loongson_spi_init_controller(dev, reg_base);
 	if (ret)
-		return dev_err_probe(dev, ret, "failed to initialize controller\n");
+		return dev_err_probe(dev, ret, "failed to initialize controller");
 
 	return 0;
 }
@@ -49,5 +49,6 @@ static struct pci_driver loongson_spi_pci_driver = {
 module_pci_driver(loongson_spi_pci_driver);
 
 MODULE_DESCRIPTION("Loongson spi pci driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
 MODULE_LICENSE("GPL");
 MODULE_IMPORT_NS("SPI_LOONGSON_CORE");
diff --git a/drivers/spi/spi-loongson-plat.c b/drivers/spi/spi-loongson-plat.c
index 6f8202264341..dcfbdd50e996 100644
--- a/drivers/spi/spi-loongson-plat.c
+++ b/drivers/spi/spi-loongson-plat.c
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0+
+//
 // Platform driver for Loongson SPI Support
-// Copyright (C) 2023 Loongson Technology Corporation Limited
+//
+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
 
 #include <linux/err.h>
 #include <linux/platform_device.h>
@@ -9,9 +11,9 @@
 
 static int loongson_spi_platform_probe(struct platform_device *pdev)
 {
-	int ret;
-	void __iomem *reg_base;
 	struct device *dev = &pdev->dev;
+	void __iomem *reg_base;
+	int ret;
 
 	reg_base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(reg_base))
@@ -19,7 +21,7 @@ static int loongson_spi_platform_probe(struct platform_device *pdev)
 
 	ret = loongson_spi_init_controller(dev, reg_base);
 	if (ret)
-		return dev_err_probe(dev, ret, "failed to initialize controller\n");
+		return dev_err_probe(dev, ret, "failed to initialize controller");
 
 	return 0;
 }
@@ -42,5 +44,6 @@ static struct platform_driver loongson_spi_plat_driver = {
 module_platform_driver(loongson_spi_plat_driver);
 
 MODULE_DESCRIPTION("Loongson spi platform driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
 MODULE_LICENSE("GPL");
 MODULE_IMPORT_NS("SPI_LOONGSON_CORE");
diff --git a/drivers/spi/spi-loongson.h b/drivers/spi/spi-loongson.h
index 89efd4ce69d8..c8b722bc5fb5 100644
--- a/drivers/spi/spi-loongson.h
+++ b/drivers/spi/spi-loongson.h
@@ -1,56 +1,13 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* Header File for Loongson SPI Driver. */
-/* Copyright (C) 2023 Loongson Technology Corporation Limited */
+//
+// Loongson SPI Support - common header
+//
+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
 
 #ifndef __LINUX_SPI_LOONGSON_H
 #define __LINUX_SPI_LOONGSON_H
 
-#include <linux/bits.h>
-#include <linux/pm.h>
-#include <linux/types.h>
-
-#define	LOONGSON_SPI_SPCR_REG	0x00
-#define	LOONGSON_SPI_SPSR_REG	0x01
-#define	LOONGSON_SPI_FIFO_REG	0x02
-#define	LOONGSON_SPI_SPER_REG	0x03
-#define	LOONGSON_SPI_PARA_REG	0x04
-#define	LOONGSON_SPI_SFCS_REG	0x05
-#define	LOONGSON_SPI_TIMI_REG	0x06
-
-/* Bits definition for Loongson SPI register */
-#define	LOONGSON_SPI_PARA_MEM_EN	BIT(0)
-#define	LOONGSON_SPI_SPCR_CPHA	BIT(2)
-#define	LOONGSON_SPI_SPCR_CPOL	BIT(3)
-#define	LOONGSON_SPI_SPCR_SPE	BIT(6)
-#define	LOONGSON_SPI_SPSR_RFEMPTY	BIT(0)
-#define	LOONGSON_SPI_SPSR_WCOL	BIT(6)
-#define	LOONGSON_SPI_SPSR_SPIF	BIT(7)
-
-#define LOONGSON_SPI_DIV_MIN		2
-#define LOONGSON_SPI_DIV_MAX		4096
-
-struct device;
-struct spi_controller;
-
-struct loongson_spi_suspend_store {
-	unsigned char		spcr;
-	unsigned char		sper;
-	unsigned char		spsr;
-	unsigned char		para;
-	unsigned char		sfcs;
-	unsigned char		timi;
-};
-
-struct loongson_spi {
-	struct	spi_controller	*controller;
-	void __iomem		*base;
-	int			cs_active;
-	unsigned int		hz;
-	unsigned int		mode;
-	u64			clk_rate;
-	struct loongson_spi_suspend_store lss;
-};
-
 int loongson_spi_init_controller(struct device *dev, void __iomem *reg);
 extern const struct dev_pm_ops loongson_spi_dev_pm_ops;
+
 #endif /* __LINUX_SPI_LOONGSON_H */
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 7/8] spi: loongson: Convert to regmap API
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
                   ` (5 preceding siblings ...)
  2026-11-03  7:08 ` [PATCH 6/8] spi: loongson: Move private definitions into the core driver Binbin Zhou
@ 2026-11-03  7:08 ` Binbin Zhou
  2026-11-03  7:08 ` [PATCH 8/8] spi: loongson: Add interrupt support for SPI controller Binbin Zhou
  7 siblings, 0 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:08 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

Replace the open-coded readb()/writeb() operations with regmap-mmio.
The driver contains numerous read-modify-write sequences, for example
when updating the clock divider, the mode bits, or enabling/disabling
the controller.  These are now expressed with regmap_update_bits(),
which simplifies the code and reduces boilerplate.

Use a flat register cache with the status and FIFO registers marked
volatile.  Suspend/resume then uses regcache_cache_only(),
regcache_mark_dirty(), and regcache_sync() instead of manually saving
and restoring individual registers.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 177 +++++++++++++++++---------------
 1 file changed, 93 insertions(+), 84 deletions(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index 082248f29ae7..5ad781471cef 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -4,13 +4,15 @@
 //
 // Copyright (C) 2023-2026 Loongson Technology Corporation Limited
 
+#include <linux/bitfield.h>
+#include <linux/bits.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/export.h>
 #include <linux/init.h>
 #include <linux/io.h>
-#include <linux/iopoll.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <linux/spi/spi.h>
 
 #include "spi-loongson.h"
@@ -45,25 +47,31 @@
 /* SFC_PARAM bits */
 #define LOONGSON_SPI_PARA_MEM_EN	BIT(0)
 
+/*
+ * SFC_SOFTCS layout:
+ *   bit  cs      : per-CS software-control enable
+ *   bit  cs + 4  : per-CS output level
+ *
+ * The chip select index is a runtime value, so FIELD_PREP() (which
+ * requires a compile-time constant mask) cannot be used here. These
+ * helpers keep the shift hidden behind a field-like accessor.
+ */
+#define LOONGSON_SPI_SFCS_OUT_SHIFT	4
+#define LOONGSON_SPI_SFCS_SW_EN(cs)	BIT(cs)
+#define LOONGSON_SPI_SFCS_OUT(cs)	BIT((cs) + LOONGSON_SPI_SFCS_OUT_SHIFT)
+
+/* Hardware limits and divider masks */
 #define LOONGSON_SPI_DIV_MIN		2
 #define LOONGSON_SPI_DIV_MAX		4096
 
-struct loongson_spi_suspend_store {
-	unsigned char		spcr;
-	unsigned char		sper;
-	unsigned char		spsr;
-	unsigned char		para;
-	unsigned char		sfcs;
-	unsigned char		timi;
-};
+#define LOONGSON_SPI_NUM_CS		4
 
 struct loongson_spi_priv {
 	struct spi_controller	*controller;
-	void __iomem		*base;
+	struct regmap		*regmap;
 	unsigned int		hz;
 	unsigned int		mode;
 	u64			clk_rate;
-	struct loongson_spi_suspend_store lss;
 };
 
 static const struct {
@@ -85,32 +93,41 @@ static const struct {
 	{4096, 3, 2 },
 };
 
-static inline void loongson_spi_write_reg(struct loongson_spi_priv *priv, unsigned char reg,
-					  unsigned char data)
+static bool loongson_spi_volatile_reg(struct device *dev, unsigned int reg)
 {
-	writeb(data, priv->base + reg);
+	switch (reg) {
+	case LOONGSON_SPI_SPSR_REG:
+	case LOONGSON_SPI_FIFO_REG:
+		return true;
+	default:
+		return false;
+	}
 }
 
-static inline char loongson_spi_read_reg(struct loongson_spi_priv *priv, unsigned char reg)
-{
-	return readb(priv->base + reg);
-}
+static const struct regmap_config loongson_spi_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.reg_stride	= 1,
+	.max_register	= LOONGSON_SPI_TIMI_REG,
+	.cache_type	= REGCACHE_FLAT_S,
+	.volatile_reg	= loongson_spi_volatile_reg,
+};
 
 static void loongson_spi_set_cs(struct spi_device *spi, bool en)
 {
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
-	unsigned char mask = (BIT(4) | BIT(0)) << spi_get_chipselect(spi, 0);
-	unsigned char val = en ? mask : (BIT(0) << spi_get_chipselect(spi, 0));
-	int cs;
+	unsigned int cs = spi_get_chipselect(spi, 0);
+	unsigned char mask, val;
 
-	cs = loongson_spi_read_reg(priv, LOONGSON_SPI_SFCS_REG) & ~mask;
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SFCS_REG, val | cs);
+	mask = LOONGSON_SPI_SFCS_SW_EN(cs) | LOONGSON_SPI_SFCS_OUT(cs);
+	val = LOONGSON_SPI_SFCS_SW_EN(cs) | field_prep(LOONGSON_SPI_SFCS_OUT(cs), en);
+
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SFCS_REG, mask, val);
 }
 
 static int loongson_spi_set_clk(struct loongson_spi_priv *priv, unsigned int hz)
 {
 	unsigned long div;
-	unsigned char val;
 	unsigned int i;
 
 	if (!hz || !priv->clk_rate)
@@ -128,15 +145,13 @@ static int loongson_spi_set_clk(struct loongson_spi_priv *priv, unsigned int hz)
 	if (i == ARRAY_SIZE(loongson_spi_div_table))
 		return -EINVAL;
 
-	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
-	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG,
-			       val | loongson_spi_div_table[i].spr);
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
+			   LOONGSON_SPI_SPCR_SPR,
+			   loongson_spi_div_table[i].spr);
 
-	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPER_REG);
-	val &= ~GENMASK(1, 0);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPER_REG,
-			       val | loongson_spi_div_table[i].spre);
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPER_REG,
+			   LOONGSON_SPI_SPER_SPRE,
+			   loongson_spi_div_table[i].spre);
 
 	priv->hz = hz;
 
@@ -146,10 +161,7 @@ static int loongson_spi_set_clk(struct loongson_spi_priv *priv, unsigned int hz)
 static void loongson_spi_set_mode(struct loongson_spi_priv *priv,
 				  struct spi_device *spi)
 {
-	unsigned char val;
-
-	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
-	val &= ~(LOONGSON_SPI_SPCR_CPOL | LOONGSON_SPI_SPCR_CPHA);
+	unsigned char val = 0;
 
 	if (spi->mode & SPI_CPOL)
 		val |= LOONGSON_SPI_SPCR_CPOL;
@@ -157,7 +169,9 @@ static void loongson_spi_set_mode(struct loongson_spi_priv *priv,
 	if (spi->mode & SPI_CPHA)
 		val |= LOONGSON_SPI_SPCR_CPHA;
 
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
+			   LOONGSON_SPI_SPCR_CPOL | LOONGSON_SPI_SPCR_CPHA, val);
+
 	priv->mode = spi->mode;
 }
 
@@ -182,10 +196,10 @@ static int loongson_spi_setup(struct spi_device *spi)
 {
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
 
-	if (spi->bits_per_word % 8)
+	if (spi->bits_per_word % BITS_PER_BYTE)
 		return -EINVAL;
 
-	if (spi_get_chipselect(spi, 0) >= spi->controller->num_chipselect)
+	if (spi_get_chipselect(spi, 0) >= LOONGSON_SPI_NUM_CS)
 		return -EINVAL;
 
 	priv->hz = 0;
@@ -198,23 +212,26 @@ static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_bu
 					u8 **rx_buf)
 {
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
-	unsigned char status;
+	unsigned int val = 0;
+	unsigned int status;
 	int ret;
 
 	if (tx_buf && *tx_buf)
-		loongson_spi_write_reg(priv, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
-	else
-		loongson_spi_write_reg(priv, LOONGSON_SPI_FIFO_REG, 0);
+		val = *((*tx_buf)++);
 
-	ret = readb_poll_timeout(priv->base + LOONGSON_SPI_SPSR_REG,
-				 status,
-				 !(status & LOONGSON_SPI_SPSR_RFEMPTY),
-				 1, USEC_PER_MSEC);
+	regmap_write(priv->regmap, LOONGSON_SPI_FIFO_REG, val);
 
+	/* Wait for RX FIFO not empty */
+	ret = regmap_read_poll_timeout(priv->regmap, LOONGSON_SPI_SPSR_REG,
+				       status,
+				       !(status & LOONGSON_SPI_SPSR_RFEMPTY),
+				       1, USEC_PER_MSEC);
+	if (ret)
+		return ret;
+
+	regmap_read(priv->regmap, LOONGSON_SPI_FIFO_REG, &val);
 	if (rx_buf && *rx_buf)
-		*(*rx_buf)++ = loongson_spi_read_reg(priv, LOONGSON_SPI_FIFO_REG);
-	else
-		loongson_spi_read_reg(priv, LOONGSON_SPI_FIFO_REG);
+		*(*rx_buf)++ = (u8)val;
 
 	return ret;
 }
@@ -238,11 +255,8 @@ static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *
 static int loongson_spi_prepare_message(struct spi_controller *host, struct spi_message *m)
 {
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
-	unsigned char para;
 
-	para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG,
-			       para & ~LOONGSON_SPI_PARA_MEM_EN);
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_PARA_REG, LOONGSON_SPI_PARA_MEM_EN, 0);
 
 	return 0;
 }
@@ -266,30 +280,26 @@ static int loongson_spi_transfer_one(struct spi_controller *host, struct spi_dev
 static int loongson_spi_unprepare_message(struct spi_controller *host, struct spi_message *m)
 {
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
-	unsigned char para;
 
 	/* Restore the hardware SPI Flash read path */
-	para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG,
-			       para | LOONGSON_SPI_PARA_MEM_EN);
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_PARA_REG,
+			   LOONGSON_SPI_PARA_MEM_EN, LOONGSON_SPI_PARA_MEM_EN);
 
 	return 0;
 }
 
 static void loongson_spi_reginit(struct loongson_spi_priv *priv)
 {
-	unsigned char val;
+	/* Disable SPI */
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG, LOONGSON_SPI_SPCR_SPE, 0);
 
-	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
-	val &= ~LOONGSON_SPI_SPCR_SPE;
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
+	/* Clear status flags (write 1 to clear) */
+	regmap_write(priv->regmap, LOONGSON_SPI_SPSR_REG,
+		     LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
 
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPSR_REG,
-			       (LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL));
-
-	val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
-	val |= LOONGSON_SPI_SPCR_SPE;
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
+	/* Enable SPI */
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
+			   LOONGSON_SPI_SPCR_SPE, LOONGSON_SPI_SPCR_SPE);
 }
 
 static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
@@ -329,13 +339,16 @@ int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
 	host->transfer_one = loongson_spi_transfer_one;
 	host->unprepare_message = loongson_spi_unprepare_message;
 	host->set_cs = loongson_spi_set_cs;
-	host->num_chipselect = 4;
+	host->num_chipselect = LOONGSON_SPI_NUM_CS;
 	dev_set_drvdata(dev, host);
 
 	priv = spi_controller_get_devdata(host);
-	priv->base = regs;
 	priv->controller = host;
 
+	priv->regmap = devm_regmap_init_mmio(dev, regs, &loongson_spi_regmap_config);
+	if (IS_ERR(priv->regmap))
+		return dev_err_probe(dev, PTR_ERR(priv->regmap), "unable to init regmap");
+
 	ret = loongson_spi_get_clk_rate(dev, &priv->clk_rate);
 	if (ret)
 		return dev_err_probe(dev, ret, "unable to get clock rate");
@@ -357,12 +370,8 @@ static int loongson_spi_suspend(struct device *dev)
 	if (ret)
 		return ret;
 
-	priv->lss.spcr = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
-	priv->lss.sper = loongson_spi_read_reg(priv, LOONGSON_SPI_SPER_REG);
-	priv->lss.spsr = loongson_spi_read_reg(priv, LOONGSON_SPI_SPSR_REG);
-	priv->lss.para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
-	priv->lss.sfcs = loongson_spi_read_reg(priv, LOONGSON_SPI_SFCS_REG);
-	priv->lss.timi = loongson_spi_read_reg(priv, LOONGSON_SPI_TIMI_REG);
+	regcache_cache_only(priv->regmap, true);
+	regcache_mark_dirty(priv->regmap);
 
 	return 0;
 }
@@ -371,15 +380,15 @@ static int loongson_spi_resume(struct device *dev)
 {
 	struct spi_controller *host = dev_get_drvdata(dev);
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
+	int ret;
 
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, priv->lss.spcr);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPER_REG, priv->lss.sper);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG, priv->lss.para);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SFCS_REG, priv->lss.sfcs);
-	loongson_spi_write_reg(priv, LOONGSON_SPI_TIMI_REG, priv->lss.timi);
-
-	loongson_spi_write_reg(priv, LOONGSON_SPI_SPSR_REG,
-			       LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
+	regcache_cache_only(priv->regmap, false);
+	ret = regcache_sync(priv->regmap);
+	if (ret) {
+		regcache_cache_only(priv->regmap, true);
+		regcache_mark_dirty(priv->regmap);
+		return ret;
+	}
 
 	spi_controller_resume(host);
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 8/8] spi: loongson: Add interrupt support for SPI controller
  2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
                   ` (6 preceding siblings ...)
  2026-11-03  7:08 ` [PATCH 7/8] spi: loongson: Convert to regmap API Binbin Zhou
@ 2026-11-03  7:08 ` Binbin Zhou
  7 siblings, 0 replies; 11+ messages in thread
From: Binbin Zhou @ 2026-11-03  7:08 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown
  Cc: Huacai Chen, Xuerui Wang, loongarch, linux-spi, Binbin Zhou

The Loongson SPI controller can generate an interrupt on byte
completion.  Add an interrupt-driven transfer path and use it when an
IRQ is available, while keeping the existing polling path as a fallback.

The PCI and platform glue drivers now pass the IRQ number to
loongson_spi_init_controller().  If no IRQ is available, the driver
keeps using polling and emits an informational message.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/spi/spi-loongson-core.c | 169 ++++++++++++++++++++++++++++++--
 drivers/spi/spi-loongson-pci.c  |   8 +-
 drivers/spi/spi-loongson-plat.c |   8 +-
 drivers/spi/spi-loongson.h      |   2 +-
 4 files changed, 175 insertions(+), 12 deletions(-)

diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index 5ad781471cef..2e56b3399657 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -2,6 +2,13 @@
 //
 // Loongson SPI Controller driver
 //
+// - Host-only controller with up to 4 chip selects.
+// - Double-buffered Tx/Rx FIFO, 8-bit wide.
+// - Supports booting the SoC from SPI flash.
+// - Both a polling mode and a per-byte interrupt mode are supported;
+//   the driver automatically falls back to polling if no IRQ is
+//   available.
+//
 // Copyright (C) 2023-2026 Loongson Technology Corporation Limited
 
 #include <linux/bitfield.h>
@@ -10,6 +17,7 @@
 #include <linux/err.h>
 #include <linux/export.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
@@ -66,12 +74,20 @@
 
 #define LOONGSON_SPI_NUM_CS		4
 
+/* Maximum bytes per individual spi_transfer (further reduced for slow links) */
+#define LOONGSON_SPI_MAX_XFER_SIZE	(64 * 1024)
+
 struct loongson_spi_priv {
 	struct spi_controller	*controller;
 	struct regmap		*regmap;
 	unsigned int		hz;
 	unsigned int		mode;
 	u64			clk_rate;
+	bool			use_irq;
+	const unsigned char	*tx_ptr;
+	unsigned char		*rx_ptr;
+	unsigned int		count;
+	unsigned int		total;
 };
 
 static const struct {
@@ -252,9 +268,119 @@ static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *
 	return ret;
 }
 
+static irqreturn_t loongson_spi_irq(int irq, void *dev_id)
+{
+	struct loongson_spi_priv *priv = dev_id;
+	unsigned int status, data = 0;
+	int ret;
+
+	ret = regmap_read(priv->regmap, LOONGSON_SPI_SPSR_REG, &status);
+	if (ret)
+		return IRQ_NONE;
+
+	if (!(status & (LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL)))
+		return IRQ_NONE;
+
+	/* Clear WCOL if it happens to be set */
+	if (status & LOONGSON_SPI_SPSR_WCOL)
+		regmap_write(priv->regmap, LOONGSON_SPI_SPSR_REG,
+			     LOONGSON_SPI_SPSR_WCOL);
+
+	if (!(status & LOONGSON_SPI_SPSR_SPIF))
+		return IRQ_HANDLED;
+
+	/* Clear the SPIF flag (write 1 to clear) */
+	regmap_write(priv->regmap, LOONGSON_SPI_SPSR_REG,
+		     LOONGSON_SPI_SPSR_SPIF);
+
+	/* Read out the byte that was clocked in */
+	regmap_read(priv->regmap, LOONGSON_SPI_FIFO_REG, &data);
+	if (priv->rx_ptr)
+		priv->rx_ptr[priv->count] = (u8)data;
+
+	priv->count++;
+
+	if (priv->count >= priv->total) {
+		/* Transfer complete: disable the byte interrupt and finalize */
+		regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
+				   LOONGSON_SPI_SPCR_SPIE, 0);
+		spi_finalize_current_transfer(priv->controller);
+		return IRQ_HANDLED;
+	}
+
+	/* Kick off the next byte */
+	if (priv->tx_ptr)
+		data = priv->tx_ptr[priv->count];
+	else
+		data = 0;
+
+	regmap_write(priv->regmap, LOONGSON_SPI_FIFO_REG, data);
+
+	return IRQ_HANDLED;
+}
+
+static int loongson_spi_transfer_one_irq(struct spi_controller *host,
+					 struct spi_device *spi,
+					 struct spi_transfer *xfer)
+{
+	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
+	unsigned int data = 0;
+
+	priv->tx_ptr = xfer->tx_buf;
+	priv->rx_ptr = xfer->rx_buf;
+	priv->count = 0;
+	priv->total = xfer->len;
+
+	/* Clear any stale status before enabling the interrupt */
+	regmap_write(priv->regmap, LOONGSON_SPI_SPSR_REG,
+		     LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
+
+	/* Enable the byte-completion interrupt */
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
+			   LOONGSON_SPI_SPCR_SPIE, LOONGSON_SPI_SPCR_SPIE);
+
+	/* Send the first byte to start the transfer */
+	if (priv->tx_ptr)
+		data = priv->tx_ptr[0];
+	regmap_write(priv->regmap, LOONGSON_SPI_FIFO_REG, data);
+
+	/*
+	 * Return 1 so the SPI core knows the transfer will be
+	 * finalized asynchronously from the IRQ handler.
+	 */
+	return 1;
+}
+
+/*
+ * Hardware limit on a single spi_transfer. Called by the SPI core via
+ * spi_max_transfer_size() to determine the effective upper bound.
+ */
+static size_t loongson_spi_max_transfer_size(struct spi_device *spi)
+{
+	return LOONGSON_SPI_MAX_XFER_SIZE;
+}
+
 static int loongson_spi_prepare_message(struct spi_controller *host, struct spi_message *m)
 {
 	struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
+	size_t hw_max = spi_max_transfer_size(m->spi);
+	unsigned int speed_hz;
+	size_t maxsize;
+	int ret;
+
+	/*
+	 * Split large transfers so that each individual spi_transfer
+	 * completes well within the SPI core's completion timeout.
+	 * We allow roughly one second of data per chunk, capped by
+	 * the controller's hardware maximum transfer size.
+	 */
+	speed_hz = m->spi->max_speed_hz ? m->spi->max_speed_hz : host->max_speed_hz;
+	maxsize = min_t(size_t, hw_max, (size_t)speed_hz / 8);
+	maxsize = max_t(size_t, maxsize, 64);
+
+	ret = spi_split_transfers_maxsize(host, m, maxsize);
+	if (ret)
+		return ret;
 
 	regmap_update_bits(priv->regmap, LOONGSON_SPI_PARA_REG, LOONGSON_SPI_PARA_MEM_EN, 0);
 
@@ -271,10 +397,13 @@ static int loongson_spi_transfer_one(struct spi_controller *host, struct spi_dev
 	if (ret)
 		return ret;
 
-	if (xfer->len)
-		return loongson_spi_write_read(spi, xfer);
+	if (!xfer->len)
+		return 0;
 
-	return 0;
+	if (priv->use_irq)
+		return loongson_spi_transfer_one_irq(host, spi, xfer);
+
+	return loongson_spi_write_read(spi, xfer);
 }
 
 static int loongson_spi_unprepare_message(struct spi_controller *host, struct spi_message *m)
@@ -290,14 +419,15 @@ static int loongson_spi_unprepare_message(struct spi_controller *host, struct sp
 
 static void loongson_spi_reginit(struct loongson_spi_priv *priv)
 {
-	/* Disable SPI */
-	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG, LOONGSON_SPI_SPCR_SPE, 0);
+	/* Disable SPI and its interrupt */
+	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
+			   LOONGSON_SPI_SPCR_SPE | LOONGSON_SPI_SPCR_SPIE, 0);
 
 	/* Clear status flags (write 1 to clear) */
 	regmap_write(priv->regmap, LOONGSON_SPI_SPSR_REG,
 		     LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
 
-	/* Enable SPI */
+	/* Enable SPI (interrupt remains disabled until a transfer starts) */
 	regmap_update_bits(priv->regmap, LOONGSON_SPI_SPCR_REG,
 			   LOONGSON_SPI_SPCR_SPE, LOONGSON_SPI_SPCR_SPE);
 }
@@ -323,7 +453,26 @@ static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
 	return 0;
 }
 
-int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
+static void loongson_spi_irqinit(struct loongson_spi_priv *priv, struct device *dev, int irq)
+{
+	int ret;
+
+	if (irq <= 0) {
+		priv->use_irq = false;
+		return;
+	}
+
+	ret = devm_request_irq(dev, irq, loongson_spi_irq, 0, "loongson-spi", priv);
+	if (ret) {
+		priv->use_irq = false;
+		dev_warn(dev, "failed to request IRQ %d with %d, using polling", irq, ret);
+		return;
+	}
+
+	priv->use_irq = true;
+}
+
+int loongson_spi_init_controller(struct device *dev, void __iomem *regs, int irq)
 {
 	struct loongson_spi_priv *priv;
 	struct spi_controller *host;
@@ -338,6 +487,7 @@ int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
 	host->prepare_message = loongson_spi_prepare_message;
 	host->transfer_one = loongson_spi_transfer_one;
 	host->unprepare_message = loongson_spi_unprepare_message;
+	host->max_transfer_size = loongson_spi_max_transfer_size;
 	host->set_cs = loongson_spi_set_cs;
 	host->num_chipselect = LOONGSON_SPI_NUM_CS;
 	dev_set_drvdata(dev, host);
@@ -353,9 +503,14 @@ int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
 	if (ret)
 		return dev_err_probe(dev, ret, "unable to get clock rate");
 
+	/* Expose controller capabilities to the SPI core */
+	host->max_speed_hz = priv->clk_rate / LOONGSON_SPI_DIV_MIN;
+
 	loongson_spi_reginit(priv);
 	priv->mode = 0;
 
+	loongson_spi_irqinit(priv, dev, irq);
+
 	return devm_spi_register_controller(dev, host);
 }
 EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
diff --git a/drivers/spi/spi-loongson-pci.c b/drivers/spi/spi-loongson-pci.c
index ad90be956e1e..582f8aa10470 100644
--- a/drivers/spi/spi-loongson-pci.c
+++ b/drivers/spi/spi-loongson-pci.c
@@ -11,8 +11,8 @@
 static int loongson_spi_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	struct device *dev = &pdev->dev;
+	int pci_bar = 0, ret, irq;
 	void __iomem *reg_base;
-	int pci_bar = 0, ret;
 
 	ret = pcim_enable_device(pdev);
 	if (ret < 0)
@@ -23,7 +23,11 @@ static int loongson_spi_pci_register(struct pci_dev *pdev, const struct pci_devi
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to request and remap memory");
 
-	ret = loongson_spi_init_controller(dev, reg_base);
+	irq = pci_irq_vector(pdev, 0);
+	if (irq < 0)
+		dev_info(dev, "no IRQ available, will use PIO mode");
+
+	ret = loongson_spi_init_controller(dev, reg_base, irq);
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to initialize controller");
 
diff --git a/drivers/spi/spi-loongson-plat.c b/drivers/spi/spi-loongson-plat.c
index dcfbdd50e996..d4d37748c3c2 100644
--- a/drivers/spi/spi-loongson-plat.c
+++ b/drivers/spi/spi-loongson-plat.c
@@ -13,13 +13,17 @@ static int loongson_spi_platform_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	void __iomem *reg_base;
-	int ret;
+	int ret, irq;
 
 	reg_base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(reg_base))
 		return PTR_ERR(reg_base);
 
-	ret = loongson_spi_init_controller(dev, reg_base);
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		dev_info(dev, "no IRQ specified, will use PIO mode");
+
+	ret = loongson_spi_init_controller(dev, reg_base, irq);
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to initialize controller");
 
diff --git a/drivers/spi/spi-loongson.h b/drivers/spi/spi-loongson.h
index c8b722bc5fb5..b60619b02ec3 100644
--- a/drivers/spi/spi-loongson.h
+++ b/drivers/spi/spi-loongson.h
@@ -7,7 +7,7 @@
 #ifndef __LINUX_SPI_LOONGSON_H
 #define __LINUX_SPI_LOONGSON_H
 
-int loongson_spi_init_controller(struct device *dev, void __iomem *reg);
+int loongson_spi_init_controller(struct device *dev, void __iomem *reg, int irq);
 extern const struct dev_pm_ops loongson_spi_dev_pm_ops;
 
 #endif /* __LINUX_SPI_LOONGSON_H */
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/8] spi: loongson: Make suspend/resume registers PM-specific
  2026-11-03  7:07 ` [PATCH 3/8] spi: loongson: Make suspend/resume registers PM-specific Binbin Zhou
@ 2026-11-04 10:02   ` Huacai Chen
  0 siblings, 0 replies; 11+ messages in thread
From: Huacai Chen @ 2026-11-04 10:02 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown, Xuerui Wang,
	loongarch, linux-spi

Hi, Binbin,

On Tue, Nov 3, 2026 at 3:08 PM Binbin Zhou <zhoubinbin@loongson.cn> wrote:
>
> Currently, the driver reuses fields like spcr, sper, spsr, and para
> within struct loongson_spi for multiple purposes (runtime setup,
> polling, and PM state). This state overloading makes the code fragile.
> For example, set_clk() modifies spcr/sper during normal operations,
> which can corrupt the saved state right before a suspend.
>
> To fix this, move these register caches into a dedicated struct
> loongson_spi_suspend_store (lss) so they are exclusively used by the
> PM callbacks. Other functions (set_clk, write_read_8bit, message
> handling) are updated to use local variables instead of mutating the
> global PM state.
>
> Fixes: 6c7a864007b6 ("spi: loongson: add bus driver for the loongson spi controller")
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  drivers/spi/spi-loongson-core.c | 80 ++++++++++++++++++---------------
>  drivers/spi/spi-loongson.h      | 14 +++---
>  2 files changed, 52 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
> index deda754e214d..9f3acf583984 100644
> --- a/drivers/spi/spi-loongson-core.c
> +++ b/drivers/spi/spi-loongson-core.c
> @@ -40,22 +40,23 @@ static void loongson_spi_set_cs(struct spi_device *spi, bool en)
>
>  static void loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
>  {
> -       unsigned char val;
> -       unsigned int div, div_tmp;
>         static const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};
> +       unsigned char val, spr, spre;
> +       unsigned int div, div_tmp;
>
>         div = clamp_val(DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz), 2, 4096);
>         div_tmp = rdiv[fls(div - 1)];
> -       loongson_spi->spcr = (div_tmp & GENMASK(1, 0)) >> 0;
> -       loongson_spi->sper = (div_tmp & GENMASK(3, 2)) >> 2;
> +
> +       spr = (div_tmp & GENMASK(1, 0)) >> 0;
>         val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
>         val &= ~GENMASK(1, 0);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val |
> -                              loongson_spi->spcr);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val | spr);
> +
> +       spre = (div_tmp & GENMASK(3, 2)) >> 2;
>         val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
>         val &= ~GENMASK(1, 0);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val |
> -                              loongson_spi->sper);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val | spre);
> +
>         loongson_spi->hz = hz;
>  }
>
> @@ -107,8 +108,9 @@ static int loongson_spi_setup(struct spi_device *spi)
>  static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_buf,
>                                         u8 **rx_buf, unsigned int num)
>  {
> -       int ret;
>         struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
> +       unsigned char status;
> +       int ret;
>
>         if (tx_buf && *tx_buf)
>                 loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
> @@ -116,8 +118,8 @@ static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_bu
>                 loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, 0);
>
>         ret = readb_poll_timeout(loongson_spi->base + LOONGSON_SPI_SPSR_REG,
> -                                loongson_spi->spsr, (loongson_spi->spsr &
> -                                LOONGSON_SPI_SPSR_RFEMPTY) != LOONGSON_SPI_SPSR_RFEMPTY,
> +                                status,
> +                                !(status & LOONGSON_SPI_SPSR_RFEMPTY),
>                                  1, USEC_PER_MSEC);
>
>         if (rx_buf && *rx_buf)
> @@ -148,10 +150,11 @@ static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *
>  static int loongson_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
>  {
>         struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctlr);
> +       unsigned char para;
>
> -       loongson_spi->para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para &
> -                              ~LOONGSON_SPI_PARA_MEM_EN);
> +       para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
> +                              para & ~LOONGSON_SPI_PARA_MEM_EN);
>
>         return 0;
>  }
> @@ -171,8 +174,12 @@ static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_dev
>  static int loongson_spi_unprepare_message(struct spi_controller *ctrl, struct spi_message *m)
>  {
>         struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctrl);
> +       unsigned char para;
>
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para);
> +       /* Restore the hardware SPI Flash read path */
> +       para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
> +                              para | LOONGSON_SPI_PARA_MEM_EN);
>
>         return 0;
>  }
> @@ -231,38 +238,37 @@ EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
>
>  static int loongson_spi_suspend(struct device *dev)
>  {
> -       struct loongson_spi *loongson_spi;
> -       struct spi_controller *controller;
> -
> -       controller = dev_get_drvdata(dev);
> -       spi_controller_suspend(controller);
> +       struct spi_controller *controller = dev_get_drvdata(dev);
> +       struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
> +       int ret;
>
> -       loongson_spi = spi_controller_get_devdata(controller);
> +       ret = spi_controller_suspend(controller);
> +       if (ret)
> +               return ret;
>
> -       loongson_spi->spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
> -       loongson_spi->sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
> -       loongson_spi->spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
> -       loongson_spi->para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> -       loongson_spi->sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
> -       loongson_spi->timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
> +       loongson_spi->lss.spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
> +       loongson_spi->lss.sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
> +       loongson_spi->lss.spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
> +       loongson_spi->lss.para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> +       loongson_spi->lss.sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
> +       loongson_spi->lss.timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
>
>         return 0;
>  }
>
>  static int loongson_spi_resume(struct device *dev)
>  {
> -       struct loongson_spi *loongson_spi;
> -       struct spi_controller *controller;
> +       struct spi_controller *controller = dev_get_drvdata(dev);
> +       struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
>
> -       controller = dev_get_drvdata(dev);
> -       loongson_spi = spi_controller_get_devdata(controller);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->lss.spcr);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->lss.sper);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->lss.para);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->lss.sfcs);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->lss.timi);
>
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->spcr);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->sper);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG, loongson_spi->spsr);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->sfcs);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->timi);
> +       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG,
> +                              LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
>
>         spi_controller_resume(controller);
>
> diff --git a/drivers/spi/spi-loongson.h b/drivers/spi/spi-loongson.h
> index 35f95b161842..b2e73fa12420 100644
> --- a/drivers/spi/spi-loongson.h
> +++ b/drivers/spi/spi-loongson.h
> @@ -29,19 +29,23 @@
>  struct device;
>  struct spi_controller;
>
> -struct loongson_spi {
> -       struct  spi_controller  *controller;
> -       void __iomem            *base;
> -       int                     cs_active;
> -       unsigned int            hz;
> +struct loongson_spi_suspend_store {
Maybe loongson_spi_pm_state is a better name.

Huacai

>         unsigned char           spcr;
>         unsigned char           sper;
>         unsigned char           spsr;
>         unsigned char           para;
>         unsigned char           sfcs;
>         unsigned char           timi;
> +};
> +
> +struct loongson_spi {
> +       struct  spi_controller  *controller;
> +       void __iomem            *base;
> +       int                     cs_active;
> +       unsigned int            hz;
>         unsigned int            mode;
>         u64                     clk_rate;
> +       struct loongson_spi_suspend_store lss;
>  };
>
>  int loongson_spi_init_controller(struct device *dev, void __iomem *reg);
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 6/8] spi: loongson: Move private definitions into the core driver
  2026-11-03  7:08 ` [PATCH 6/8] spi: loongson: Move private definitions into the core driver Binbin Zhou
@ 2026-11-04 10:04   ` Huacai Chen
  0 siblings, 0 replies; 11+ messages in thread
From: Huacai Chen @ 2026-11-04 10:04 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Yinbo Zhu, Mark Brown, Xuerui Wang,
	loongarch, linux-spi

Hi, Binbin,

On Tue, Nov 3, 2026 at 3:08 PM Binbin Zhou <zhoubinbin@loongson.cn> wrote:
>
> The Loongson SPI register definitions and the private controller
> structure are only used by the core driver.  Move them from the shared
> header into spi-loongson-core.c and rename the structure to
> loongson_spi_priv to make it clear that it is driver-private data.
Moving is OK but renaming is unnecessary.

Huacai

>
> The shared header now only contains the exported init function and the
> PM ops declaration, which reduces the surface exposed to the PCI and
> platform glue drivers.
>
> While at it, update the copyright years and add MODULE_AUTHOR() entries
> to the PCI and platform drivers.
>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  drivers/spi/spi-loongson-core.c | 252 +++++++++++++++++++-------------
>  drivers/spi/spi-loongson-pci.c  |  19 +--
>  drivers/spi/spi-loongson-plat.c |  11 +-
>  drivers/spi/spi-loongson.h      |  53 +------
>  4 files changed, 173 insertions(+), 162 deletions(-)
>
> diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
> index 91d7acbe4302..082248f29ae7 100644
> --- a/drivers/spi/spi-loongson-core.c
> +++ b/drivers/spi/spi-loongson-core.c
> @@ -1,21 +1,71 @@
>  // SPDX-License-Identifier: GPL-2.0+
> -// Loongson SPI Support
> -// Copyright (C) 2023 Loongson Technology Corporation Limited
> +//
> +// Loongson SPI Controller driver
> +//
> +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
>
>  #include <linux/clk.h>
> -#include <linux/delay.h>
>  #include <linux/err.h>
>  #include <linux/export.h>
>  #include <linux/init.h>
> -#include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
> -#include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/spi/spi.h>
>
>  #include "spi-loongson.h"
>
> +/* SPI controller register offsets */
> +#define LOONGSON_SPI_SPCR_REG          0x00
> +#define LOONGSON_SPI_SPSR_REG          0x01
> +#define LOONGSON_SPI_FIFO_REG          0x02
> +#define LOONGSON_SPI_SPER_REG          0x03
> +#define LOONGSON_SPI_PARA_REG          0x04
> +#define LOONGSON_SPI_SFCS_REG          0x05
> +#define LOONGSON_SPI_TIMI_REG          0x06
> +
> +/* SPCR bits */
> +#define LOONGSON_SPI_SPCR_SPR          GENMASK(1, 0)
> +#define LOONGSON_SPI_SPCR_CPHA         BIT(2)
> +#define LOONGSON_SPI_SPCR_CPOL         BIT(3)
> +#define LOONGSON_SPI_SPCR_SPE          BIT(6)
> +#define LOONGSON_SPI_SPCR_SPIE         BIT(7)
> +
> +/* SPSR bits */
> +#define LOONGSON_SPI_SPSR_RFEMPTY      BIT(0)
> +#define LOONGSON_SPI_SPSR_RFFULL       BIT(1)
> +#define LOONGSON_SPI_SPSR_WFEMPTY      BIT(2)
> +#define LOONGSON_SPI_SPSR_WFFULL       BIT(3)
> +#define LOONGSON_SPI_SPSR_WCOL         BIT(6)
> +#define LOONGSON_SPI_SPSR_SPIF         BIT(7)
> +
> +/* SPER bits */
> +#define LOONGSON_SPI_SPER_SPRE         GENMASK(1, 0)
> +
> +/* SFC_PARAM bits */
> +#define LOONGSON_SPI_PARA_MEM_EN       BIT(0)
> +
> +#define LOONGSON_SPI_DIV_MIN           2
> +#define LOONGSON_SPI_DIV_MAX           4096
> +
> +struct loongson_spi_suspend_store {
> +       unsigned char           spcr;
> +       unsigned char           sper;
> +       unsigned char           spsr;
> +       unsigned char           para;
> +       unsigned char           sfcs;
> +       unsigned char           timi;
> +};
> +
> +struct loongson_spi_priv {
> +       struct spi_controller   *controller;
> +       void __iomem            *base;
> +       unsigned int            hz;
> +       unsigned int            mode;
> +       u64                     clk_rate;
> +       struct loongson_spi_suspend_store lss;
> +};
> +
>  static const struct {
>         unsigned int    div;
>         unsigned char   spr;
> @@ -35,38 +85,38 @@ static const struct {
>         {4096, 3, 2 },
>  };
>
> -static inline void loongson_spi_write_reg(struct loongson_spi *spi, unsigned char reg,
> +static inline void loongson_spi_write_reg(struct loongson_spi_priv *priv, unsigned char reg,
>                                           unsigned char data)
>  {
> -       writeb(data, spi->base + reg);
> +       writeb(data, priv->base + reg);
>  }
>
> -static inline char loongson_spi_read_reg(struct loongson_spi *spi, unsigned char reg)
> +static inline char loongson_spi_read_reg(struct loongson_spi_priv *priv, unsigned char reg)
>  {
> -       return readb(spi->base + reg);
> +       return readb(priv->base + reg);
>  }
>
>  static void loongson_spi_set_cs(struct spi_device *spi, bool en)
>  {
> -       int cs;
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
>         unsigned char mask = (BIT(4) | BIT(0)) << spi_get_chipselect(spi, 0);
> -       unsigned char val = en ? mask :  (BIT(0) << spi_get_chipselect(spi, 0));
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
> +       unsigned char val = en ? mask : (BIT(0) << spi_get_chipselect(spi, 0));
> +       int cs;
>
> -       cs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG) & ~mask;
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, val | cs);
> +       cs = loongson_spi_read_reg(priv, LOONGSON_SPI_SFCS_REG) & ~mask;
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SFCS_REG, val | cs);
>  }
>
> -static int loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
> +static int loongson_spi_set_clk(struct loongson_spi_priv *priv, unsigned int hz)
>  {
>         unsigned long div;
>         unsigned char val;
>         unsigned int i;
>
> -       if (!hz || !loongson_spi->clk_rate)
> +       if (!hz || !priv->clk_rate)
>                 return -EINVAL;
>
> -       div = DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz);
> +       div = DIV_ROUND_UP_ULL(priv->clk_rate, hz);
>         if (div < LOONGSON_SPI_DIV_MIN)
>                 div = LOONGSON_SPI_DIV_MIN;
>
> @@ -78,106 +128,106 @@ static int loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int
>         if (i == ARRAY_SIZE(loongson_spi_div_table))
>                 return -EINVAL;
>
> -       val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
> +       val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
>         val &= ~GENMASK(1, 0);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG,
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG,
>                                val | loongson_spi_div_table[i].spr);
>
> -       val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
> +       val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPER_REG);
>         val &= ~GENMASK(1, 0);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG,
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPER_REG,
>                                val | loongson_spi_div_table[i].spre);
>
> -       loongson_spi->hz = hz;
> +       priv->hz = hz;
>
>         return 0;
>  }
>
> -static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
> +static void loongson_spi_set_mode(struct loongson_spi_priv *priv,
>                                   struct spi_device *spi)
>  {
>         unsigned char val;
>
> -       val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
> +       val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
>         val &= ~(LOONGSON_SPI_SPCR_CPOL | LOONGSON_SPI_SPCR_CPHA);
> +
>         if (spi->mode & SPI_CPOL)
>                 val |= LOONGSON_SPI_SPCR_CPOL;
> +
>         if (spi->mode & SPI_CPHA)
>                 val |= LOONGSON_SPI_SPCR_CPHA;
>
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val);
> -       loongson_spi->mode = spi->mode;
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
> +       priv->mode = spi->mode;
>  }
>
> -static int loongson_spi_update_state(struct loongson_spi *loongson_spi,
> -                               struct spi_device *spi, struct spi_transfer *t)
> +static int loongson_spi_update_state(struct loongson_spi_priv *priv, struct spi_device *spi,
> +                                    struct spi_transfer *t)
>  {
>         int ret;
>
> -       if (t && loongson_spi->hz != t->speed_hz) {
> -               ret = loongson_spi_set_clk(loongson_spi, t->speed_hz);
> +       if (t && priv->hz != t->speed_hz) {
> +               ret = loongson_spi_set_clk(priv, t->speed_hz);
>                 if (ret)
>                         return ret;
>         }
>
> -       if ((spi->mode ^ loongson_spi->mode) & SPI_MODE_X_MASK)
> -               loongson_spi_set_mode(loongson_spi, spi);
> +       if ((spi->mode ^ priv->mode) & SPI_MODE_X_MASK)
> +               loongson_spi_set_mode(priv, spi);
>
>         return 0;
>  }
>
>  static int loongson_spi_setup(struct spi_device *spi)
>  {
> -       struct loongson_spi *loongson_spi;
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
>
> -       loongson_spi = spi_controller_get_devdata(spi->controller);
>         if (spi->bits_per_word % 8)
>                 return -EINVAL;
>
>         if (spi_get_chipselect(spi, 0) >= spi->controller->num_chipselect)
>                 return -EINVAL;
>
> -       loongson_spi->hz = 0;
> +       priv->hz = 0;
>         loongson_spi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
>
>         return 0;
>  }
>
>  static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_buf,
> -                                       u8 **rx_buf, unsigned int num)
> +                                       u8 **rx_buf)
>  {
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
>         unsigned char status;
>         int ret;
>
>         if (tx_buf && *tx_buf)
> -               loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
> +               loongson_spi_write_reg(priv, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
>         else
> -               loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, 0);
> +               loongson_spi_write_reg(priv, LOONGSON_SPI_FIFO_REG, 0);
>
> -       ret = readb_poll_timeout(loongson_spi->base + LOONGSON_SPI_SPSR_REG,
> +       ret = readb_poll_timeout(priv->base + LOONGSON_SPI_SPSR_REG,
>                                  status,
>                                  !(status & LOONGSON_SPI_SPSR_RFEMPTY),
>                                  1, USEC_PER_MSEC);
>
>         if (rx_buf && *rx_buf)
> -               *(*rx_buf)++ = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_FIFO_REG);
> +               *(*rx_buf)++ = loongson_spi_read_reg(priv, LOONGSON_SPI_FIFO_REG);
>         else
> -               loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_FIFO_REG);
> +               loongson_spi_read_reg(priv, LOONGSON_SPI_FIFO_REG);
>
>         return ret;
>  }
>
>  static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
>  {
> -       int ret;
> -       unsigned int count;
> +       unsigned int count = xfer->len;
>         const u8 *tx = xfer->tx_buf;
>         u8 *rx = xfer->rx_buf;
> +       int ret;
>
> -       count = xfer->len;
>         do {
> -               ret = loongson_spi_write_read_8bit(spi, &tx, &rx, count);
> +               ret = loongson_spi_write_read_8bit(spi, &tx, &rx);
>                 if (ret)
>                         break;
>         } while (--count);
> @@ -185,25 +235,25 @@ static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *
>         return ret;
>  }
>
> -static int loongson_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
> +static int loongson_spi_prepare_message(struct spi_controller *host, struct spi_message *m)
>  {
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctlr);
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
>         unsigned char para;
>
> -       para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
> +       para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG,
>                                para & ~LOONGSON_SPI_PARA_MEM_EN);
>
>         return 0;
>  }
>
> -static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_device *spi,
> +static int loongson_spi_transfer_one(struct spi_controller *host, struct spi_device *spi,
>                                      struct spi_transfer *xfer)
>  {
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(spi->controller);
>         int ret;
>
> -       ret = loongson_spi_update_state(loongson_spi, spi, xfer);
> +       ret = loongson_spi_update_state(priv, spi, xfer);
>         if (ret)
>                 return ret;
>
> @@ -213,33 +263,33 @@ static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_dev
>         return 0;
>  }
>
> -static int loongson_spi_unprepare_message(struct spi_controller *ctrl, struct spi_message *m)
> +static int loongson_spi_unprepare_message(struct spi_controller *host, struct spi_message *m)
>  {
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctrl);
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
>         unsigned char para;
>
>         /* Restore the hardware SPI Flash read path */
> -       para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG,
> +       para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG,
>                                para | LOONGSON_SPI_PARA_MEM_EN);
>
>         return 0;
>  }
>
> -static void loongson_spi_reginit(struct loongson_spi *loongson_spi_dev)
> +static void loongson_spi_reginit(struct loongson_spi_priv *priv)
>  {
>         unsigned char val;
>
> -       val = loongson_spi_read_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG);
> +       val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
>         val &= ~LOONGSON_SPI_SPCR_SPE;
> -       loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
>
> -       loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPSR_REG,
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPSR_REG,
>                                (LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL));
>
> -       val = loongson_spi_read_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG);
> +       val = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
>         val |= LOONGSON_SPI_SPCR_SPE;
> -       loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, val);
>  }
>
>  static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
> @@ -265,74 +315,73 @@ static int loongson_spi_get_clk_rate(struct device *dev, u64 *clk_rate)
>
>  int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
>  {
> -       struct spi_controller *controller;
> -       struct loongson_spi *spi;
> +       struct loongson_spi_priv *priv;
> +       struct spi_controller *host;
>         int ret;
>
> -       controller = devm_spi_alloc_host(dev, sizeof(struct loongson_spi));
> -       if (controller == NULL)
> +       host = devm_spi_alloc_host(dev, sizeof(*priv));
> +       if (!host)
>                 return -ENOMEM;
>
> -       controller->mode_bits = SPI_MODE_X_MASK | SPI_CS_HIGH;
> -       controller->setup = loongson_spi_setup;
> -       controller->prepare_message = loongson_spi_prepare_message;
> -       controller->transfer_one = loongson_spi_transfer_one;
> -       controller->unprepare_message = loongson_spi_unprepare_message;
> -       controller->set_cs = loongson_spi_set_cs;
> -       controller->num_chipselect = 4;
> -       dev_set_drvdata(dev, controller);
> +       host->mode_bits = SPI_MODE_X_MASK | SPI_CS_HIGH;
> +       host->setup = loongson_spi_setup;
> +       host->prepare_message = loongson_spi_prepare_message;
> +       host->transfer_one = loongson_spi_transfer_one;
> +       host->unprepare_message = loongson_spi_unprepare_message;
> +       host->set_cs = loongson_spi_set_cs;
> +       host->num_chipselect = 4;
> +       dev_set_drvdata(dev, host);
>
> -       spi = spi_controller_get_devdata(controller);
> -       spi->base = regs;
> -       spi->controller = controller;
> +       priv = spi_controller_get_devdata(host);
> +       priv->base = regs;
> +       priv->controller = host;
>
> -       ret = loongson_spi_get_clk_rate(dev, &spi->clk_rate);
> +       ret = loongson_spi_get_clk_rate(dev, &priv->clk_rate);
>         if (ret)
>                 return dev_err_probe(dev, ret, "unable to get clock rate");
>
> -       loongson_spi_reginit(spi);
> -
> -       spi->mode = 0;
> +       loongson_spi_reginit(priv);
> +       priv->mode = 0;
>
> -       return devm_spi_register_controller(dev, controller);
> +       return devm_spi_register_controller(dev, host);
>  }
>  EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
>
>  static int loongson_spi_suspend(struct device *dev)
>  {
> -       struct spi_controller *controller = dev_get_drvdata(dev);
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
> +       struct spi_controller *host = dev_get_drvdata(dev);
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
>         int ret;
>
> -       ret = spi_controller_suspend(controller);
> +       ret = spi_controller_suspend(host);
>         if (ret)
>                 return ret;
>
> -       loongson_spi->lss.spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
> -       loongson_spi->lss.sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
> -       loongson_spi->lss.spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
> -       loongson_spi->lss.para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
> -       loongson_spi->lss.sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
> -       loongson_spi->lss.timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
> +       priv->lss.spcr = loongson_spi_read_reg(priv, LOONGSON_SPI_SPCR_REG);
> +       priv->lss.sper = loongson_spi_read_reg(priv, LOONGSON_SPI_SPER_REG);
> +       priv->lss.spsr = loongson_spi_read_reg(priv, LOONGSON_SPI_SPSR_REG);
> +       priv->lss.para = loongson_spi_read_reg(priv, LOONGSON_SPI_PARA_REG);
> +       priv->lss.sfcs = loongson_spi_read_reg(priv, LOONGSON_SPI_SFCS_REG);
> +       priv->lss.timi = loongson_spi_read_reg(priv, LOONGSON_SPI_TIMI_REG);
>
>         return 0;
>  }
>
>  static int loongson_spi_resume(struct device *dev)
>  {
> -       struct spi_controller *controller = dev_get_drvdata(dev);
> -       struct loongson_spi *loongson_spi = spi_controller_get_devdata(controller);
> +       struct spi_controller *host = dev_get_drvdata(dev);
> +       struct loongson_spi_priv *priv = spi_controller_get_devdata(host);
>
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->lss.spcr);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->lss.sper);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->lss.para);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->lss.sfcs);
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->lss.timi);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPCR_REG, priv->lss.spcr);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPER_REG, priv->lss.sper);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_PARA_REG, priv->lss.para);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SFCS_REG, priv->lss.sfcs);
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_TIMI_REG, priv->lss.timi);
>
> -       loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG,
> +       loongson_spi_write_reg(priv, LOONGSON_SPI_SPSR_REG,
>                                LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL);
>
> -       spi_controller_resume(controller);
> +       spi_controller_resume(host);
>
>         return 0;
>  }
> @@ -342,4 +391,5 @@ DEFINE_SIMPLE_DEV_PM_OPS(loongson_spi_dev_pm_ops, loongson_spi_suspend, loongson
>  EXPORT_SYMBOL_NS_GPL(loongson_spi_dev_pm_ops, "SPI_LOONGSON_CORE");
>
>  MODULE_DESCRIPTION("Loongson SPI core driver");
> +MODULE_AUTHOR("Loongson Technology Corporation Limited");
>  MODULE_LICENSE("GPL");
> diff --git a/drivers/spi/spi-loongson-pci.c b/drivers/spi/spi-loongson-pci.c
> index ccc26efd88fe..ad90be956e1e 100644
> --- a/drivers/spi/spi-loongson-pci.c
> +++ b/drivers/spi/spi-loongson-pci.c
> @@ -1,31 +1,31 @@
>  // SPDX-License-Identifier: GPL-2.0+
> +//
>  // PCI interface driver for Loongson SPI Support
> -// Copyright (C) 2023 Loongson Technology Corporation Limited
> +//
> +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
>
>  #include <linux/pci.h>
>
>  #include "spi-loongson.h"
>
> -static int loongson_spi_pci_register(struct pci_dev *pdev,
> -                       const struct pci_device_id *ent)
> +static int loongson_spi_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
>  {
> -       int ret;
> -       void __iomem *reg_base;
>         struct device *dev = &pdev->dev;
> -       int pci_bar = 0;
> +       void __iomem *reg_base;
> +       int pci_bar = 0, ret;
>
>         ret = pcim_enable_device(pdev);
>         if (ret < 0)
> -               return dev_err_probe(dev, ret, "cannot enable pci device\n");
> +               return dev_err_probe(dev, ret, "cannot enable pci device");
>
>         reg_base = pcim_iomap_region(pdev, pci_bar, pci_name(pdev));
>         ret = PTR_ERR_OR_ZERO(reg_base);
>         if (ret)
> -               return dev_err_probe(dev, ret, "failed to request and remap memory\n");
> +               return dev_err_probe(dev, ret, "failed to request and remap memory");
>
>         ret = loongson_spi_init_controller(dev, reg_base);
>         if (ret)
> -               return dev_err_probe(dev, ret, "failed to initialize controller\n");
> +               return dev_err_probe(dev, ret, "failed to initialize controller");
>
>         return 0;
>  }
> @@ -49,5 +49,6 @@ static struct pci_driver loongson_spi_pci_driver = {
>  module_pci_driver(loongson_spi_pci_driver);
>
>  MODULE_DESCRIPTION("Loongson spi pci driver");
> +MODULE_AUTHOR("Loongson Technology Corporation Limited");
>  MODULE_LICENSE("GPL");
>  MODULE_IMPORT_NS("SPI_LOONGSON_CORE");
> diff --git a/drivers/spi/spi-loongson-plat.c b/drivers/spi/spi-loongson-plat.c
> index 6f8202264341..dcfbdd50e996 100644
> --- a/drivers/spi/spi-loongson-plat.c
> +++ b/drivers/spi/spi-loongson-plat.c
> @@ -1,6 +1,8 @@
>  // SPDX-License-Identifier: GPL-2.0+
> +//
>  // Platform driver for Loongson SPI Support
> -// Copyright (C) 2023 Loongson Technology Corporation Limited
> +//
> +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
>
>  #include <linux/err.h>
>  #include <linux/platform_device.h>
> @@ -9,9 +11,9 @@
>
>  static int loongson_spi_platform_probe(struct platform_device *pdev)
>  {
> -       int ret;
> -       void __iomem *reg_base;
>         struct device *dev = &pdev->dev;
> +       void __iomem *reg_base;
> +       int ret;
>
>         reg_base = devm_platform_ioremap_resource(pdev, 0);
>         if (IS_ERR(reg_base))
> @@ -19,7 +21,7 @@ static int loongson_spi_platform_probe(struct platform_device *pdev)
>
>         ret = loongson_spi_init_controller(dev, reg_base);
>         if (ret)
> -               return dev_err_probe(dev, ret, "failed to initialize controller\n");
> +               return dev_err_probe(dev, ret, "failed to initialize controller");
>
>         return 0;
>  }
> @@ -42,5 +44,6 @@ static struct platform_driver loongson_spi_plat_driver = {
>  module_platform_driver(loongson_spi_plat_driver);
>
>  MODULE_DESCRIPTION("Loongson spi platform driver");
> +MODULE_AUTHOR("Loongson Technology Corporation Limited");
>  MODULE_LICENSE("GPL");
>  MODULE_IMPORT_NS("SPI_LOONGSON_CORE");
> diff --git a/drivers/spi/spi-loongson.h b/drivers/spi/spi-loongson.h
> index 89efd4ce69d8..c8b722bc5fb5 100644
> --- a/drivers/spi/spi-loongson.h
> +++ b/drivers/spi/spi-loongson.h
> @@ -1,56 +1,13 @@
>  /* SPDX-License-Identifier: GPL-2.0+ */
> -/* Header File for Loongson SPI Driver. */
> -/* Copyright (C) 2023 Loongson Technology Corporation Limited */
> +//
> +// Loongson SPI Support - common header
> +//
> +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited
>
>  #ifndef __LINUX_SPI_LOONGSON_H
>  #define __LINUX_SPI_LOONGSON_H
>
> -#include <linux/bits.h>
> -#include <linux/pm.h>
> -#include <linux/types.h>
> -
> -#define        LOONGSON_SPI_SPCR_REG   0x00
> -#define        LOONGSON_SPI_SPSR_REG   0x01
> -#define        LOONGSON_SPI_FIFO_REG   0x02
> -#define        LOONGSON_SPI_SPER_REG   0x03
> -#define        LOONGSON_SPI_PARA_REG   0x04
> -#define        LOONGSON_SPI_SFCS_REG   0x05
> -#define        LOONGSON_SPI_TIMI_REG   0x06
> -
> -/* Bits definition for Loongson SPI register */
> -#define        LOONGSON_SPI_PARA_MEM_EN        BIT(0)
> -#define        LOONGSON_SPI_SPCR_CPHA  BIT(2)
> -#define        LOONGSON_SPI_SPCR_CPOL  BIT(3)
> -#define        LOONGSON_SPI_SPCR_SPE   BIT(6)
> -#define        LOONGSON_SPI_SPSR_RFEMPTY       BIT(0)
> -#define        LOONGSON_SPI_SPSR_WCOL  BIT(6)
> -#define        LOONGSON_SPI_SPSR_SPIF  BIT(7)
> -
> -#define LOONGSON_SPI_DIV_MIN           2
> -#define LOONGSON_SPI_DIV_MAX           4096
> -
> -struct device;
> -struct spi_controller;
> -
> -struct loongson_spi_suspend_store {
> -       unsigned char           spcr;
> -       unsigned char           sper;
> -       unsigned char           spsr;
> -       unsigned char           para;
> -       unsigned char           sfcs;
> -       unsigned char           timi;
> -};
> -
> -struct loongson_spi {
> -       struct  spi_controller  *controller;
> -       void __iomem            *base;
> -       int                     cs_active;
> -       unsigned int            hz;
> -       unsigned int            mode;
> -       u64                     clk_rate;
> -       struct loongson_spi_suspend_store lss;
> -};
> -
>  int loongson_spi_init_controller(struct device *dev, void __iomem *reg);
>  extern const struct dev_pm_ops loongson_spi_dev_pm_ops;
> +
>  #endif /* __LINUX_SPI_LOONGSON_H */
> --
> 2.52.0
>
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-11-04 10:04 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03  7:07 [PATCH 0/8] spi: loongson: Fixes, cleanup and IRQ support Binbin Zhou
2026-11-03  7:07 ` [PATCH 1/8] spi: loongson: Fix incorrect SPI mode caching logic Binbin Zhou
2026-11-03  7:07 ` [PATCH 2/8] spi: loongson: Fix CS idle state for SPI_CS_HIGH devices Binbin Zhou
2026-11-03  7:07 ` [PATCH 3/8] spi: loongson: Make suspend/resume registers PM-specific Binbin Zhou
2026-11-04 10:02   ` Huacai Chen
2026-11-03  7:08 ` [PATCH 4/8] spi: loongson: Fix clock divider calculation with explicit table Binbin Zhou
2026-11-03  7:08 ` [PATCH 5/8] spi: loongson: Support ACPI clock-frequency and add error handling Binbin Zhou
2026-11-03  7:08 ` [PATCH 6/8] spi: loongson: Move private definitions into the core driver Binbin Zhou
2026-11-04 10:04   ` Huacai Chen
2026-11-03  7:08 ` [PATCH 7/8] spi: loongson: Convert to regmap API Binbin Zhou
2026-11-03  7:08 ` [PATCH 8/8] spi: loongson: Add interrupt support for SPI controller Binbin Zhou

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox