diff --git a/Documentation/ABI/testing/sys-platform-tegra-bpmp b/Documentation/ABI/testing/sys-platform-tegra-bpmp new file mode 100644 index 0000000000000..0be09cd8d00f3 --- /dev/null +++ b/Documentation/ABI/testing/sys-platform-tegra-bpmp @@ -0,0 +1,51 @@ +What: /sys/bus/platform/devices//mbwt_control/pcie_instance_id +What: /sys/bus/platform/devices//mbwt_control/vc_type +What: /sys/bus/platform/devices//mbwt_control/bandwidth +Date: March 2026 +KernelVersion: 6.18 +Contact: Aniruddha TVS Rao +Description: + On ACPI-based Tegra systems the BPMP driver does not use the + device-tree mailbox path; firmware interaction is via AML. This sysfs + interface is a userspace tuning knob for memory bandwidth throttler + (MBWT) settings. + + On Tegra410, the PCIe bandwidth control path exposes QoS that caps + aggregate bandwidth for PCIe and for GPU traffic over UPHY. Each PCIe + bandwidth group has a single shared cap for all traffic in that group. + A group may contain only PCIe devices, only a GPU on UPHY, or PCIe and + GPU together in a bifurcated topology. + + Following attributes appear under a kobject named mbwt_control on the + tegra-bpmp platform device (the same struct device as the driver + binds to), only when that device has an ACPI companion and BPMP + firmware reports support for both MBWT_GET and MBWT_SET via the MBWT + QUERY probe. + + pcie_instance_id (RW): + PCIe bandwidth group index: 0 = pcie0, 1 = pcie1, ..., 5 = pcie5. + + vc_type (RW): + Traffic type to cap for that group: + 0 = PCIe read + 1 = PCIe write + 2 = GPU UPHY read plus write (combined) + + bandwidth (RW): + Target bandwidth cap in GB/s for the pcie_instance_id and vc_type + currently stored in the other two attributes. Intended range is + 1-110 GB/s; firmware validates the request (via MRQ MBWT_SET). + + When read, issues a MBWT_GET for that same pcie_instance_id and vc_type + and returns one line of three decimal fields separated by ASCII + space: PCIe instance ID, vc_type, and the bandwidth value in GB/s + returned by firmware. + + Example: + echo 0 > .../mbwt_control/pcie_instance_id + echo 1 > .../mbwt_control/vc_type + echo 100 > .../mbwt_control/bandwidth + cat .../mbwt_control/bandwidth + +Users: Customer tuning of PCIe and GPU UPHY bandwidth caps on + ACPI-based Tegra410 systems. diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 41e2e4dc31d63..6c577e59b3ee7 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile @@ -7,4 +7,5 @@ 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 obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o +obj-$(CONFIG_TEGRA_BPMP) += bpmp-tegra-sysfs.o obj-$(CONFIG_TEGRA_IVC) += ivc.o diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c index 4221fed70ad48..9898aa9396c56 100644 --- a/drivers/firmware/tegra/bpmp-debugfs.c +++ b/drivers/firmware/tegra/bpmp-debugfs.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. */ +#include #include #include #include @@ -771,6 +772,8 @@ static int bpmp_populate_debugfs_shmem(struct tegra_bpmp *bpmp) int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) { + const char *root_name = "bpmp"; + char *acpi_root_name = NULL; struct dentry *root; bool inband; int err; @@ -780,13 +783,23 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS)) return 0; - root = debugfs_create_dir("bpmp", NULL); + if (ACPI_HANDLE(bpmp->dev)) { + acpi_root_name = kasprintf(GFP_KERNEL, "bpmp-%s", + dev_name(bpmp->dev)); + if (!acpi_root_name) + return -ENOMEM; + + root_name = acpi_root_name; + } + + root = debugfs_create_dir(root_name, NULL); + kfree(acpi_root_name); if (IS_ERR(root)) - return -ENOMEM; + return PTR_ERR(root); bpmp->debugfs_mirror = debugfs_create_dir("debug", root); if (IS_ERR(bpmp->debugfs_mirror)) { - err = -ENOMEM; + err = PTR_ERR(bpmp->debugfs_mirror); goto out; } diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h index 07c3d46abb874..6cb5b8b544561 100644 --- a/drivers/firmware/tegra/bpmp-private.h +++ b/drivers/firmware/tegra/bpmp-private.h @@ -26,4 +26,12 @@ struct tegra_bpmp_ops { extern const struct tegra_bpmp_ops tegra186_bpmp_ops; extern const struct tegra_bpmp_ops tegra210_bpmp_ops; +#define TEGRA_BPMP_ACPI_BMRQ_DATA_SZ 3960U + +struct tegra_bpmp_acpi_message { + u64 status; + u8 *data_ptr; + u8 data[TEGRA_BPMP_ACPI_BMRQ_DATA_SZ]; +}; + #endif diff --git a/drivers/firmware/tegra/bpmp-tegra-sysfs.c b/drivers/firmware/tegra/bpmp-tegra-sysfs.c new file mode 100644 index 0000000000000..4e5203d04ad31 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra-sysfs.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026, NVIDIA CORPORATION. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bpmp-private.h" + +struct tegra_bpmp_mbwt_sysfs { + struct kobject kobj; + /* Serializes pcie_instance_id and vc_type stores and bandwidth I/O. */ + struct mutex lock; + struct tegra_bpmp *bpmp; + unsigned int pcie_instance_id; + unsigned int vc_type; +}; + +#define to_mbwt_sysfs(k) container_of((k), struct tegra_bpmp_mbwt_sysfs, kobj) + +static void tegra_bpmp_mbwt_kobj_release(struct kobject *kobj) +{ + /* Lifetime is managed by devm; no dynamic allocation here. */ +} + +static const struct kobj_type tegra_bpmp_mbwt_ktype = { + .release = tegra_bpmp_mbwt_kobj_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +/** + * tegra_sochub_mbwt_query_abi() - Ask BPMP whether an MBWT sub-command is supported. + * @bpmp: BPMP handle + * @cmd_code: Sub-command to probe (e.g. CMD_SOCHUB_MBWT_SET_BW) + * + * Returns 0 if the firmware reports the sub-command is supported (MRQ error 0). + * Returns a negative errno if the transfer fails, or %-EOPNOTSUPP if the + * firmware reports the sub-command is not supported. + */ +static int tegra_sochub_mbwt_query_abi(struct tegra_bpmp *bpmp, + unsigned int cmd_code) +{ + struct mrq_sochub_mbwt_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_SOCHUB_MBWT_QUERY_ABI; + request.query_abi.cmd_code = cmd_code; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_SOCHUB_MBWT; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err) + return err; + + if (msg.rx.ret) + return -EOPNOTSUPP; + + return 0; +} + +static int tegra_sochub_set_mbwt(struct tegra_bpmp *bpmp, + unsigned int instance, + unsigned int vc_type, + unsigned int bandwidth) +{ + struct mrq_sochub_mbwt_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_SOCHUB_MBWT_SET_BW; + request.set_bw.instance = instance; + request.set_bw.vc_type = vc_type; + request.set_bw.bw = bandwidth; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_SOCHUB_MBWT; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + + err = tegra_bpmp_transfer(bpmp, &msg); + + if (err) + dev_err(bpmp->dev, + "Failed setting up the SocHub MBWT with error %d\n", + err); + return err; +} + +static int tegra_sochub_get_mbwt(struct tegra_bpmp *bpmp, + unsigned int instance, + unsigned int vc_type, + unsigned int *bandwidth_out) +{ + struct mrq_sochub_mbwt_request request; + struct mrq_sochub_mbwt_response response; + struct cmd_sochub_mbwt_get_bw_resp mbwt; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_SOCHUB_MBWT_GET_BW; + request.get_bw.instance = instance; + request.get_bw.vc_type = vc_type; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_SOCHUB_MBWT; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err) { + dev_err(bpmp->dev, + "Failed reading the SocHub MBWT with error %d\n", + err); + return err; + } + if (msg.rx.ret < 0) + return -EINVAL; + + memcpy(&mbwt, &response.get_bw, sizeof(response.get_bw)); + if (bandwidth_out) + *bandwidth_out = mbwt.bw; + + return 0; +} + +static ssize_t pcie_instance_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = to_mbwt_sysfs(kobj); + unsigned int id; + + mutex_lock(&mbwt->lock); + id = mbwt->pcie_instance_id; + mutex_unlock(&mbwt->lock); + + return sysfs_emit(buf, "%u\n", id); +} + +static ssize_t pcie_instance_id_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = to_mbwt_sysfs(kobj); + unsigned int val; + int err; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + mutex_lock(&mbwt->lock); + mbwt->pcie_instance_id = val; + mutex_unlock(&mbwt->lock); + + return count; +} + +static ssize_t vc_type_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = to_mbwt_sysfs(kobj); + unsigned int vt; + + mutex_lock(&mbwt->lock); + vt = mbwt->vc_type; + mutex_unlock(&mbwt->lock); + + return sysfs_emit(buf, "%u\n", vt); +} + +static ssize_t vc_type_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = to_mbwt_sysfs(kobj); + unsigned int val; + int err; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + mutex_lock(&mbwt->lock); + mbwt->vc_type = val; + mutex_unlock(&mbwt->lock); + + return count; +} + +static ssize_t bandwidth_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = to_mbwt_sysfs(kobj); + unsigned int inst, vt, bw; + int err; + + mutex_lock(&mbwt->lock); + inst = mbwt->pcie_instance_id; + vt = mbwt->vc_type; + mutex_unlock(&mbwt->lock); + + err = tegra_sochub_get_mbwt(mbwt->bpmp, inst, vt, &bw); + if (err) + return err; + + return sysfs_emit(buf, "%u %u %u\n", inst, vt, bw); +} + +static ssize_t bandwidth_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = to_mbwt_sysfs(kobj); + unsigned int bw; + unsigned int inst, vt; + int err; + + err = kstrtou32(buf, 0, &bw); + if (err) + return err; + + mutex_lock(&mbwt->lock); + inst = mbwt->pcie_instance_id; + vt = mbwt->vc_type; + mutex_unlock(&mbwt->lock); + + err = tegra_sochub_set_mbwt(mbwt->bpmp, inst, vt, bw); + if (err) + return err; + + return count; +} + +static struct kobj_attribute pcie_instance_id_attr = + __ATTR(pcie_instance_id, 0644, pcie_instance_id_show, pcie_instance_id_store); +static struct kobj_attribute vc_type_attr = + __ATTR(vc_type, 0644, vc_type_show, vc_type_store); +static struct kobj_attribute bandwidth_attr = + __ATTR(bandwidth, 0644, bandwidth_show, bandwidth_store); + +static struct attribute *mbwt_attrs[] = { + &pcie_instance_id_attr.attr, + &vc_type_attr.attr, + &bandwidth_attr.attr, + NULL, +}; + +static const struct attribute_group mbwt_attr_group = { + .attrs = mbwt_attrs, +}; + +static void tegra_bpmp_mbwt_sysfs_teardown(void *data) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt = data; + + sysfs_remove_group(&mbwt->kobj, &mbwt_attr_group); + kobject_del(&mbwt->kobj); + kobject_put(&mbwt->kobj); +} + +int tegra_bpmp_sysfs_register(struct tegra_bpmp *bpmp) +{ + struct tegra_bpmp_mbwt_sysfs *mbwt; + int err; + + if (!ACPI_HANDLE(bpmp->dev)) + return 0; + + err = tegra_sochub_mbwt_query_abi(bpmp, CMD_SOCHUB_MBWT_SET_BW); + if (err) + return 0; + + err = tegra_sochub_mbwt_query_abi(bpmp, CMD_SOCHUB_MBWT_GET_BW); + if (err) + return 0; + + mbwt = devm_kzalloc(bpmp->dev, sizeof(*mbwt), GFP_KERNEL); + if (!mbwt) + return -ENOMEM; + + mbwt->bpmp = bpmp; + mutex_init(&mbwt->lock); + + kobject_init(&mbwt->kobj, &tegra_bpmp_mbwt_ktype); + err = kobject_add(&mbwt->kobj, &bpmp->dev->kobj, "mbwt_control"); + if (err) { + kobject_put(&mbwt->kobj); + return err; + } + + err = sysfs_create_group(&mbwt->kobj, &mbwt_attr_group); + if (err) + goto err_put; + + err = devm_add_action(bpmp->dev, tegra_bpmp_mbwt_sysfs_teardown, mbwt); + if (err) { + sysfs_remove_group(&mbwt->kobj, &mbwt_attr_group); + goto err_put; + } + + return 0; + +err_put: + kobject_del(&mbwt->kobj); + kobject_put(&mbwt->kobj); + return err; +} diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index e74bba7ccc443..eb07a45db4849 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 #include #include #include @@ -309,12 +310,93 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, static int __maybe_unused tegra_bpmp_resume(struct device *dev); +static int tegra_bpmp_transfer_acpi(struct tegra_bpmp *bpmp, + struct tegra_bpmp_message *msg) +{ + acpi_status status; + union acpi_object params[2]; + struct acpi_object_list param_list; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + struct tegra_bpmp_acpi_message pkg; + struct acpi_buffer format = { sizeof("NB"), "NB" }; + struct acpi_buffer extract; + u32 i; + size_t rbuf_len, rdata_len; + + if (!tegra_bpmp_message_valid(msg)) + return -EINVAL; + + 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; + } + + obj = (union acpi_object *)output.pointer; + /* Validate returned type */ + if (!obj || obj->type != ACPI_TYPE_PACKAGE) { + dev_err(bpmp->dev, "Invalid BMRQ data\n"); + kfree(output.pointer); + return -ENODATA; + } + + if (obj->package.count < 2 || + obj->package.elements[1].type != ACPI_TYPE_BUFFER) { + dev_err(bpmp->dev, "Invalid BMRQ data\n"); + kfree(output.pointer); + return -ENODATA; + } + + rdata_len = obj->package.elements[1].buffer.length; + rbuf_len = sizeof(u64) + sizeof(u8 *) + rdata_len; + if (rbuf_len > sizeof(pkg)) { + dev_err(bpmp->dev, "BMRQ: reply buffer too large (%zu)\n", rbuf_len); + kfree(output.pointer); + return -EINVAL; + } + + extract.length = rbuf_len; + extract.pointer = &pkg; + + status = acpi_extract_package(obj, &format, &extract); + if (ACPI_FAILURE(status)) { + dev_err(bpmp->dev, "BMRQ: failed to parse package (%s)\n", + acpi_format_exception(status)); + kfree(output.pointer); + return -EINVAL; + } + + msg->rx.ret = (int)pkg.status; + if (msg->rx.data && msg->rx.size) + memcpy(msg->rx.data, pkg.data, msg->rx.size); + + /* Free memory allocated by ACPI core */ + kfree(output.pointer); + return 0; +} + 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; @@ -355,8 +437,8 @@ 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; @@ -394,6 +476,15 @@ 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 (ACPI_HANDLE(bpmp->dev)) + return tegra_bpmp_transfer_acpi(bpmp, msg); + else + 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, @@ -472,6 +563,9 @@ int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, struct tegra_bpmp_mrq *entry; unsigned long flags; + if (ACPI_HANDLE(bpmp->dev)) + return -EOPNOTSUPP; + if (!handler) return -EINVAL; @@ -497,6 +591,9 @@ void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data) struct tegra_bpmp_mrq *entry; unsigned long flags; + if (ACPI_HANDLE(bpmp->dev)) + return; + spin_lock_irqsave(&bpmp->lock, flags); entry = tegra_bpmp_find_mrq(bpmp, mrq); @@ -572,11 +669,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); - end = ktime_get(); - local_irq_restore(flags); + if (ACPI_HANDLE(bpmp->dev)) { + start = ktime_get(); + err = tegra_bpmp_transfer_acpi(bpmp, &msg); + end = ktime_get(); + } else { + local_irq_save(flags); + start = ktime_get(); + err = tegra_bpmp_transfer_atomic(bpmp, &msg); + end = ktime_get(); + local_irq_restore(flags); + } if (!err) dev_dbg(bpmp->dev, @@ -614,10 +717,13 @@ static int tegra_bpmp_get_firmware_tag_old(struct tegra_bpmp *bpmp, char *tag, msg.tx.data = &request; msg.tx.size = sizeof(request); - local_irq_save(flags); - err = tegra_bpmp_transfer_atomic(bpmp, &msg); - local_irq_restore(flags); - + if (ACPI_HANDLE(bpmp->dev)) { + err = tegra_bpmp_transfer_acpi(bpmp, &msg); + } else { + local_irq_save(flags); + err = tegra_bpmp_transfer_atomic(bpmp, &msg); + local_irq_restore(flags); + } if (err == 0) memcpy(tag, virt, TAG_SZ); @@ -699,19 +805,12 @@ void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) spin_unlock(&bpmp->lock); } -static int tegra_bpmp_probe(struct platform_device *pdev) +static int tegra_bpmp_init_channels(struct tegra_bpmp *bpmp) { - struct tegra_bpmp *bpmp; - char tag[TAG_SZ]; size_t size; - int err; - bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL); - if (!bpmp) - return -ENOMEM; - - bpmp->soc = of_device_get_match_data(&pdev->dev); - bpmp->dev = &pdev->dev; + if (ACPI_HANDLE(bpmp->dev)) + return 0; INIT_LIST_HEAD(&bpmp->mrqs); spin_lock_init(&bpmp->lock); @@ -721,41 +820,94 @@ 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), + 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; +} + +static int tegra_bpmp_init_resources(struct tegra_bpmp *bpmp) +{ + int err; + + if (!bpmp->dev->of_node) + return 0; - err = bpmp->soc->ops->init(bpmp); + err = of_platform_default_populate(bpmp->dev->of_node, NULL, bpmp->dev); if (err < 0) return err; - err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, - tegra_bpmp_mrq_handle_ping, bpmp); + if (of_property_present(bpmp->dev->of_node, "#clock-cells")) { + err = tegra_bpmp_init_clocks(bpmp); + if (err < 0) + return err; + } + + if (of_property_present(bpmp->dev->of_node, "#reset-cells")) { + err = tegra_bpmp_init_resets(bpmp); + if (err < 0) + return err; + } + + if (of_property_present(bpmp->dev->of_node, "#power-domain-cells")) + err = tegra_bpmp_init_powergates(bpmp); + + return err; +} + +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; + + err = tegra_bpmp_init_channels(bpmp); if (err < 0) - goto deinit; + return err; + + platform_set_drvdata(pdev, bpmp); + + if (!ACPI_HANDLE(bpmp->dev) && bpmp->soc->ops->init) { + err = bpmp->soc->ops->init(bpmp); + if (err < 0) + return err; + } + + if (!ACPI_HANDLE(bpmp->dev)) { + err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, + tegra_bpmp_mrq_handle_ping, bpmp); + if (err < 0) + goto deinit; + } err = tegra_bpmp_ping(bpmp); if (err < 0) { @@ -771,27 +923,15 @@ static int tegra_bpmp_probe(struct platform_device *pdev) dev_info(&pdev->dev, "firmware: %.*s\n", (int)sizeof(tag), tag); - err = of_platform_default_populate(pdev->dev.of_node, NULL, &pdev->dev); + err = tegra_bpmp_init_resources(bpmp); if (err < 0) goto free_mrq; - if (of_property_present(pdev->dev.of_node, "#clock-cells")) { - err = tegra_bpmp_init_clocks(bpmp); - if (err < 0) - goto free_mrq; - } - - if (of_property_present(pdev->dev.of_node, "#reset-cells")) { - err = tegra_bpmp_init_resets(bpmp); - if (err < 0) - goto free_mrq; - } - - if (of_property_present(pdev->dev.of_node, "#power-domain-cells")) { - err = tegra_bpmp_init_powergates(bpmp); - if (err < 0) - goto free_mrq; - } + err = tegra_bpmp_sysfs_register(bpmp); + if (err < 0) + dev_err(&pdev->dev, + "Failed registering sysfs attribute to the BPMP platform device: %d\n", + err); err = tegra_bpmp_init_debugfs(bpmp); if (err < 0) @@ -895,10 +1035,16 @@ static const struct of_device_id tegra_bpmp_match[] = { { } }; +static const struct acpi_device_id tegra_bpmp_acpi_match[] = { + {.id = "NVDA3001", .driver_data = 0}, + { } +}; + static struct platform_driver tegra_bpmp_driver = { .driver = { .name = "tegra-bpmp", .of_match_table = tegra_bpmp_match, + .acpi_match_table = tegra_bpmp_acpi_match, .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 dc0789c20333a..0b8d51556f124 100644 --- a/include/soc/tegra/bpmp-abi.h +++ b/include/soc/tegra/bpmp-abi.h @@ -341,6 +341,7 @@ struct mrq_response { #define MRQ_GEARS 82U #define MRQ_BWMGR_INT 83U #define MRQ_OC_STATUS 84U +#define MRQ_SOCHUB_MBWT 96U /** @cond DEPRECATED */ #define MRQ_RESERVED_2 2U @@ -374,7 +375,7 @@ struct mrq_response { * @brief Maximum MRQ code to be sent by CPU software to * BPMP. Subject to change in future */ -#define MAX_CPU_MRQ_ID 84U +#define MAX_CPU_MRQ_ID 96U /** * @addtogroup MRQ_Payloads @@ -3911,6 +3912,67 @@ struct mrq_gears_response { /** @} Gears */ /** @endcond bpmp_th500 */ +/** @cond (bpmp_tb500) + * @ingroup MRQ_Codes + * @def MRQ_SOCHUB_MBWT + * @brief Configure per-virtual-channel bandwidth caps for a SoC Hub instance 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 { + CMD_SOCHUB_MBWT_QUERY_ABI = 0, + CMD_SOCHUB_MBWT_GET_BW = 1, + CMD_SOCHUB_MBWT_SET_BW = 2, +}; + +struct cmd_sochub_mbwt_query_abi_req { + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +struct cmd_sochub_mbwt_get_bw_req { + uint32_t instance; + uint32_t vc_type; +} BPMP_ABI_PACKED; + +struct cmd_sochub_mbwt_set_bw_req { + uint32_t instance; + uint32_t vc_type; + uint32_t bw; +} BPMP_ABI_PACKED; + +struct cmd_sochub_mbwt_get_bw_resp { + uint32_t bw; +} BPMP_ABI_PACKED; + +struct mrq_sochub_mbwt_request { + 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; + +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 f5e4ac5b8cce8..bae8ad985cbcb 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -140,6 +140,7 @@ int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data); bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq); +int tegra_bpmp_sysfs_register(struct tegra_bpmp *bpmp); #else static inline struct tegra_bpmp *tegra_bpmp_get(struct device *dev) { @@ -181,6 +182,12 @@ static inline bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, { return false; } + +static inline int tegra_bpmp_sysfs_register(struct tegra_bpmp *bpmp) +{ + return -EOPNOTSUPP; +} + #endif void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp);