Commit 84a222d1 authored by Tudor Ambarus's avatar Tudor Ambarus Committed by Krzysztof Kozlowski
Browse files

firmware: exynos-acpm: add DVFS protocol



Add ACPM DVFS protocol handler. It constructs DVFS messages that
the APM firmware can understand.

Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@linaro.org>
Reviewed-by: default avatarPeter Griffin <peter.griffin@linaro.org>
Tested-by: Peter Griffin <peter.griffin@linaro.org> # on gs101-oriole
Link: https://patch.msgid.link/20251010-acpm-clk-v6-2-321ee8826fd4@linaro.org


Signed-off-by: default avatarKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
parent 83c4e3c3
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only

acpm-protocol-objs			:= exynos-acpm.o exynos-acpm-pmic.o
acpm-protocol-objs			:= exynos-acpm.o
acpm-protocol-objs			+= exynos-acpm-pmic.o
acpm-protocol-objs			+= exynos-acpm-dvfs.o
obj-$(CONFIG_EXYNOS_ACPM_PROTOCOL)	+= acpm-protocol.o
+80 −0
Original line number Diff line number Diff line
// 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;
}
+21 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright 2020 Samsung Electronics Co., Ltd.
 * Copyright 2020 Google LLC.
 * Copyright 2025 Linaro Ltd.
 */
#ifndef __EXYNOS_ACPM_DVFS_H__
#define __EXYNOS_ACPM_DVFS_H__

#include <linux/types.h>

struct acpm_handle;

int acpm_dvfs_set_rate(const struct acpm_handle *handle,
		       unsigned int acpm_chan_id, unsigned int id,
		       unsigned long rate);
unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle,
				 unsigned int acpm_chan_id,
				 unsigned int clk_id);

#endif /* __EXYNOS_ACPM_DVFS_H__ */
+5 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/types.h>

#include "exynos-acpm.h"
#include "exynos-acpm-dvfs.h"
#include "exynos-acpm-pmic.h"

#define ACPM_PROTOCOL_SEQNUM		GENMASK(21, 16)
@@ -590,8 +591,12 @@ static int acpm_channels_init(struct acpm_info *acpm)
 */
static void acpm_setup_ops(struct acpm_info *acpm)
{
	struct acpm_dvfs_ops *dvfs_ops = &acpm->handle.ops.dvfs_ops;
	struct acpm_pmic_ops *pmic_ops = &acpm->handle.ops.pmic_ops;

	dvfs_ops->set_rate = acpm_dvfs_set_rate;
	dvfs_ops->get_rate = acpm_dvfs_get_rate;

	pmic_ops->read_reg = acpm_pmic_read_reg;
	pmic_ops->bulk_read = acpm_pmic_bulk_read;
	pmic_ops->write_reg = acpm_pmic_write_reg;
+10 −0
Original line number Diff line number Diff line
@@ -13,6 +13,15 @@
struct acpm_handle;
struct device_node;

struct acpm_dvfs_ops {
	int (*set_rate)(const struct acpm_handle *handle,
			unsigned int acpm_chan_id, unsigned int clk_id,
			unsigned long rate);
	unsigned long (*get_rate)(const struct acpm_handle *handle,
				  unsigned int acpm_chan_id,
				  unsigned int clk_id);
};

struct acpm_pmic_ops {
	int (*read_reg)(const struct acpm_handle *handle,
			unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
@@ -32,6 +41,7 @@ struct acpm_pmic_ops {
};

struct acpm_ops {
	struct acpm_dvfs_ops dvfs_ops;
	struct acpm_pmic_ops pmic_ops;
};