mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
drm/tegra: virt: Add WAR for retrieving clock rate
Normal clock APIs are not necessarily accessible from guest, so as a workaround, use the BPMP debugfs interface to determine engine rate when calculating usage. Bug 4090576 Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com> Change-Id: Ibbffb5700162aec18317c43e9b7e03e3b8227be2 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2988957 (cherry picked from commit c8dc95c94b6c1077a6ff3e51cacef4913387828f) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2991018 Reviewed-by: Johnny Liu <johnliu@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
64b442b57d
commit
6a8e488b48
@@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023, NVIDIA Corporation.
|
||||
* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/host1x-next.h>
|
||||
#include <linux/module.h>
|
||||
@@ -15,6 +14,7 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/virt/hv-ivc.h>
|
||||
|
||||
#include "drm.h"
|
||||
@@ -48,7 +48,8 @@ struct virt_engine {
|
||||
struct tegra_drm_client client;
|
||||
|
||||
struct dentry *actmon_debugfs_dir;
|
||||
struct clk *clk;
|
||||
const char *name;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ivc_cookie_lock);
|
||||
@@ -270,6 +271,210 @@ static int virt_engine_resume(struct device *dev)
|
||||
return virt_engine_transfer(&msg, sizeof(msg));
|
||||
}
|
||||
|
||||
static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name,
|
||||
u32 *fd, u32 *len, bool write)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO),
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
ssize_t sz_name;
|
||||
int err = 0;
|
||||
|
||||
sz_name = strscpy(req.fop.name, name, sizeof(req.fop.name));
|
||||
if (sz_name < 0) {
|
||||
pr_err("File name too large: %s\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (msg.rx.ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*len = resp.fop.datalen;
|
||||
*fd = resp.fop.fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrq_debug_close(struct tegra_bpmp *bpmp, u32 fd)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUG_CLOSE),
|
||||
.frd = {
|
||||
.fd = fd,
|
||||
},
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
int err = 0;
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (msg.rx.ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name,
|
||||
char *data, size_t sz_data, u32 *nbytes)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUG_READ),
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
u32 fd = 0, len = 0;
|
||||
int remaining, err, close_err;
|
||||
|
||||
err = mrq_debug_open(bpmp, name, &fd, &len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (len > sz_data) {
|
||||
err = -EFBIG;
|
||||
goto close;
|
||||
}
|
||||
|
||||
req.frd.fd = fd;
|
||||
remaining = len;
|
||||
|
||||
while (remaining > 0) {
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0) {
|
||||
goto close;
|
||||
} else if (msg.rx.ret < 0) {
|
||||
err = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (resp.frd.readlen > remaining) {
|
||||
pr_err("%s: read data length invalid\n", __func__);
|
||||
err = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
memcpy(data, resp.frd.data, resp.frd.readlen);
|
||||
data += resp.frd.readlen;
|
||||
remaining -= resp.frd.readlen;
|
||||
}
|
||||
|
||||
*nbytes = len;
|
||||
|
||||
close:
|
||||
close_err = mrq_debug_close(bpmp, fd);
|
||||
if (!err)
|
||||
err = close_err;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_bpmp_hv_match[] = {
|
||||
{ .compatible = "nvidia,tegra186-bpmp-hv" },
|
||||
{ .compatible = "nvidia,tegra194-safe-bpmp-hv" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct tegra_bpmp *get_bpmp(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *bpmp_dev;
|
||||
struct tegra_bpmp *bpmp;
|
||||
|
||||
/* Check for bpmp device status in DT */
|
||||
bpmp_dev = of_find_matching_node_and_match(NULL, tegra_bpmp_hv_match, NULL);
|
||||
if (!bpmp_dev) {
|
||||
bpmp = ERR_PTR(-ENODEV);
|
||||
goto err_out;
|
||||
}
|
||||
if (!of_device_is_available(bpmp_dev)) {
|
||||
bpmp = ERR_PTR(-ENODEV);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(bpmp_dev);
|
||||
if (!pdev) {
|
||||
bpmp = ERR_PTR(-ENODEV);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
bpmp = platform_get_drvdata(pdev);
|
||||
if (!bpmp) {
|
||||
bpmp = ERR_PTR(-EPROBE_DEFER);
|
||||
put_device(&pdev->dev);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
return bpmp;
|
||||
err_put:
|
||||
of_node_put(bpmp_dev);
|
||||
err_out:
|
||||
return bpmp;
|
||||
}
|
||||
|
||||
static unsigned long get_clk_rate(const char *name)
|
||||
{
|
||||
char path[50], data[50] = {0};
|
||||
struct tegra_bpmp *bpmp;
|
||||
unsigned long rate;
|
||||
uint32_t len;
|
||||
int err;
|
||||
|
||||
bpmp = get_bpmp();
|
||||
if (IS_ERR(bpmp))
|
||||
return PTR_ERR(bpmp);
|
||||
|
||||
sprintf(path, "clk/%s/rate", name);
|
||||
err = mrq_debug_read(bpmp, path, data, ARRAY_SIZE(data)-1, &len);
|
||||
if (err < 0)
|
||||
goto put_bpmp;
|
||||
|
||||
err = kstrtoul(data, 10, &rate);
|
||||
if (err < 0)
|
||||
goto put_bpmp;
|
||||
|
||||
return rate;
|
||||
|
||||
put_bpmp:
|
||||
tegra_bpmp_put(bpmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int actmon_debugfs_usage_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct virt_engine *virt = s->private;
|
||||
@@ -277,10 +482,15 @@ static int actmon_debugfs_usage_show(struct seq_file *s, void *unused)
|
||||
int cycles_per_actmon_sample;
|
||||
int count;
|
||||
|
||||
rate = clk_get_rate(virt->clk);
|
||||
if (rate == 0) {
|
||||
seq_printf(s, "0\n");
|
||||
return 0;
|
||||
if (virt->rate) {
|
||||
rate = virt->rate;
|
||||
} else {
|
||||
rate = get_clk_rate(virt->name);
|
||||
if (rate == 0) {
|
||||
seq_printf(s, "0\n");
|
||||
return 0;
|
||||
}
|
||||
virt->rate = rate;
|
||||
}
|
||||
|
||||
count = host1x_actmon_read_avg_count(&virt->client.base);
|
||||
@@ -300,23 +510,21 @@ DEFINE_SHOW_ATTRIBUTE(actmon_debugfs_usage);
|
||||
|
||||
static void virt_engine_setup_actmon_debugfs(struct virt_engine *virt)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
switch (virt->client.base.class) {
|
||||
case HOST1X_CLASS_VIC:
|
||||
name = "vic";
|
||||
virt->name = "vic";
|
||||
break;
|
||||
case HOST1X_CLASS_NVENC:
|
||||
name = "msenc";
|
||||
virt->name = "msenc";
|
||||
break;
|
||||
case HOST1X_CLASS_NVDEC:
|
||||
name = "nvdec";
|
||||
virt->name = "nvdec";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
virt->actmon_debugfs_dir = debugfs_create_dir(name, NULL);
|
||||
virt->actmon_debugfs_dir = debugfs_create_dir(virt->name, NULL);
|
||||
debugfs_create_file("usage", S_IRUGO, virt->actmon_debugfs_dir, virt, &actmon_debugfs_usage_fops);
|
||||
}
|
||||
|
||||
@@ -462,12 +670,6 @@ static int virt_engine_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, virt);
|
||||
|
||||
virt->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
if (IS_ERR(virt->clk)) {
|
||||
dev_err(dev, "could not get clock: %ld\n", PTR_ERR(virt->clk));
|
||||
return PTR_ERR(virt->clk);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&virt->client.base.list);
|
||||
virt->client.base.ops = &virt_engine_client_ops;
|
||||
virt->client.base.dev = dev;
|
||||
|
||||
Reference in New Issue
Block a user