Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: Srinivas Kandagatla <srini@kernel.org>,
	 Heiko Stuebner <heiko@sntech.de>
Cc: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	 linux-rockchip@lists.infradead.org,
	 Stefan Kerkmann <s.kerkmann@pengutronix.de>,
	 Sascha Hauer <s.hauer@pengutronix.de>
Subject: [PATCH] nvmem: rockchip-otp: convert to runtime PM
Date: Wed, 19 Aug 2026 14:33:45 +0200	[thread overview]
Message-ID: <20260819-rockchip-nvmem-pmruntime-v1-1-01f1acf80dba@pengutronix.de> (raw)

From: Stefan Kerkmann <s.kerkmann@pengutronix.de>

The driver enables the OTP clocks inside rockchip_otp_read() and drops
them again before returning, so the controller is only alive for the
duration of a nvmem read issued by Linux.

On RK3588 that is not sufficient. Part of the OTP array is readable only
from the secure world, and OP-TEE reads it through this same controller.
Its clocks are in the normal world's CRU and are plain gates, so an SMC
into OP-TEE hits a clock-gated controller unless Linux turns them on
first. OP-TEE cannot do that without a CRU driver of its own, which
would put both worlds on the same gate registers.

Move the clock handling into runtime PM callbacks. A consumer can then
take a DL_FLAG_PM_RUNTIME device link on the OTP and hold a reference
for as long as it needs the controller, without knowing anything about
its clock list. DEFINE_RUNTIME_DEV_PM_OPS() supplies the system sleep
callbacks too, so holding one does not keep the clocks on over suspend.

Enabling the clocks is now the callbacks' job alone, hence the dependency
on PM: with CONFIG_PM=n they never run and a read would go out to a gated
controller. Reads are otherwise unchanged.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
The driver enables the OTP clocks inside rockchip_otp_read() and drops
them again before returning, so the controller is only alive for the
duration of a nvmem read issued by Linux.

On RK3588 that is not sufficient. Part of the OTP array is readable only
from the secure world, and OP-TEE reads it through this same controller.
Its clocks are in the normal world's CRU and are plain gates, so an SMC
into OP-TEE hits a clock-gated controller unless Linux turns them on
first. OP-TEE cannot do that without a CRU driver of its own, which
would put both worlds on the same gate registers.

Move the clock handling into runtime PM callbacks. A consumer can then
take a DL_FLAG_PM_RUNTIME device link on the OTP and hold a reference
for as long as it needs the controller, without knowing anything about
its clock list. DEFINE_RUNTIME_DEV_PM_OPS() supplies the system sleep
callbacks too, so holding one does not keep the clocks on over suspend.

Enabling the clocks is now the callbacks' job alone, hence the dependency
on PM: with CONFIG_PM=n they never run and a read would go out to a gated
controller. Reads are otherwise unchanged.
---
 drivers/nvmem/Kconfig        |  1 +
 drivers/nvmem/rockchip-otp.c | 39 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 74ddbd0f79b0e..3e1a66530fdea 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -337,6 +337,7 @@ config NVMEM_ROCKCHIP_OTP
 	tristate "Rockchip OTP controller support"
 	depends on ARCH_ROCKCHIP || COMPILE_TEST
 	depends on HAS_IOMEM
+	depends on PM
 	help
 	  This is a simple driver to dump specified values of Rockchip SoC
 	  from OTP, such as cpu-leakage.
diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c
index 0ec78b5e19e7d..bf1ffb46550c2 100644
--- a/drivers/nvmem/rockchip-otp.c
+++ b/drivers/nvmem/rockchip-otp.c
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 /* OTP Register Offsets */
 #define OTPC_SBPI_CTRL			0x0020
@@ -272,9 +273,9 @@ static int rockchip_otp_read(void *context, unsigned int offset,
 	if (!otp->data || !otp->data->reg_read)
 		return -EINVAL;
 
-	ret = clk_bulk_prepare_enable(otp->data->num_clks, otp->clks);
+	ret = pm_runtime_resume_and_get(otp->dev);
 	if (ret < 0) {
-		dev_err(otp->dev, "failed to prepare/enable clks\n");
+		dev_err(otp->dev, "failed to resume OTP: %d\n", ret);
 		return ret;
 	}
 
@@ -306,7 +307,7 @@ static int rockchip_otp_read(void *context, unsigned int offset,
 	}
 
 err:
-	clk_bulk_disable_unprepare(otp->data->num_clks, otp->clks);
+	pm_runtime_put(otp->dev);
 
 	return ret;
 }
@@ -457,18 +458,50 @@ static int rockchip_otp_probe(struct platform_device *pdev)
 	otp_config.priv = otp;
 	otp_config.dev = dev;
 
+	platform_set_drvdata(pdev, otp);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to enable runtime PM\n");
+
 	nvmem = devm_nvmem_register(dev, &otp_config);
 	if (IS_ERR(nvmem))
 		return dev_err_probe(dev, PTR_ERR(nvmem),
 				     "failed to register nvmem device\n");
+
+	return 0;
+}
+
+static int rockchip_otp_runtime_suspend(struct device *dev)
+{
+	struct rockchip_otp *otp = dev_get_drvdata(dev);
+
+	clk_bulk_disable_unprepare(otp->data->num_clks, otp->clks);
+
 	return 0;
 }
 
+static int rockchip_otp_runtime_resume(struct device *dev)
+{
+	struct rockchip_otp *otp = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_bulk_prepare_enable(otp->data->num_clks, otp->clks);
+	if (ret)
+		dev_err(dev, "failed to prepare/enable clks\n");
+
+	return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(rockchip_otp_pm_ops,
+				 rockchip_otp_runtime_suspend,
+				 rockchip_otp_runtime_resume, NULL);
+
 static struct platform_driver rockchip_otp_driver = {
 	.probe = rockchip_otp_probe,
 	.driver = {
 		.name = "rockchip-otp",
 		.of_match_table = rockchip_otp_match,
+		.pm = pm_ptr(&rockchip_otp_pm_ops),
 	},
 };
 

---
base-commit: bd5f485f3f026225b86573e559af0b7254ef4184
change-id: 20260819-rockchip-nvmem-pmruntime-82bd0437a216

Best regards,
-- 
Sascha Hauer <s.hauer@pengutronix.de>



             reply	other threads:[~2026-08-19 12:33 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-19 12:33 Sascha Hauer [this message]
2026-08-27 13:54 ` [PATCH] nvmem: rockchip-otp: convert to runtime PM Heiko Stuebner

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=20260819-rockchip-nvmem-pmruntime-v1-1-01f1acf80dba@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=heiko@sntech.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=s.kerkmann@pengutronix.de \
    --cc=srini@kernel.org \
    /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