]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: exynos-acpm: add DVFS protocol
authorTudor Ambarus <tudor.ambarus@linaro.org>
Fri, 10 Oct 2025 12:46:32 +0000 (12:46 +0000)
committerKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Mon, 20 Oct 2025 06:49:44 +0000 (08:49 +0200)
Add ACPM DVFS protocol handler. It constructs DVFS messages that
the APM firmware can understand.

Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Reviewed-by: Peter Griffin <peter.griffin@linaro.org>
Tested-by: Peter Griffin <peter.griffin@linaro.org> # on gs101-oriole
Link: https://patch.msgid.link/20251010-acpm-clk-v6-2-321ee8826fd4@linaro.org
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
drivers/firmware/samsung/Makefile
drivers/firmware/samsung/exynos-acpm-dvfs.c [new file with mode: 0644]
drivers/firmware/samsung/exynos-acpm-dvfs.h [new file with mode: 0644]
drivers/firmware/samsung/exynos-acpm.c
include/linux/firmware/samsung/exynos-acpm-protocol.h

index 7b4c9f6f34f54fd731886d97a615fe1aa97ba9a0..80d4f89b33a9558b68c9083da675c70ec3d05f19 100644 (file)
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-acpm-protocol-objs                     := exynos-acpm.o exynos-acpm-pmic.o
+acpm-protocol-objs                     := exynos-acpm.o
+acpm-protocol-objs                     += exynos-acpm-pmic.o
+acpm-protocol-objs                     += exynos-acpm-dvfs.o
 obj-$(CONFIG_EXYNOS_ACPM_PROTOCOL)     += acpm-protocol.o
diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c
new file mode 100644 (file)
index 0000000..1c5b2b1
--- /dev/null
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2020 Samsung Electronics Co., Ltd.
+ * Copyright 2020 Google LLC.
+ * Copyright 2025 Linaro Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/firmware/samsung/exynos-acpm-protocol.h>
+#include <linux/ktime.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include "exynos-acpm.h"
+#include "exynos-acpm-dvfs.h"
+
+#define ACPM_DVFS_ID                   GENMASK(11, 0)
+#define ACPM_DVFS_REQ_TYPE             GENMASK(15, 0)
+
+#define ACPM_DVFS_FREQ_REQ             0
+#define ACPM_DVFS_FREQ_GET             1
+
+static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
+                              unsigned int acpm_chan_id, bool response)
+{
+       xfer->acpm_chan_id = acpm_chan_id;
+       xfer->txd = cmd;
+       xfer->txlen = cmdlen;
+
+       if (response) {
+               xfer->rxd = cmd;
+               xfer->rxlen = cmdlen;
+       }
+}
+
+static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id,
+                                       unsigned long rate)
+{
+       cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id);
+       cmd[1] = rate / HZ_PER_KHZ;
+       cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_REQ);
+       cmd[3] = ktime_to_ms(ktime_get());
+}
+
+int acpm_dvfs_set_rate(const struct acpm_handle *handle,
+                      unsigned int acpm_chan_id, unsigned int clk_id,
+                      unsigned long rate)
+{
+       struct acpm_xfer xfer = {0};
+       u32 cmd[4];
+
+       acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate);
+       acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, false);
+
+       return acpm_do_xfer(handle, &xfer);
+}
+
+static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id)
+{
+       cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id);
+       cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_GET);
+       cmd[3] = ktime_to_ms(ktime_get());
+}
+
+unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle,
+                                unsigned int acpm_chan_id, unsigned int clk_id)
+{
+       struct acpm_xfer xfer;
+       unsigned int cmd[4] = {0};
+       int ret;
+
+       acpm_dvfs_init_get_rate_cmd(cmd, clk_id);
+       acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, true);
+
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+               return 0;
+
+       return xfer.rxd[1] * HZ_PER_KHZ;
+}
diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.h b/drivers/firmware/samsung/exynos-acpm-dvfs.h
new file mode 100644 (file)
index 0000000..9f2778e
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Samsung Electronics Co., Ltd.
+ * Copyright 2020 Google LLC.
+ * Copyright 2025 Linaro Ltd.
+ */
+#ifndef __EXYNOS_ACPM_DVFS_H__
+#define __EXYNOS_ACPM_DVFS_H__
+
+#include <linux/types.h>
+
+struct acpm_handle;
+
+int acpm_dvfs_set_rate(const struct acpm_handle *handle,
+                      unsigned int acpm_chan_id, unsigned int id,
+                      unsigned long rate);
+unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle,
+                                unsigned int acpm_chan_id,
+                                unsigned int clk_id);
+
+#endif /* __EXYNOS_ACPM_DVFS_H__ */
index 3a69fe3234c75e0b5a93cbea6bb210dc6f69d2a6..9fa0335ccf5db32892fdf09e8d4b0a885a8f8fb5 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/types.h>
 
 #include "exynos-acpm.h"
+#include "exynos-acpm-dvfs.h"
 #include "exynos-acpm-pmic.h"
 
 #define ACPM_PROTOCOL_SEQNUM           GENMASK(21, 16)
@@ -590,8 +591,12 @@ static int acpm_channels_init(struct acpm_info *acpm)
  */
 static void acpm_setup_ops(struct acpm_info *acpm)
 {
+       struct acpm_dvfs_ops *dvfs_ops = &acpm->handle.ops.dvfs_ops;
        struct acpm_pmic_ops *pmic_ops = &acpm->handle.ops.pmic_ops;
 
+       dvfs_ops->set_rate = acpm_dvfs_set_rate;
+       dvfs_ops->get_rate = acpm_dvfs_get_rate;
+
        pmic_ops->read_reg = acpm_pmic_read_reg;
        pmic_ops->bulk_read = acpm_pmic_bulk_read;
        pmic_ops->write_reg = acpm_pmic_write_reg;
index f628bf1862c25fa018a2fe5e7e123bf05c5254b9..b1e95435240fdb895a03f178d4fb3789411b1583 100644 (file)
 struct acpm_handle;
 struct device_node;
 
+struct acpm_dvfs_ops {
+       int (*set_rate)(const struct acpm_handle *handle,
+                       unsigned int acpm_chan_id, unsigned int clk_id,
+                       unsigned long rate);
+       unsigned long (*get_rate)(const struct acpm_handle *handle,
+                                 unsigned int acpm_chan_id,
+                                 unsigned int clk_id);
+};
+
 struct acpm_pmic_ops {
        int (*read_reg)(const struct acpm_handle *handle,
                        unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
@@ -32,6 +41,7 @@ struct acpm_pmic_ops {
 };
 
 struct acpm_ops {
+       struct acpm_dvfs_ops dvfs_ops;
        struct acpm_pmic_ops pmic_ops;
 };