summaryrefslogtreecommitdiff
path: root/drivers/firmware/samsung/exynos-acpm-dvfs.c
blob: 1c5b2b143bcc3fa2d1f92146206a2351fe0b9e95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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;
}