diff options
| author | Thierry Reding <treding@nvidia.com> | 2026-10-03 11:47:59 +0200 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2026-10-03 11:47:59 +0200 |
| commit | 436ae3f3ebe95cd8ee71e35fd3cc91f9fc88aae1 (patch) | |
| tree | 74f727ee172fb8a928b3f6d0fff0332676830ed8 | |
| parent | af351a8392e5e97366c764f5de80d17145677716 (diff) | |
| parent | 1bbcf4a19f1495f769b69e9569d8c1e81df074f5 (diff) | |
| download | linux-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-bpmp | 48 | ||||
| -rw-r--r-- | drivers/firmware/tegra/Makefile | 1 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-debugfs.c | 9 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-private.h | 39 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-sysfs.c | 207 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-tegra186.c | 14 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-tegra210.c | 17 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp.c | 434 | ||||
| -rw-r--r-- | include/soc/tegra/bpmp-abi.h | 165 | ||||
| -rw-r--r-- | include/soc/tegra/bpmp.h | 2 |
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", + ¶m_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; }; |
