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