android_kernel_xiaomi_sm8350/drivers/firmware/arm_scmi/memlat_vendor.c
Santosh Mardi 7737ef9307 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: Santosh Mardi <gsantosh@codeaurora.org>
2020-09-11 12:30:38 -07:00

336 lines
7.8 KiB
C

// 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);