[PATCH] nvmem: rockchip-otp: convert to runtime PM
Sascha Hauer <[email protected]>
| Newsgroups | org.infradead.lists.linux-rockchip,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260819-rockchip-nvmem-pmruntime-v1-1-01f1acf80dba@pengutronix.de> |
From: Stefan Kerkmann <[email protected]> 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 <[email protected]> Signed-off-by: Sascha Hauer <[email protected]> --- 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 <[email protected]> _______________________________________________ Linux-rockchip mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-rockchip