Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7737ef93 authored by Santosh Mardi's avatar Santosh Mardi Committed by Gerrit - the friendly Code Review server
Browse files

Firmware: arm_scmi: add initial support for memlat vendor protocol



The memlat protocol is intended for the management of memlat based
scaling for devices like l3.

The comands in this protocol provide functionality to configure
memlat details, includes the cpugrp, memlat monitors, memlat
tunables, configuring the log levels and debugging support.

Change-Id: I1b45f833eff3aa82c3ec2c0debb703dbf8838c8a
Signed-off-by: default avatarSantosh Mardi <gsantosh@codeaurora.org>
parent 1563778e
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -27,6 +27,18 @@ config ARM_SCMI_PROTOCOL
	  This protocol library provides interface for all the client drivers
	  making use of the features offered by the SCMI.

config QTI_SCMI_MEMLAT_PROTOCOL
	tristate "Qualcomm Technologies, Inc. SCMI MEMLAT vendor Protocol"
	depends on ARM || ARM64 || COMPILE_TEST
	depends on ARM_SCMI_PROTOCOL && QCOM_RIMPS
	help
	  System Control and Management Interface (SCMI) memlat vendor protocol
	  this protocol provides interface to communicate with micro controller
	  which is executing the hw memlat governor

	  This driver defines the comands or message ID's used for this
	  communication and also exposes the ops used by clients.

config ARM_SCMI_POWER_DOMAIN
	tristate "SCMI power domain driver"
	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+1 −0
Original line number Diff line number Diff line
@@ -4,3 +4,4 @@ scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
obj-$(CONFIG_QTI_SCMI_MEMLAT_PROTOCOL) += memlat_vendor.o
+335 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
 */

#include "common.h"

#define MAX_MAP_ENTRIES 14
#define MAX_PMU_ENTRIES 24

#define SCMI_VENDOR_MSG_START   (3)
#define SCMI_VENDOR_MSG_MODULE_START   (16)
#define SCMI_MAX_RX_SIZE	128
#define SCMI_MAX_GET_DATA_SIZE	124

enum scmi_memlat_protocol_cmd {
	MEMLAT_SET_LOG_LEVEL = SCMI_VENDOR_MSG_START,
	MEMLAT_SET_CPU_GROUP = SCMI_VENDOR_MSG_MODULE_START,
	MEMLAT_SET_MONITOR,
	MEMLAT_COMMON_PMU_MAP,
	MEMLAT_MON_PMU_MAP,
	MEMLAT_RATIO_CEIL,
	MEMLAT_STALL_FLOOR,
	MEMLAT_L3_L2WB_PCT,
	MEMLAT_L3_IPM_FILTER,
	MEMLAT_SAMPLE_MS,
	MEMLAT_MON_FREQ_MAP,
	MEMLAT_SET_MIN_FREQ,
	MEMLAT_SET_MAX_FREQ,
	MEMLAT_START_MONITOR,
	MEMLAT_STOP_MONITOR,
	MEMLAT_GET_DATA = 0xFF,
	MEMLAT_MAX_MSG
};

struct node_msg {
	uint32_t cpumask;
	uint32_t mon_type;
};

struct scalar_param_msg {
	uint32_t cpumask;
	uint32_t mon_type;
	uint32_t val;
};

struct map_table {
	uint32_t v1;
	uint32_t v2;
};

struct map_param_msg {
	uint32_t cpumask;
	uint32_t mon_type;
	uint32_t nr_rows;
	struct map_table tbl[MAX_MAP_ENTRIES];
};

struct pmu_map_msg {
	uint32_t cpumask;
	uint32_t mon_type;
	uint32_t nr_entries;
	uint32_t pmu[MAX_PMU_ENTRIES];
};

static int scmi_set_cpugrp_mon(const struct scmi_handle *handle,
			u32 cpus_mpidr, u32 mon_type, u32 msg_id)
{
	int ret = 0;
	struct scmi_xfer *t;
	struct node_msg *msg;

	ret = scmi_xfer_get_init(handle, msg_id,
				SCMI_PROTOCOL_MEMLAT,
				sizeof(*msg), sizeof(*msg), &t);
	if (ret)
		return ret;

	msg = t->tx.buf;
	msg->cpumask = cpu_to_le32(cpus_mpidr);
	msg->mon_type = cpu_to_le32(mon_type);
	ret = scmi_do_xfer(handle, t);
	scmi_xfer_put(handle, t);

	return ret;
}

static int scmi_set_mon(const struct scmi_handle *handle,
			u32 cpus_mpidr, u32 mon_type)
{
	return scmi_set_cpugrp_mon(handle, cpus_mpidr,
				mon_type, MEMLAT_SET_MONITOR);
}

static int scmi_set_cpu_grp(const struct scmi_handle *handle,
			u32 cpus_mpidr, u32 mon_type)
{
	return scmi_set_cpugrp_mon(handle, cpus_mpidr,
				mon_type, MEMLAT_SET_CPU_GROUP);
}

static int scmi_send_pmu_map_command(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 nr_entries,
				void *buf, u32 msg_id)
{
	int ret, i = 0;
	struct scmi_xfer *t;
	struct pmu_map_msg *msg;
	u32 *dst;
	struct map_table *src = buf;

	if (nr_entries > MAX_PMU_ENTRIES)
		return -EINVAL;

	ret = scmi_xfer_get_init(handle, msg_id,
				SCMI_PROTOCOL_MEMLAT,
				sizeof(*msg), sizeof(*msg), &t);
	if (ret)
		return ret;

	msg = t->tx.buf;
	msg->cpumask = cpu_to_le32(cpus_mpidr);
	msg->mon_type = cpu_to_le32(mon_type);
	msg->nr_entries = cpu_to_le32(nr_entries);
	dst = msg->pmu;

	for (i = 0; i < nr_entries; i++)
		dst[i] = cpu_to_le32(src[i].v2);

	ret = scmi_do_xfer(handle, t);
	scmi_xfer_put(handle, t);
	return ret;
}

static int scmi_common_pmu_map(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type,
				u32 nr_entries, void *buf)
{
	return scmi_send_pmu_map_command(handle, cpus_mpidr,
					mon_type, nr_entries, buf,
					MEMLAT_COMMON_PMU_MAP);
}

static int scmi_mon_pmu_map(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type,
				u32 nr_entries, void *buf)
{
	return scmi_send_pmu_map_command(handle, cpus_mpidr,
					mon_type, nr_entries, buf,
					MEMLAT_MON_PMU_MAP);
}

static int scmi_freq_map(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type,
				u32 nr_rows, void *buf)
{
	int ret, i = 0;
	struct scmi_xfer *t;
	struct map_param_msg *msg;
	struct map_table *tbl, *src = buf;

	if (nr_rows > MAX_MAP_ENTRIES)
		return -EINVAL;

	ret = scmi_xfer_get_init(handle, MEMLAT_MON_FREQ_MAP,
				SCMI_PROTOCOL_MEMLAT,
				sizeof(*msg), sizeof(*msg), &t);
	if (ret)
		return ret;

	msg = t->tx.buf;
	msg->cpumask = cpu_to_le32(cpus_mpidr);
	msg->mon_type = cpu_to_le32(mon_type);
	msg->nr_rows = cpu_to_le32(nr_rows);
	tbl = msg->tbl;

	for (i = 0; i < nr_rows; i++) {
		tbl[i].v1 = cpu_to_le32(src[i].v1);
		tbl[i].v2 = cpu_to_le32(src[i].v2);
	}
	ret = scmi_do_xfer(handle, t);
	scmi_xfer_put(handle, t);
	return ret;
}

#define scmi_send_cmd(name, _msg_id)					\
static int scmi_##name(const struct scmi_handle *handle,		\
				u32 cpus_mpidr, u32 mon_type, u32 val)	\
{									\
	int ret = 0;							\
	struct scmi_xfer *t;						\
	struct scalar_param_msg *msg;					\
	ret = scmi_xfer_get_init(handle, _msg_id,			\
				SCMI_PROTOCOL_MEMLAT,			\
				sizeof(*msg), sizeof(*msg), &t);	\
	if (ret)							\
		return ret;						\
	msg = t->tx.buf;						\
	msg->cpumask = cpu_to_le32(cpus_mpidr);				\
	msg->mon_type = cpu_to_le32(mon_type);				\
	msg->val = cpu_to_le32(val);					\
	ret = scmi_do_xfer(handle, t);					\
	scmi_xfer_put(handle, t);					\
	return ret;							\
}									\

scmi_send_cmd(ratio_ceil, MEMLAT_RATIO_CEIL);
scmi_send_cmd(stall_floor, MEMLAT_STALL_FLOOR);
scmi_send_cmd(l2wb_pct, MEMLAT_L3_L2WB_PCT);
scmi_send_cmd(l2wb_filter, MEMLAT_L3_IPM_FILTER);
scmi_send_cmd(sample_ms, MEMLAT_SAMPLE_MS);
scmi_send_cmd(min_freq, MEMLAT_SET_MIN_FREQ);
scmi_send_cmd(max_freq, MEMLAT_SET_MAX_FREQ);

static int scmi_send_start_stop(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 msg_id)
{
	int ret = 0;
	struct scmi_xfer *t;
	struct scalar_param_msg *msg;

	ret = scmi_xfer_get_init(handle, msg_id,
				SCMI_PROTOCOL_MEMLAT,
				sizeof(*msg), sizeof(*msg), &t);
	if (ret)
		return ret;
	msg = t->tx.buf;
	msg->cpumask = cpu_to_le32(cpus_mpidr);
	msg->mon_type = cpu_to_le32(mon_type);
	ret = scmi_do_xfer(handle, t);
	scmi_xfer_put(handle, t);

	return ret;
}

static int scmi_stop_mon(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type)
{
	return scmi_send_start_stop(handle, cpus_mpidr,
				mon_type, MEMLAT_STOP_MONITOR);
}

static int scmi_start_mon(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type)
{
	return scmi_send_start_stop(handle, cpus_mpidr,
				mon_type, MEMLAT_START_MONITOR);
}

static int scmi_get_data(const struct scmi_handle *handle, u8 *buf)
{
	int ret = 0;
	struct scmi_xfer *t;
	u32 prev_cnt = 0;
	struct scalar_param_msg *msg;

	ret = scmi_xfer_get_init(handle, MEMLAT_GET_DATA,
				 SCMI_PROTOCOL_MEMLAT, sizeof(*msg),
				 SCMI_MAX_RX_SIZE, &t);
	if (ret)
		return ret;
	do {
		ret = scmi_do_xfer(handle, t);
		if (ret == -ETIMEDOUT)
			ret = scmi_do_xfer(handle, t);
		if (ret < 0)
			break;

		memcpy((buf + prev_cnt), t->rx.buf, t->rx.len);
		prev_cnt += t->rx.len;
	} while (t->rx.len >= SCMI_MAX_GET_DATA_SIZE);

	scmi_xfer_put(handle, t);

	return ret;
}

static int scmi_set_log_level(const struct scmi_handle *handle, u32 val)
{
	int ret = 0;
	struct scmi_xfer *t;
	u32 *ptr;

	ret = scmi_xfer_get_init(handle, MEMLAT_SET_LOG_LEVEL,
				SCMI_PROTOCOL_MEMLAT, sizeof(u32),
				sizeof(u32), &t);
	if (ret)
		return ret;
	ptr = (u32 *)t->tx.buf;
	*ptr = cpu_to_le32(val);
	ret = scmi_do_xfer(handle, t);
	scmi_xfer_put(handle, t);

	return ret;
}

static struct scmi_memlat_vendor_ops memlat_ops = {
	.set_cpu_grp = scmi_set_cpu_grp,
	.freq_map = scmi_freq_map,
	.set_mon = scmi_set_mon,
	.common_pmu_map = scmi_common_pmu_map,
	.mon_pmu_map = scmi_mon_pmu_map,
	.ratio_ceil = scmi_ratio_ceil,
	.stall_floor = scmi_stall_floor,
	.sample_ms = scmi_sample_ms,
	.l2wb_filter = scmi_l2wb_filter,
	.l2wb_pct = scmi_l2wb_pct,
	.min_freq = scmi_min_freq,
	.max_freq = scmi_max_freq,
	.start_monitor = scmi_start_mon,
	.stop_monitor = scmi_stop_mon,
	.set_log_level = scmi_set_log_level,
	.get_data = scmi_get_data,
};

static int scmi_memlat_vendor_protocol_init(struct scmi_handle *handle)
{
	u32 version;

	scmi_version_get(handle, SCMI_PROTOCOL_MEMLAT, &version);

	dev_dbg(handle->dev, "memlat version %d.%d\n",
		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));

	handle->memlat_ops = &memlat_ops;

	return 0;
}

static int __init scmi_memlat_init(void)
{
	return scmi_protocol_register(SCMI_PROTOCOL_MEMLAT,
				      &scmi_memlat_vendor_protocol_init);
}
subsys_initcall(scmi_memlat_init);
+60 −0
Original line number Diff line number Diff line
@@ -207,6 +207,64 @@ struct scmi_reset_ops {
	int (*deassert)(const struct scmi_handle *handle, u32 domain);
};

/**
 * struct scmi_memlat_vendor_ops - represents the various operations provided
 *	by SCMI HW Memlat Protocol
 *
 * @cpu_grp: set the cpugrp
 * @set_mon: set the supported monitors
 * @common_pmu_map: sets the common PMU map supported by gov
 * @mon_pmu_map: sets the common PMU map supported by gov
 * @ratio_ceil: sets the ratio_ceil needed for hw memlat governor
 * @stall_floor: sets the stall_floor needed for hw memlat governor
 * @l2wb_pct: sets the stall_floor needed for hw memlat governor
 * @l2wb_filter: sets the l2wb_filter needed for hw memlat governor
 * @sample_ms: sets the sample_ms at this interval governor will poll
 * @freq_map: sets the freq_map of the monitor
 * @min_freq: sets the min_freq of monitor
 * @max_freq: sets the max_freq of monitor
 * @start_mon: starts monitor in rimps
 * @stop_mon: stops monitor in rimps
 * @log_level: configure the supported log_level in rimps
 * @get_data: added for debug purpose gets the data structure information
 */
struct scmi_memlat_vendor_ops {
	int (*set_cpu_grp)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type);
	int (*set_mon)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type);
	int (*common_pmu_map)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type,
				u32 nr_rows, void *buf);
	int (*mon_pmu_map)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type,
				u32 nr_rows, void *buf);
	int (*ratio_ceil)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*stall_floor)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*l2wb_pct)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*l2wb_filter)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*sample_ms)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*freq_map)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type,
				u32 nr_rows, void *buf);
	int (*min_freq)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*max_freq)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type, u32 val);
	int (*start_monitor)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type);
	int (*stop_monitor)(const struct scmi_handle *handle,
				u32 cpus_mpidr, u32 mon_type);
	int (*set_log_level)(const struct scmi_handle *handle, u32 val);
	int (*get_data)(const struct scmi_handle *handle, u8 *buf);
};


/**
 * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
 *
@@ -236,6 +294,7 @@ struct scmi_handle {
	struct scmi_power_ops *power_ops;
	struct scmi_sensor_ops *sensor_ops;
	struct scmi_reset_ops *reset_ops;
	struct scmi_memlat_vendor_ops *memlat_ops;
	/* for protocol internal use */
	void *perf_priv;
	void *clk_priv;
@@ -252,6 +311,7 @@ enum scmi_std_protocol {
	SCMI_PROTOCOL_CLOCK = 0x14,
	SCMI_PROTOCOL_SENSOR = 0x15,
	SCMI_PROTOCOL_RESET = 0x16,
	SCMI_PROTOCOL_MEMLAT = 0x80,
};

struct scmi_device {