From: Heiko Stuebner <heiko@sntech.de>
To: broonie@kernel.org
Cc: Heiko Stuebner <heiko@sntech.de>,
linux-spi@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-rockchip@lists.infradead.org, quentin.schulz@cherry.de,
Lee Jones <lee@kernel.org>,
Sebastian Reichel <sebastian.reichel@collabora.com>,
Heiko Stuebner <heiko.stuebner@cherry.de>
Subject: [PATCH 2/2] spi: rockchip: add an atomic transfer function
Date: Fri, 6 Nov 2026 19:18:23 +0100 [thread overview]
Message-ID: <20261106181824.1361235-3-heiko@sntech.de> (raw)
In-Reply-To: <20261106181824.1361235-1-heiko@sntech.de>
From: Heiko Stuebner <heiko.stuebner@cherry.de>
Rockchip SPI controllers are often used to handle SPI connected PMICs that
also handle system power-off events, like the RK806.
When the kernel signals the power-off, all the interrupts are already
disabled, so regular SPI with its mutexes and DMA transfers does not
work anymore.
Implement an atomic transfer function that does SPI transfers polling the
controller in a loop without any DMA or IRQ.
This can successfully handle the power-off command to RK806, but also
read back the 2 chip-id registers just fine before that, as a test.
Additionally, setting ctrl->transfer to this function also let it work
just fine as the standard transfer function, so I'm hopefully not abusing
the SPI framework too much.
Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de>
---
drivers/spi/spi-rockchip.c | 88 ++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 1a81fd94b439..96582c7d5c03 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -5,6 +5,7 @@
*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -13,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/pm_runtime.h>
+#include <linux/reboot.h>
#include <linux/scatterlist.h>
#define DRIVER_NAME "rockchip-spi"
@@ -724,6 +726,74 @@ static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
return xfer->len / bytes_per_word >= rs->fifo_len;
}
+static int rockchip_spi_transfer_atomic(struct spi_device *spi, struct spi_message *msg)
+{
+ struct spi_controller *ctlr = spi->controller;
+ struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ bool cs_en = (spi->mode & SPI_CS_HIGH) ? 1 : 0;
+ struct spi_transfer *xfer;
+ int ret = 0;
+
+ pm_runtime_get_sync(rs->dev);
+ rockchip_spi_set_cs(spi, cs_en);
+
+ /* make sure all interrupts are masked and status cleared */
+ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+ writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
+ (readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY));
+
+ rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;
+
+ ret = rockchip_spi_config(rs, spi, xfer, false, false);
+ if (ret < 0)
+ goto out_disable;
+
+ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_RXFTLR);
+
+ rs->tx = xfer->tx_buf;
+ rs->rx = xfer->rx_buf;
+ rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
+ rs->rx_left = xfer->len / rs->n_bytes;
+
+ spi_enable_chip(rs, true);
+
+ while (rs->tx_left || rs->rx_left) {
+ u32 status = readl_relaxed(rs->regs + ROCKCHIP_SPI_SR);
+
+ if (rs->tx_left && !(status & SR_TF_FULL))
+ rockchip_spi_pio_writer(rs);
+
+ if (rs->rx_left && !(status & SR_RF_EMPTY))
+ rockchip_spi_pio_reader(rs);
+ }
+
+ wait_for_tx_idle(rs, false);
+
+ spi_enable_chip(rs, false);
+
+ if (xfer->cs_change && !list_is_last(&xfer->transfer_list, &msg->transfers)) {
+ rockchip_spi_set_cs(spi, !cs_en);
+ ndelay(10 * NSEC_PER_USEC);
+ rockchip_spi_set_cs(spi, cs_en);
+ }
+ }
+
+out_disable:
+ rockchip_spi_set_cs(spi, !cs_en);
+ ndelay(10 * NSEC_PER_USEC);
+ msg->status = ret;
+
+ if (msg->complete)
+ msg->complete(msg->context);
+
+ pm_runtime_put(rs->dev);
+
+ return ret;
+}
+
static int rockchip_spi_setup(struct spi_device *spi)
{
struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
@@ -752,6 +822,14 @@ static int rockchip_spi_setup(struct spi_device *spi)
return 0;
}
+static int rockchip_spi_power_off(struct sys_off_data *data)
+{
+ struct rockchip_spi *rs = data->cb_data;
+
+ /* enable controller for possible calls from shutdown handlers */
+ return pm_runtime_get_sync(rs->dev);
+}
+
static int rockchip_spi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -832,6 +910,14 @@ static int rockchip_spi_probe(struct platform_device *pdev)
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ ret = devm_register_sys_off_handler(&pdev->dev,
+ SYS_OFF_MODE_POWER_OFF_PREPARE,
+ SYS_OFF_PRIO_DEFAULT,
+ &rockchip_spi_power_off, rs);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register poweroff handler\n");
+
ctlr->auto_runtime_pm = true;
ctlr->bus_num = pdev->id;
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
@@ -860,6 +946,8 @@ static int rockchip_spi_probe(struct platform_device *pdev)
ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
ctlr->handle_err = rockchip_spi_handle_err;
+ ctlr->transfer_atomic = rockchip_spi_transfer_atomic;
+
ctlr->dma_tx = dma_request_chan(rs->dev, "tx");
if (IS_ERR(ctlr->dma_tx)) {
/* Check tx to see if we need to defer driver probing */
--
2.47.3
next prev parent reply other threads:[~2026-11-06 19:07 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 18:18 [PATCH 0/2] spi: Implement a atomic transfer as a last resort Heiko Stuebner
2026-11-06 18:18 ` [PATCH 1/2] spi: Add infrastructure for atomic sync transfers Heiko Stuebner
2026-11-09 22:02 ` Mark Brown
2026-11-06 18:18 ` Heiko Stuebner [this message]
2026-11-09 22:02 ` [PATCH 2/2] spi: rockchip: add an atomic transfer function Mark Brown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106181824.1361235-3-heiko@sntech.de \
--to=heiko@sntech.de \
--cc=broonie@kernel.org \
--cc=heiko.stuebner@cherry.de \
--cc=lee@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-rockchip@lists.infradead.org \
--cc=linux-spi@vger.kernel.org \
--cc=quentin.schulz@cherry.de \
--cc=sebastian.reichel@collabora.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).