summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2026-10-03 11:47:59 +0200
committerThierry Reding <treding@nvidia.com>2026-10-03 11:47:59 +0200
commit436ae3f3ebe95cd8ee71e35fd3cc91f9fc88aae1 (patch)
tree74f727ee172fb8a928b3f6d0fff0332676830ed8
parentaf351a8392e5e97366c764f5de80d17145677716 (diff)
parent1bbcf4a19f1495f769b69e9569d8c1e81df074f5 (diff)
downloadlinux-next-436ae3f3ebe95cd8ee71e35fd3cc91f9fc88aae1.tar.gz
linux-next-436ae3f3ebe95cd8ee71e35fd3cc91f9fc88aae1.zip
Merge branch for-7.4/firmware into for-next
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--Documentation/ABI/testing/sysfs-platform-tegra-bpmp48
-rw-r--r--drivers/firmware/tegra/Makefile1
-rw-r--r--drivers/firmware/tegra/bpmp-debugfs.c9
-rw-r--r--drivers/firmware/tegra/bpmp-private.h39
-rw-r--r--drivers/firmware/tegra/bpmp-sysfs.c207
-rw-r--r--drivers/firmware/tegra/bpmp-tegra186.c14
-rw-r--r--drivers/firmware/tegra/bpmp-tegra210.c17
-rw-r--r--drivers/firmware/tegra/bpmp.c434
-rw-r--r--include/soc/tegra/bpmp-abi.h165
-rw-r--r--include/soc/tegra/bpmp.h2
10 files changed, 880 insertions, 56 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-tegra-bpmp b/Documentation/ABI/testing/sysfs-platform-tegra-bpmp
new file mode 100644
index 000000000000..5d0b525bd72a
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-tegra-bpmp
@@ -0,0 +1,48 @@
+What: /sys/bus/platform/devices/<bpmp-device>/mbwt/<bandwidth-group>/<traffic-type>/bandwidth
+Date: September 2026
+KernelVersion: 7.4
+Contact: Aniruddha TVS Rao <anrao@nvidia.com>
+Description:
+ Provides access to Memory Bandwidth Throttler (MBWT)
+ controls exposed by BPMP firmware for PCIe traffic and GPU
+ traffic connected over the chip-to-chip link (NVCLINK) on
+ the path to DRAM.
+
+ The attributes are present only when BPMP firmware reports
+ support for the MBWT GET_BW and SET_BW requests through the
+ MBWT query ABI.
+
+ Each <bandwidth-group> directory identifies one PCIe bandwidth
+ group. The available groups and their directory names are
+ platform-specific. Current platforms use pcie0 through pcie5.
+ Each bandwidth group has a single shared cap for all traffic
+ in that group. A group may contain only PCIe devices, only a
+ GPU connected over NVCLINK, or both PCIe and GPU traffic in a
+ bifurcated topology.
+
+ The <traffic-type> directory selects the traffic type for the
+ selected group. The following traffic types may be available:
+
+ pcie_read
+ PCIe read traffic
+
+ pcie_write
+ PCIe write traffic
+
+ nvclink
+ GPU traffic connected over NVCLINK
+
+ Reading a bandwidth attribute returns the bandwidth cap in GB/s
+ reported by firmware for that bandwidth group and traffic
+ type.
+
+ Writing an integer to a bandwidth attribute programs the target
+ bandwidth cap in GB/s for that bandwidth group and traffic
+ type.
+
+ Examples:
+ cat .../mbwt/pcie0/pcie_write/bandwidth
+ echo 100 > .../mbwt/pcie0/pcie_write/bandwidth
+
+Users: Platform integration and bandwidth tuning on systems with BPMP
+ firmware MBWT support.
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
index 41e2e4dc31d6..97f7a5db9d0a 100644
--- a/drivers/firmware/tegra/Makefile
+++ b/drivers/firmware/tegra/Makefile
@@ -6,5 +6,6 @@ tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o
tegra-bpmp-$(CONFIG_ARCH_TEGRA_234_SOC) += bpmp-tegra186.o
tegra-bpmp-$(CONFIG_ARCH_TEGRA_264_SOC) += bpmp-tegra186.o
tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o
+tegra-bpmp-$(CONFIG_SYSFS) += bpmp-sysfs.o
obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o
obj-$(CONFIG_TEGRA_IVC) += ivc.o
diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c
index 33c6300af964..2efc48b522ac 100644
--- a/drivers/firmware/tegra/bpmp-debugfs.c
+++ b/drivers/firmware/tegra/bpmp-debugfs.c
@@ -6,6 +6,7 @@
#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
+#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -773,6 +774,11 @@ free:
static DEFINE_MUTEX(bpmp_debugfs_root_lock);
static struct dentry *bpmp_debugfs_root;
+static bool bpmp_debugfs_enabled = true;
+
+module_param_named(debugfs, bpmp_debugfs_enabled, bool, 0444);
+MODULE_PARM_DESC(debugfs,
+ "Populate the BPMP firmware debugfs (default: enabled)");
static struct dentry *bpmp_debugfs_get_root(void)
{
@@ -789,6 +795,9 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
bool inband;
int err;
+ if (!bpmp_debugfs_enabled)
+ return 0;
+
inband = tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUG);
if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h
index 07c3d46abb87..39cf31771836 100644
--- a/drivers/firmware/tegra/bpmp-private.h
+++ b/drivers/firmware/tegra/bpmp-private.h
@@ -10,6 +10,7 @@
struct tegra_bpmp_ops {
int (*init)(struct tegra_bpmp *bpmp);
+ int (*init_providers)(struct tegra_bpmp *bpmp);
void (*deinit)(struct tegra_bpmp *bpmp);
bool (*is_response_ready)(struct tegra_bpmp_channel *channel);
bool (*is_request_ready)(struct tegra_bpmp_channel *channel);
@@ -23,7 +24,45 @@ struct tegra_bpmp_ops {
int (*resume)(struct tegra_bpmp *bpmp);
};
+struct tegra_bpmp_mbwt_vc {
+ const char *name;
+ unsigned int type;
+};
+
+struct tegra_bpmp_mbwt_group {
+ const char *name;
+ unsigned int id;
+ const struct tegra_bpmp_mbwt_vc *vcs;
+ unsigned int num_vcs;
+};
+
+struct tegra_bpmp_mbwt_soc {
+ const struct tegra_bpmp_mbwt_group *groups;
+ unsigned int num_groups;
+};
+
extern const struct tegra_bpmp_ops tegra186_bpmp_ops;
extern const struct tegra_bpmp_ops tegra210_bpmp_ops;
+int tegra_bpmp_init_channels(struct tegra_bpmp *bpmp);
+int tegra_bpmp_init_ping(struct tegra_bpmp *bpmp);
+void tegra_bpmp_deinit_ping(struct tegra_bpmp *bpmp);
+int tegra_bpmp_init_dt_providers(struct tegra_bpmp *bpmp);
+
+bool tegra_bpmp_mbwt_cmd_is_supported(struct tegra_bpmp *bpmp,
+ unsigned int cmd_code);
+int tegra_bpmp_mbwt_get(struct tegra_bpmp *bpmp, unsigned int instance,
+ unsigned int vc_type, unsigned int *bandwidth);
+int tegra_bpmp_mbwt_set(struct tegra_bpmp *bpmp, unsigned int instance,
+ unsigned int vc_type, unsigned int bandwidth);
+
+#ifdef CONFIG_SYSFS
+int tegra_bpmp_init_sysfs(struct tegra_bpmp *bpmp);
+#else
+static inline int tegra_bpmp_init_sysfs(struct tegra_bpmp *bpmp)
+{
+ return 0;
+}
+#endif
+
#endif
diff --git a/drivers/firmware/tegra/bpmp-sysfs.c b/drivers/firmware/tegra/bpmp-sysfs.c
new file mode 100644
index 000000000000..c7bf7f13f5d7
--- /dev/null
+++ b/drivers/firmware/tegra/bpmp-sysfs.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026, NVIDIA CORPORATION.
+ */
+
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/kstrtox.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+#include "bpmp-private.h"
+
+struct tegra_bpmp_mbwt_attr {
+ struct kobj_attribute attr;
+ struct tegra_bpmp_mbwt_sysfs *mbwt;
+ struct kobject *kobj;
+ unsigned int instance;
+ unsigned int vc_type;
+};
+
+struct tegra_bpmp_mbwt_sysfs {
+ struct tegra_bpmp *bpmp;
+ struct kobject *root;
+ struct kobject **groups;
+ struct tegra_bpmp_mbwt_attr *attrs;
+ unsigned int num_groups;
+ unsigned int num_attrs;
+ /* Serializes bandwidth requests to firmware. */
+ struct mutex lock;
+};
+
+static struct tegra_bpmp_mbwt_attr *
+tegra_bpmp_mbwt_attr_from_kobj_attr(struct kobj_attribute *attr)
+{
+ return container_of(attr, struct tegra_bpmp_mbwt_attr, attr);
+}
+
+static ssize_t tegra_bpmp_mbwt_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct tegra_bpmp_mbwt_attr *mbwt_attr;
+ struct tegra_bpmp_mbwt_sysfs *mbwt;
+ unsigned int bandwidth;
+ int err;
+
+ mbwt_attr = tegra_bpmp_mbwt_attr_from_kobj_attr(attr);
+ mbwt = mbwt_attr->mbwt;
+
+ mutex_lock(&mbwt->lock);
+ err = tegra_bpmp_mbwt_get(mbwt->bpmp, mbwt_attr->instance,
+ mbwt_attr->vc_type, &bandwidth);
+ mutex_unlock(&mbwt->lock);
+ if (err)
+ return err;
+
+ return sysfs_emit(buf, "%u\n", bandwidth);
+}
+
+static ssize_t tegra_bpmp_mbwt_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tegra_bpmp_mbwt_attr *mbwt_attr;
+ struct tegra_bpmp_mbwt_sysfs *mbwt;
+ unsigned int bandwidth;
+ int err;
+
+ err = kstrtou32(buf, 0, &bandwidth);
+ if (err)
+ return err;
+
+ mbwt_attr = tegra_bpmp_mbwt_attr_from_kobj_attr(attr);
+ mbwt = mbwt_attr->mbwt;
+
+ mutex_lock(&mbwt->lock);
+ err = tegra_bpmp_mbwt_set(mbwt->bpmp, mbwt_attr->instance,
+ mbwt_attr->vc_type, bandwidth);
+ mutex_unlock(&mbwt->lock);
+ if (err)
+ return err;
+
+ return count;
+}
+
+static void tegra_bpmp_mbwt_sysfs_teardown(void *data)
+{
+ struct tegra_bpmp_mbwt_sysfs *mbwt = data;
+ unsigned int i;
+
+ for (i = 0; i < mbwt->num_attrs; i++) {
+ sysfs_remove_file(mbwt->attrs[i].kobj,
+ &mbwt->attrs[i].attr.attr);
+ kobject_put(mbwt->attrs[i].kobj);
+ }
+
+ for (i = 0; i < mbwt->num_groups; i++)
+ kobject_put(mbwt->groups[i]);
+
+ kobject_put(mbwt->root);
+}
+
+static int tegra_bpmp_mbwt_sysfs_add_group(struct tegra_bpmp_mbwt_sysfs *mbwt,
+ const struct tegra_bpmp_mbwt_group *group)
+{
+ struct tegra_bpmp_mbwt_attr *attr;
+ struct kobject *group_kobj;
+ unsigned int i;
+ int err;
+
+ group_kobj = kobject_create_and_add(group->name, mbwt->root);
+ if (!group_kobj)
+ return -ENOMEM;
+
+ mbwt->groups[mbwt->num_groups++] = group_kobj;
+
+ for (i = 0; i < group->num_vcs; i++) {
+ attr = &mbwt->attrs[mbwt->num_attrs];
+ attr->kobj = kobject_create_and_add(group->vcs[i].name,
+ group_kobj);
+ if (!attr->kobj)
+ return -ENOMEM;
+
+ sysfs_attr_init(&attr->attr.attr);
+ attr->attr.attr.name = "bandwidth";
+ attr->attr.attr.mode = 0644;
+ attr->attr.show = tegra_bpmp_mbwt_show;
+ attr->attr.store = tegra_bpmp_mbwt_store;
+ attr->mbwt = mbwt;
+ attr->instance = group->id;
+ attr->vc_type = group->vcs[i].type;
+
+ err = sysfs_create_file(attr->kobj, &attr->attr.attr);
+ if (err) {
+ kobject_put(attr->kobj);
+ return err;
+ }
+
+ mbwt->num_attrs++;
+ }
+
+ return 0;
+}
+
+int tegra_bpmp_init_sysfs(struct tegra_bpmp *bpmp)
+{
+ const struct tegra_bpmp_mbwt_soc *soc = bpmp->soc->mbwt;
+ struct tegra_bpmp_mbwt_sysfs *mbwt;
+ unsigned int i, num_attrs = 0;
+ int err;
+
+ if (!soc)
+ return 0;
+
+ if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_SOCHUB_MBWT))
+ return 0;
+
+ if (!tegra_bpmp_mbwt_cmd_is_supported(bpmp, CMD_SOCHUB_MBWT_GET_BW) ||
+ !tegra_bpmp_mbwt_cmd_is_supported(bpmp, CMD_SOCHUB_MBWT_SET_BW))
+ return 0;
+
+ mbwt = devm_kzalloc(bpmp->dev, sizeof(*mbwt), GFP_KERNEL);
+ if (!mbwt)
+ return -ENOMEM;
+
+ mbwt->bpmp = bpmp;
+ mutex_init(&mbwt->lock);
+
+ mbwt->groups = devm_kcalloc(bpmp->dev, soc->num_groups,
+ sizeof(*mbwt->groups), GFP_KERNEL);
+ if (!mbwt->groups)
+ return -ENOMEM;
+
+ for (i = 0; i < soc->num_groups; i++)
+ num_attrs += soc->groups[i].num_vcs;
+
+ mbwt->attrs = devm_kcalloc(bpmp->dev, num_attrs,
+ sizeof(*mbwt->attrs), GFP_KERNEL);
+ if (!mbwt->attrs)
+ return -ENOMEM;
+
+ mbwt->root = kobject_create_and_add("mbwt", &bpmp->dev->kobj);
+ if (!mbwt->root)
+ return -ENOMEM;
+
+ for (i = 0; i < soc->num_groups; i++) {
+ err = tegra_bpmp_mbwt_sysfs_add_group(mbwt, &soc->groups[i]);
+ if (err)
+ goto remove_sysfs;
+ }
+
+ err = devm_add_action_or_reset(bpmp->dev,
+ tegra_bpmp_mbwt_sysfs_teardown, mbwt);
+ if (err)
+ return err;
+
+ return 0;
+
+remove_sysfs:
+ tegra_bpmp_mbwt_sysfs_teardown(mbwt);
+
+ return err;
+}
diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c
index 64863db7a715..c34f6d2d275c 100644
--- a/drivers/firmware/tegra/bpmp-tegra186.c
+++ b/drivers/firmware/tegra/bpmp-tegra186.c
@@ -325,6 +325,10 @@ static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)
struct tegra186_bpmp *priv;
int err;
+ err = tegra_bpmp_init_channels(bpmp);
+ if (err < 0)
+ return err;
+
priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -352,6 +356,13 @@ static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)
tegra186_bpmp_reset_channels(bpmp);
+ err = tegra_bpmp_init_ping(bpmp);
+ if (err < 0) {
+ mbox_free_channel(priv->mbox.channel);
+ tegra186_bpmp_teardown_channels(bpmp);
+ return err;
+ }
+
return 0;
}
@@ -359,6 +370,8 @@ static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp)
{
struct tegra186_bpmp *priv = bpmp->priv;
+ tegra_bpmp_deinit_ping(bpmp);
+
mbox_free_channel(priv->mbox.channel);
tegra186_bpmp_teardown_channels(bpmp);
@@ -373,6 +386,7 @@ static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp)
const struct tegra_bpmp_ops tegra186_bpmp_ops = {
.init = tegra186_bpmp_init,
+ .init_providers = tegra_bpmp_init_dt_providers,
.deinit = tegra186_bpmp_deinit,
.is_response_ready = tegra186_bpmp_is_message_ready,
.is_request_ready = tegra186_bpmp_is_message_ready,
diff --git a/drivers/firmware/tegra/bpmp-tegra210.c b/drivers/firmware/tegra/bpmp-tegra210.c
index 6295f5640c15..fdc26c66f7c2 100644
--- a/drivers/firmware/tegra/bpmp-tegra210.c
+++ b/drivers/firmware/tegra/bpmp-tegra210.c
@@ -166,6 +166,10 @@ static int tegra210_bpmp_init(struct tegra_bpmp *bpmp)
unsigned int i;
int err;
+ err = tegra_bpmp_init_channels(bpmp);
+ if (err < 0)
+ return err;
+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -215,16 +219,21 @@ static int tegra210_bpmp_init(struct tegra_bpmp *bpmp)
err = devm_request_irq(&pdev->dev, err, rx_irq,
IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+ if (err < 0)
return err;
- }
- return 0;
+ return tegra_bpmp_init_ping(bpmp);
+}
+
+static void tegra210_bpmp_deinit(struct tegra_bpmp *bpmp)
+{
+ tegra_bpmp_deinit_ping(bpmp);
}
const struct tegra_bpmp_ops tegra210_bpmp_ops = {
.init = tegra210_bpmp_init,
+ .init_providers = tegra_bpmp_init_dt_providers,
+ .deinit = tegra210_bpmp_deinit,
.is_response_ready = tegra210_bpmp_is_response_ready,
.is_request_ready = tegra210_bpmp_is_request_ready,
.ack_response = tegra210_bpmp_ack_response,
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
index 753472b53bd8..d3508e98e338 100644
--- a/drivers/firmware/tegra/bpmp.c
+++ b/drivers/firmware/tegra/bpmp.c
@@ -3,6 +3,7 @@
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
*/
+#include <linux/acpi.h>
#include <linux/clk/tegra.h>
#include <linux/genalloc.h>
#include <linux/mailbox_client.h>
@@ -11,8 +12,10 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/semaphore.h>
#include <linux/sched/clock.h>
+#include <linux/slab.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
@@ -23,6 +26,12 @@
#define MSG_ACK BIT(0)
#define MSG_RING BIT(1)
#define TAG_SZ 32
+#define TEGRA_BPMP_ACPI_BMRQ_DATA_SZ 3960U
+
+struct tegra_bpmp_acpi_message {
+ u64 status;
+ u8 *data;
+};
static inline const struct tegra_bpmp_ops *
channel_to_ops(struct tegra_bpmp_channel *channel)
@@ -343,12 +352,96 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
static int __maybe_unused tegra_bpmp_resume(struct device *dev);
+#ifdef CONFIG_ACPI
+static int tegra_bpmp_transfer_acpi(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_message *msg)
+{
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NB"), "NB" };
+ struct acpi_object_list param_list;
+ union acpi_object params[2];
+ struct tegra_bpmp_acpi_message *reply;
+ acpi_status status;
+ size_t data_len;
+ int err = 0;
+
+ params[0].type = ACPI_TYPE_INTEGER;
+ params[0].integer.value = msg->mrq;
+
+ params[1].type = ACPI_TYPE_BUFFER;
+ params[1].buffer.length = msg->tx.size;
+ params[1].buffer.pointer = (u8 *)msg->tx.data;
+
+ param_list.count = 2;
+ param_list.pointer = params;
+
+ status = acpi_evaluate_object(ACPI_HANDLE(bpmp->dev), "BMRQ",
+ &param_list, &output);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(ACPI_HANDLE(bpmp->dev), "BMRQ",
+ status);
+ return -ENODEV;
+ }
+
+ status = acpi_extract_package(output.pointer, &format, &response);
+ if (ACPI_FAILURE(status)) {
+ dev_err(bpmp->dev, "BMRQ: invalid response package: %s\n",
+ acpi_format_exception(status));
+ err = -ENODATA;
+ goto out;
+ }
+
+ if (response.length < sizeof(*reply)) {
+ dev_err(bpmp->dev, "BMRQ: response too short\n");
+ err = -ENODATA;
+ goto out;
+ }
+
+ reply = response.pointer;
+ data_len = response.length - sizeof(*reply);
+ if (data_len > TEGRA_BPMP_ACPI_BMRQ_DATA_SZ) {
+ dev_err(bpmp->dev, "BMRQ: reply buffer too large (%zu)\n",
+ data_len);
+ err = -EINVAL;
+ goto out;
+ }
+
+ msg->rx.ret = (int)reply->status;
+
+ if (msg->rx.data && msg->rx.size) {
+ if (data_len < msg->rx.size) {
+ dev_err(bpmp->dev, "BMRQ: response data too short\n");
+ err = -ENODATA;
+ goto out;
+ }
+
+ memcpy(msg->rx.data, reply->data, msg->rx.size);
+ }
+
+out:
+ kfree(response.pointer);
+ kfree(output.pointer);
+
+ return err;
+}
+#else
+static int tegra_bpmp_transfer_acpi(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_message *msg)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
struct tegra_bpmp_message *msg)
{
struct tegra_bpmp_channel *channel;
int err;
+ if (WARN_ON(ACPI_HANDLE(bpmp->dev)))
+ return -EOPNOTSUPP;
+
if (WARN_ON(!irqs_disabled()))
return -EPERM;
@@ -389,19 +482,13 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
}
EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
-int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
- struct tegra_bpmp_message *msg)
+static int tegra_bpmp_transfer_channel(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_message *msg)
{
struct tegra_bpmp_channel *channel;
unsigned long timeout;
int err;
- if (WARN_ON(irqs_disabled()))
- return -EPERM;
-
- if (!tegra_bpmp_message_valid(msg))
- return -EINVAL;
-
if (bpmp->suspended) {
/* Reset BPMP IPC channels during resume based on flags passed */
if (msg->flags & TEGRA_BPMP_MESSAGE_RESET)
@@ -428,6 +515,21 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size,
&msg->rx.ret);
}
+
+int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_message *msg)
+{
+ if (WARN_ON(irqs_disabled()))
+ return -EPERM;
+
+ if (!tegra_bpmp_message_valid(msg))
+ return -EINVAL;
+
+ if (ACPI_HANDLE(bpmp->dev))
+ return tegra_bpmp_transfer_acpi(bpmp, msg);
+
+ return tegra_bpmp_transfer_channel(bpmp, msg);
+}
EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
@@ -606,11 +708,17 @@ static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
msg.rx.data = &response;
msg.rx.size = sizeof(response);
- local_irq_save(flags);
start = ktime_get();
- err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+
+ if (ACPI_HANDLE(bpmp->dev)) {
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ } else {
+ local_irq_save(flags);
+ err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+ local_irq_restore(flags);
+ }
+
end = ktime_get();
- local_irq_restore(flags);
if (!err)
dev_dbg(bpmp->dev,
@@ -632,6 +740,9 @@ static int tegra_bpmp_get_firmware_tag_old(struct tegra_bpmp *bpmp, char *tag,
void *virt;
int err;
+ if (ACPI_HANDLE(bpmp->dev))
+ return -EOPNOTSUPP;
+
if (size != TAG_SZ)
return -EINVAL;
@@ -733,19 +844,119 @@ void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp)
spin_unlock(&bpmp->lock);
}
-static int tegra_bpmp_probe(struct platform_device *pdev)
+
+/*
+ * MBWT controls are not exposed through ordinary host MMIO and are accessed
+ * through BPMP firmware requests. These helpers provide the request wrappers
+ * used by bandwidth control callers.
+ */
+bool tegra_bpmp_mbwt_cmd_is_supported(struct tegra_bpmp *bpmp,
+ unsigned int cmd_code)
{
- struct tegra_bpmp *bpmp;
- char tag[TAG_SZ];
- size_t size;
+ struct mrq_sochub_mbwt_request request = {
+ .cmd = CMD_SOCHUB_MBWT_QUERY_ABI,
+ .query_abi.cmd_code = cmd_code,
+ };
+ struct tegra_bpmp_message msg = {
+ .mrq = MRQ_SOCHUB_MBWT,
+ .tx = {
+ .data = &request,
+ .size = sizeof(request),
+ },
+ };
int err;
- bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
- if (!bpmp)
- return -ENOMEM;
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err || msg.rx.ret)
+ return false;
- bpmp->soc = of_device_get_match_data(&pdev->dev);
- bpmp->dev = &pdev->dev;
+ return true;
+}
+
+int tegra_bpmp_mbwt_get(struct tegra_bpmp *bpmp, unsigned int instance,
+ unsigned int vc_type, unsigned int *bandwidth)
+{
+ struct mrq_sochub_mbwt_request request = {
+ .cmd = CMD_SOCHUB_MBWT_GET_BW,
+ .get_bw = {
+ .instance = instance,
+ .vc_type = vc_type,
+ },
+ };
+ struct mrq_sochub_mbwt_response response = {};
+ struct tegra_bpmp_message msg = {
+ .mrq = MRQ_SOCHUB_MBWT,
+ .tx = {
+ .data = &request,
+ .size = sizeof(request),
+ },
+ .rx = {
+ .data = &response,
+ .size = sizeof(response),
+ },
+ };
+ int err;
+
+ if (!bandwidth)
+ return -EINVAL;
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err) {
+ dev_err(bpmp->dev, "MBWT get bandwidth transfer failed: %d\n",
+ err);
+ return err;
+ }
+
+ if (msg.rx.ret) {
+ dev_err(bpmp->dev, "MBWT get bandwidth failed: BPMP error %d\n",
+ msg.rx.ret);
+ return -EIO;
+ }
+
+ *bandwidth = response.get_bw.bw;
+
+ return 0;
+}
+
+int tegra_bpmp_mbwt_set(struct tegra_bpmp *bpmp, unsigned int instance,
+ unsigned int vc_type, unsigned int bandwidth)
+{
+ struct mrq_sochub_mbwt_request request = {
+ .cmd = CMD_SOCHUB_MBWT_SET_BW,
+ .set_bw = {
+ .instance = instance,
+ .vc_type = vc_type,
+ .bw = bandwidth,
+ },
+ };
+ struct tegra_bpmp_message msg = {
+ .mrq = MRQ_SOCHUB_MBWT,
+ .tx = {
+ .data = &request,
+ .size = sizeof(request),
+ },
+ };
+ int err;
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err) {
+ dev_err(bpmp->dev, "MBWT set bandwidth transfer failed: %d\n",
+ err);
+ return err;
+ }
+
+ if (msg.rx.ret) {
+ dev_err(bpmp->dev, "MBWT set bandwidth failed: BPMP error %d\n",
+ msg.rx.ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int tegra_bpmp_init_channels(struct tegra_bpmp *bpmp)
+{
+ size_t size;
INIT_LIST_HEAD(&bpmp->mrqs);
spin_lock_init(&bpmp->lock);
@@ -755,86 +966,135 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long);
- bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ bpmp->threaded.allocated = devm_kzalloc(bpmp->dev, size, GFP_KERNEL);
if (!bpmp->threaded.allocated)
return -ENOMEM;
- bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ bpmp->threaded.busy = devm_kzalloc(bpmp->dev, size, GFP_KERNEL);
if (!bpmp->threaded.busy)
return -ENOMEM;
spin_lock_init(&bpmp->atomic_tx_lock);
- bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel),
+ bpmp->tx_channel = devm_kzalloc(bpmp->dev, sizeof(*bpmp->tx_channel),
GFP_KERNEL);
if (!bpmp->tx_channel)
return -ENOMEM;
- bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel),
- GFP_KERNEL);
+ bpmp->rx_channel = devm_kzalloc(bpmp->dev, sizeof(*bpmp->rx_channel),
+ GFP_KERNEL);
if (!bpmp->rx_channel)
return -ENOMEM;
- bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count,
+ bpmp->threaded_channels = devm_kcalloc(bpmp->dev, bpmp->threaded.count,
sizeof(*bpmp->threaded_channels),
GFP_KERNEL);
if (!bpmp->threaded_channels)
return -ENOMEM;
- platform_set_drvdata(pdev, bpmp);
+ return 0;
+}
- err = bpmp->soc->ops->init(bpmp);
- if (err < 0)
- return err;
+int tegra_bpmp_init_ping(struct tegra_bpmp *bpmp)
+{
+ int err;
err = tegra_bpmp_request_mrq(bpmp, MRQ_PING,
tegra_bpmp_mrq_handle_ping, bpmp);
if (err < 0)
- goto deinit;
+ return err;
err = tegra_bpmp_ping(bpmp);
if (err < 0) {
- dev_err(&pdev->dev, "failed to ping BPMP: %d\n", err);
+ dev_err(bpmp->dev, "failed to ping BPMP: %d\n", err);
goto free_mrq;
}
- err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag));
- if (err < 0) {
- dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
- goto free_mrq;
- }
+ return 0;
- dev_info(&pdev->dev, "firmware: %.*s\n", (int)sizeof(tag), tag);
+free_mrq:
+ tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
+
+ return err;
+}
+
+void tegra_bpmp_deinit_ping(struct tegra_bpmp *bpmp)
+{
+ tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
+}
- err = of_platform_default_populate(pdev->dev.of_node, NULL, &pdev->dev);
+int tegra_bpmp_init_dt_providers(struct tegra_bpmp *bpmp)
+{
+ struct device *dev = bpmp->dev;
+ int err;
+
+ err = of_platform_default_populate(dev->of_node, NULL, dev);
if (err < 0)
- goto free_mrq;
+ return err;
- if (of_property_present(pdev->dev.of_node, "#clock-cells")) {
+ if (of_property_present(dev->of_node, "#clock-cells")) {
err = tegra_bpmp_init_clocks(bpmp);
if (err < 0)
- goto free_mrq;
+ return err;
}
- if (of_property_present(pdev->dev.of_node, "#reset-cells")) {
+ if (of_property_present(dev->of_node, "#reset-cells")) {
err = tegra_bpmp_init_resets(bpmp);
if (err < 0)
- goto free_mrq;
+ return err;
}
- if (of_property_present(pdev->dev.of_node, "#power-domain-cells")) {
+ if (of_property_present(dev->of_node, "#power-domain-cells")) {
err = tegra_bpmp_init_powergates(bpmp);
if (err < 0)
- goto free_mrq;
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_bpmp_probe(struct platform_device *pdev)
+{
+ struct tegra_bpmp *bpmp;
+ char tag[TAG_SZ];
+ int err;
+
+ bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
+ if (!bpmp)
+ return -ENOMEM;
+
+ bpmp->soc = device_get_match_data(&pdev->dev);
+ bpmp->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, bpmp);
+
+ err = bpmp->soc->ops->init(bpmp);
+ if (err < 0)
+ return err;
+
+ err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag));
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
+ goto deinit;
+ }
+
+ dev_info(&pdev->dev, "firmware: %.*s\n", (int)sizeof(tag), tag);
+
+ if (bpmp->soc->ops->init_providers) {
+ err = bpmp->soc->ops->init_providers(bpmp);
+ if (err < 0)
+ goto deinit;
}
+ err = tegra_bpmp_init_sysfs(bpmp);
+ if (err < 0)
+ dev_err(&pdev->dev, "failed to initialize sysfs: %d\n", err);
+
err = tegra_bpmp_init_debugfs(bpmp);
if (err < 0)
dev_err(&pdev->dev, "debugfs initialization failed: %d\n", err);
return 0;
-free_mrq:
- tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
deinit:
if (bpmp->soc->ops->deinit)
bpmp->soc->ops->deinit(bpmp);
@@ -857,7 +1117,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev)
bpmp->suspended = false;
- if (bpmp->soc->ops->resume)
+ if (bpmp->soc->ops && bpmp->soc->ops->resume)
return bpmp->soc->ops->resume(bpmp);
else
return 0;
@@ -929,10 +1189,86 @@ static const struct of_device_id tegra_bpmp_match[] = {
{ }
};
+#ifdef CONFIG_ACPI
+#define TEGRA_BPMP_MBWT_VC_PCIE_READ 0
+#define TEGRA_BPMP_MBWT_VC_PCIE_WRITE 1
+#define TEGRA_BPMP_MBWT_VC_NVCLINK 2
+
+static const struct tegra_bpmp_mbwt_vc tegra_bpmp_acpi_mbwt_vcs[] = {
+ { .name = "pcie_read", .type = TEGRA_BPMP_MBWT_VC_PCIE_READ },
+ { .name = "pcie_write", .type = TEGRA_BPMP_MBWT_VC_PCIE_WRITE },
+ { .name = "nvclink", .type = TEGRA_BPMP_MBWT_VC_NVCLINK },
+};
+
+static const struct tegra_bpmp_mbwt_group tegra_bpmp_acpi_mbwt_groups[] = {
+ {
+ .name = "pcie0",
+ .id = 0,
+ .vcs = tegra_bpmp_acpi_mbwt_vcs,
+ .num_vcs = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_vcs)
+ }, {
+ .name = "pcie1",
+ .id = 1,
+ .vcs = tegra_bpmp_acpi_mbwt_vcs,
+ .num_vcs = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_vcs)
+ }, {
+ .name = "pcie2",
+ .id = 2,
+ .vcs = tegra_bpmp_acpi_mbwt_vcs,
+ .num_vcs = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_vcs)
+ }, {
+ .name = "pcie3",
+ .id = 3,
+ .vcs = tegra_bpmp_acpi_mbwt_vcs,
+ .num_vcs = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_vcs)
+ }, {
+ .name = "pcie4",
+ .id = 4,
+ .vcs = tegra_bpmp_acpi_mbwt_vcs,
+ .num_vcs = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_vcs)
+ }, {
+ .name = "pcie5",
+ .id = 5,
+ .vcs = tegra_bpmp_acpi_mbwt_vcs,
+ .num_vcs = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_vcs)
+ },
+};
+
+static const struct tegra_bpmp_mbwt_soc tegra_bpmp_acpi_mbwt_soc = {
+ .groups = tegra_bpmp_acpi_mbwt_groups,
+ .num_groups = ARRAY_SIZE(tegra_bpmp_acpi_mbwt_groups),
+};
+
+static int tegra_bpmp_acpi_init(struct tegra_bpmp *bpmp)
+{
+ return tegra_bpmp_ping(bpmp);
+}
+
+static const struct tegra_bpmp_ops tegra_bpmp_acpi_ops = {
+ .init = tegra_bpmp_acpi_init,
+};
+
+static const struct tegra_bpmp_soc tegra_bpmp_acpi_soc = {
+ .ops = &tegra_bpmp_acpi_ops,
+ .mbwt = &tegra_bpmp_acpi_mbwt_soc,
+};
+
+static const struct acpi_device_id tegra_bpmp_acpi_match[] = {
+ {
+ .id = "NVDA3001",
+ .driver_data = (kernel_ulong_t)&tegra_bpmp_acpi_soc,
+ },
+ { }
+};
+#endif
+
static struct platform_driver tegra_bpmp_driver = {
.driver = {
.name = "tegra-bpmp",
.of_match_table = tegra_bpmp_match,
+#ifdef CONFIG_ACPI
+ .acpi_match_table = tegra_bpmp_acpi_match,
+#endif
.pm = &tegra_bpmp_pm_ops,
.suppress_bind_attrs = true,
},
diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
index 5c64b3e02211..7c46748aa8e3 100644
--- a/include/soc/tegra/bpmp-abi.h
+++ b/include/soc/tegra/bpmp-abi.h
@@ -76,7 +76,7 @@
* @ingroup MRQ_Format
*
* Request an answer from the peer.
- * This should be set in mrq_request::flags for all requests targetted
+ * This should be set in mrq_request::flags for all requests targeted
* at BPMP. For requests originating in BPMP, this flag is optional except
* for messages targeting MCE, for which the field must be set.
* When this flag is not set, the remote peer must not send a response
@@ -587,12 +587,13 @@ struct mrq_response {
#define MRQ_HWPM 93U
#define MRQ_DVFS 94U
#define MRQ_PPP_PROFILE 95U
+#define MRQ_SOCHUB_MBWT 96U
/**
* @brief Maximum MRQ code to be sent by CPU software to
* BPMP. Subject to change in future
*/
-#define MAX_CPU_MRQ_ID 95U
+#define MAX_CPU_MRQ_ID 96U
/** @} */
@@ -2727,7 +2728,7 @@ struct mrq_abi_ratchet_request {
* @brief Response to #MRQ_ABI_RATCHET
*
* #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header
- * against which BPMP firwmare was compiled.
+ * against which BPMP firmware was compiled.
*
* If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE,
* the requster must either interoperate with BPMP according to an ABI
@@ -6686,6 +6687,164 @@ struct mrq_ppp_profile_response {
/** @} PPP */
/** @endcond */
+/** @cond (bpmp_tb500)
+ * @ingroup MRQ_Codes
+ * @def MRQ_SOCHUB_MBWT
+ * @brief Configure per-virtual-channel bandwidth caps for a bandwidth group using
+ * Memory Bandwidth Throttler (MBWT).
+ *
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_sochub_mbwt_request
+ * * Response Payload: @ref mrq_sochub_mbwt_response
+ *
+ * @addtogroup SOCHUB_MBWT
+ * @{
+ */
+
+/**
+ * @brief Sub-command identifiers for #MRQ_SOCHUB_MBWT.
+ */
+enum mrq_sochub_mbwt_cmd {
+ /**
+ * @brief Check whether the BPMP-FW supports the specified
+ * #MRQ_SOCHUB_MBWT sub-command.
+ *
+ * mrq_response::err is 0 if the specified request is
+ * supported and -#BPMP_ENODEV otherwise.
+ */
+ CMD_SOCHUB_MBWT_QUERY_ABI = 0,
+ /**
+ * @brief Get bandwidth cap for a virtual channel
+ *
+ * mrq_response::err is defined as:
+ *
+ * | Value | Description |
+ * |----------------|------------------------------------------------|
+ * | 0 | Success |
+ * | -#BPMP_ENOTSUP | #MRQ_SOCHUB_MBWT is not supported by BPMP-FW. |
+ * | -#BPMP_EINVAL | Invalid request parameters. |
+ * | -#BPMP_EIO | Failed to retrieve the bandwidth. |
+ */
+ CMD_SOCHUB_MBWT_GET_BW = 1,
+ /**
+ * @brief Set bandwidth cap for a virtual channel.
+ *
+ * mrq_response::err is defined as:
+ *
+ * | Value | Description |
+ * |----------------|------------------------------------------------|
+ * | 0 | Success |
+ * | -#BPMP_ENOTSUP | #MRQ_SOCHUB_MBWT is not supported by BPMP-FW. |
+ * | -#BPMP_EINVAL | Invalid request parameters. |
+ * | -#BPMP_EIO | Failed to set the bandwidth. |
+ */
+ CMD_SOCHUB_MBWT_SET_BW = 2,
+};
+
+/**
+ * @brief Request data for #MRQ_SOCHUB_MBWT sub-command
+ * #CMD_SOCHUB_MBWT_QUERY_ABI
+ */
+struct cmd_sochub_mbwt_query_abi_req {
+ /** @brief Sub-command identifier from @ref mrq_sochub_mbwt_cmd */
+ uint32_t cmd_code;
+} BPMP_ABI_PACKED;
+
+/**
+ * @brief Request data for #MRQ_SOCHUB_MBWT sub-command
+ * #CMD_SOCHUB_MBWT_GET_BW
+ */
+struct cmd_sochub_mbwt_get_bw_req {
+ /**
+ * @brief Instance ID for the bandwidth group
+ *
+ * Valid range is [0, 5]
+ */
+ uint32_t instance;
+ /**
+ * @brief Type of the virtual channel/traffic
+ *
+ * Valid values:
+ *
+ * * Value 0: PCIe read
+ * * Value 1: PCIe write
+ * * Value 2: GPU connected over the chip-to-chip link (NVCLINK)
+ */
+ uint32_t vc_type;
+} BPMP_ABI_PACKED;
+
+/**
+ * @brief Request data for #MRQ_SOCHUB_MBWT sub-command
+ * #CMD_SOCHUB_MBWT_SET_BW
+ */
+struct cmd_sochub_mbwt_set_bw_req {
+ /**
+ * @brief Instance ID for the bandwidth group
+ *
+ * Valid range is [0, 5]
+ */
+ uint32_t instance;
+ /**
+ * @brief Type of the virtual channel/traffic
+ *
+ * Valid values:
+ *
+ * * Value 0: PCIe read
+ * * Value 1: PCIe write
+ * * Value 2: GPU connected over the chip-to-chip link (NVCLINK)
+ */
+ uint32_t vc_type;
+ /** @brief Bandwidth cap in GB/s */
+ uint32_t bw;
+} BPMP_ABI_PACKED;
+
+/**
+ * @brief Response data for #MRQ_SOCHUB_MBWT sub-command
+ * #CMD_SOCHUB_MBWT_GET_BW
+ */
+struct cmd_sochub_mbwt_get_bw_resp {
+ /** @brief Bandwidth cap in GB/s */
+ uint32_t bw;
+} BPMP_ABI_PACKED;
+
+/**
+ * @brief Request payload for the #MRQ_SOCHUB_MBWT -command
+ *
+ * | Sub-command | Request payload |
+ * |-------------------------------|----------------------------------------|
+ * | #CMD_SOCHUB_MBWT_QUERY_ABI | #cmd_sochub_mbwt_query_abi_req |
+ * | #CMD_SOCHUB_MBWT_GET_BW | #cmd_sochub_mbwt_get_bw_req |
+ * | #CMD_SOCHUB_MBWT_SET_BW | #cmd_sochub_mbwt_set_bw_req |
+ */
+struct mrq_sochub_mbwt_request {
+ /** @brief Sub-command ID from @ref mrq_sochub_mbwt_cmd. */
+ uint32_t cmd;
+ union {
+ struct cmd_sochub_mbwt_query_abi_req query_abi;
+ struct cmd_sochub_mbwt_get_bw_req get_bw;
+ struct cmd_sochub_mbwt_set_bw_req set_bw;
+ } BPMP_UNION_ANON;
+} BPMP_ABI_PACKED;
+
+/**
+ * @brief Response payload for the #MRQ_SOCHUB_MBWT -command.
+ *
+ * | Sub-command | Response payload |
+ * |-------------------------------|----------------------------------------|
+ * | #CMD_SOCHUB_MBWT_QUERY_ABI | - |
+ * | #CMD_SOCHUB_MBWT_GET_BW | #cmd_sochub_mbwt_get_bw_resp |
+ * | #CMD_SOCHUB_MBWT_SET_BW | - |
+ */
+struct mrq_sochub_mbwt_response {
+ union {
+ struct cmd_sochub_mbwt_get_bw_resp get_bw;
+ } BPMP_UNION_ANON;
+} BPMP_ABI_PACKED;
+
+/** @} SOCHUB_MBWT */
+/** @endcond */
+
/**
* @addtogroup Error_Codes
* Negative values for mrq_response::err generally indicate some
diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h
index a33582590a3b..f55db918ccb1 100644
--- a/include/soc/tegra/bpmp.h
+++ b/include/soc/tegra/bpmp.h
@@ -16,6 +16,7 @@
#include <soc/tegra/bpmp-abi.h>
struct tegra_bpmp_clk;
+struct tegra_bpmp_mbwt_soc;
struct tegra_bpmp_ops;
struct tegra_bpmp_soc {
@@ -28,6 +29,7 @@ struct tegra_bpmp_soc {
} channels;
const struct tegra_bpmp_ops *ops;
+ const struct tegra_bpmp_mbwt_soc *mbwt;
unsigned int num_resets;
};