xref: /linux/drivers/firmware/samsung/exynos-acpm-dvfs.c (revision ba65a4e7120a616d9c592750d9147f6dcafedffa)
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