From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4142E606A329 for ; Fri, 6 Nov 2026 19:07:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=O/WlM21J; dkim=pass (2048-bit key, unprotected) header.d=sntech.de header.i=@sntech.de header.a=rsa-sha256 header.s=gloria202408 header.b=ELKGUAEL DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Unsubscribe-Post: List-Owner; bh=viGs+vwwaeijL9cDm80I/p7kjifrlWJt38UtKh73BZk=; b=O/WlM21JNm27x/ Cre1SbyiotgFMX7BEL9i9H0JDq59DnpgXvYQ5rST4s04vqAyIIFjXRNABDtMwyjZ7n0jIGMC7gnU3 sNmHDsy9DeI0JyEhqnK1cd9F5aiNP6IIOyvBFV76ha2uIB9e4BW5OZ44Bl9gdh/Qk4yCZl0rOpPwh KBACWQ+do2rUG4SIK+gKDL5Ak5kDfi7SI88W6BFlZhKxB1u0Hnax6m+c/lH1gJXor0+D2KaW3ZaY8 wZaO99mxPdRXtYHL2o7e8pV9NdZLNkHZteqkKNS7ie1Z4peBuv5VycxhZt66mZktqwAEhiA2G9hUo sPqNKTX2ilPvXZxFYznQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.100.1 #2 (Red Hat Linux)) id 1xPOWH-0000000DNeS-1oBy; Fri, 06 Nov 2026 18:18:33 +0000 Received: from gloria.sntech.de ([185.11.138.130]) by bombadil.infradead.org with esmtps (Exim 4.100.1 #2 (Red Hat Linux)) id 1xPOWE-0000000DNd2-3CIK; Fri, 06 Nov 2026 18:18:32 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sntech.de; s=gloria202408; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Reply-To:Content-Type; bh=viGs+vwwaeijL9cDm80I/p7kjifrlWJt38UtKh73BZk=; b=ELKGUAELozpvtv/RXWTyPzn1zp /4JMLLL8ebK3BK4gAKPmXQRcnIwTzyqfk4P7C57iJFNYkNOEBwME9Xa5vyUjG3GIn8CpMS7TMfjZ1 XrQ8wa7zEPXPY9K1VJoXm/WlprA+GDQtFq8JzHpOT8vc8RDfLGEAW7AoABZU0tYFz3YUoXStreDks +/7g8ZtRV8F2I+s/HPLYSn3d63ze8VFvYGrZFonAEV/wIK4LmdL1++5RTlCbyAS6WoO7EZ0AjCwXP 6wPVqwEoBLNUEsbmsS1/erQQqz/LGTlTGsQoGJ652B9JJRzIQwjXz4NFgRHWDl3TsQzHMqfQh+c8S KwOpjx2Q==; From: Heiko Stuebner To: broonie@kernel.org Cc: Heiko Stuebner , linux-spi@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, quentin.schulz@cherry.de, Lee Jones , Sebastian Reichel , Heiko Stuebner Subject: [PATCH 2/2] spi: rockchip: add an atomic transfer function Date: Fri, 6 Nov 2026 19:18:23 +0100 Message-ID: <20261106181824.1361235-3-heiko@sntech.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261106181824.1361235-1-heiko@sntech.de> References: <20261106181824.1361235-1-heiko@sntech.de> X-Archived-By: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20261106_101830_892597_92EBE09F X-CRM114-Status: GOOD ( 19.55 ) X-Spam-Score: -2.1 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 From: Heiko Stuebner 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 --- 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 +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #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