1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright 2020 Samsung Electronics Co., Ltd.
4 * Copyright 2020 Google LLC.
5 * Copyright 2025 Linaro Ltd.
6 */
7
8 #include <linux/bitfield.h>
9 #include <linux/firmware/samsung/exynos-acpm-protocol.h>
10 #include <linux/ktime.h>
11 #include <linux/types.h>
12 #include <linux/units.h>
13
14 #include "exynos-acpm.h"
15 #include "exynos-acpm-dvfs.h"
16
17 #define ACPM_DVFS_ID GENMASK(11, 0)
18 #define ACPM_DVFS_REQ_TYPE GENMASK(15, 0)
19
20 #define ACPM_DVFS_FREQ_REQ 0
21 #define ACPM_DVFS_FREQ_GET 1
22
acpm_dvfs_set_xfer(struct acpm_xfer * xfer,u32 * cmd,size_t cmdlen,unsigned int acpm_chan_id,bool response)23 static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
24 unsigned int acpm_chan_id, bool response)
25 {
26 xfer->acpm_chan_id = acpm_chan_id;
27 xfer->txd = cmd;
28 xfer->txlen = cmdlen;
29
30 if (response) {
31 xfer->rxd = cmd;
32 xfer->rxlen = cmdlen;
33 }
34 }
35
acpm_dvfs_init_set_rate_cmd(u32 cmd[4],unsigned int clk_id,unsigned long rate)36 static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id,
37 unsigned long rate)
38 {
39 cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id);
40 cmd[1] = rate / HZ_PER_KHZ;
41 cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_REQ);
42 cmd[3] = ktime_to_ms(ktime_get());
43 }
44
acpm_dvfs_set_rate(const struct acpm_handle * handle,unsigned int acpm_chan_id,unsigned int clk_id,unsigned long rate)45 int acpm_dvfs_set_rate(const struct acpm_handle *handle,
46 unsigned int acpm_chan_id, unsigned int clk_id,
47 unsigned long rate)
48 {
49 struct acpm_xfer xfer = {0};
50 u32 cmd[4];
51
52 acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate);
53 acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, false);
54
55 return acpm_do_xfer(handle, &xfer);
56 }
57
acpm_dvfs_init_get_rate_cmd(u32 cmd[4],unsigned int clk_id)58 static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id)
59 {
60 cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id);
61 cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_GET);
62 cmd[3] = ktime_to_ms(ktime_get());
63 }
64
acpm_dvfs_get_rate(const struct acpm_handle * handle,unsigned int acpm_chan_id,unsigned int clk_id)65 unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle,
66 unsigned int acpm_chan_id, unsigned int clk_id)
67 {
68 struct acpm_xfer xfer;
69 unsigned int cmd[4] = {0};
70 int ret;
71
72 acpm_dvfs_init_get_rate_cmd(cmd, clk_id);
73 acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, true);
74
75 ret = acpm_do_xfer(handle, &xfer);
76 if (ret)
77 return 0;
78
79 return xfer.rxd[1] * HZ_PER_KHZ;
80 }
81