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