NVIDIA: SAUCE: firmware: bpmp: build bpmp hv driver as built-in driver

skip building tegra_bpmp driver for oot kernel from
nvidia-oot repo and start building with oot kernel
as inbuild driver to improve boot KPI.

http://nvbugs/4551265

Signed-off-by: Manish Bhardwaj <mbhardwaj@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Sumit Gupta <sumitg@nvidia.com>
Signed-off-by: Vishwaroop A <va@nvidia.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Manish Bhardwaj
2024-08-22 19:42:26 +00:00
committed by Noah Wager
parent b1aca4ebc0
commit 72ac5288b5
4 changed files with 311 additions and 10 deletions
+2
View File
@@ -1,9 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
tegra-bpmp-y = bpmp.o
tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o
tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o
tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o
tegra-bpmp-$(CONFIG_ARCH_TEGRA_234_SOC) += bpmp-tegra186.o
tegra-bpmp-$(CONFIG_TEGRA_HV_DRIVER) += bpmp-tegra194-hv.o
tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o
obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o
obj-$(CONFIG_TEGRA_IVC) += ivc.o
+5 -5
View File
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018, NVIDIA CORPORATION.
*/
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright (c) 2018-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#ifndef __FIRMWARE_TEGRA_BPMP_PRIVATE_H
#define __FIRMWARE_TEGRA_BPMP_PRIVATE_H
@@ -31,5 +29,7 @@ extern const struct tegra_bpmp_ops tegra186_bpmp_ops;
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
extern const struct tegra_bpmp_ops tegra210_bpmp_ops;
#endif
#if IS_ENABLED(CONFIG_TEGRA_HV_DRIVER)
extern const struct tegra_bpmp_ops tegra194_bpmp_hv_ops;
#endif
#endif
+267
View File
@@ -0,0 +1,267 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/ivc.h>
#include <soc/tegra/virt/hv-ivc.h>
#include "bpmp-private.h"
/* utilizing the struct tegra_ivc *ivc in struct tegra_bpmp_channel
* to store struct tegra_hv_ivc_cookie pointer, being used by this driver
* to use hypervisor exposed ivc queue implementation, so that we do not
* need to introduced new member in struct tegra_bpmp_channel which
* will be unused in upstream kernel
*/
#define to_hv_ivc(ivc) ((struct tegra_hv_ivc_cookie *)ivc)
static struct tegra_hv_ivc_cookie **hv_bpmp_ivc_cookies;
static struct device_node *hv_of_node;
static irqreturn_t tegra194_hv_bpmp_rx_handler(int irq, void *bpmp)
{
tegra_bpmp_handle_rx(bpmp);
return IRQ_HANDLED;
}
static int tegra194_hv_bpmp_channel_init(struct tegra_bpmp_channel *channel,
struct tegra_bpmp *bpmp,
unsigned int queue_id, bool threaded)
{
static int cookie_idx;
struct tegra_hv_ivc_cookie *hv_ivc;
int err;
hv_ivc = tegra_hv_ivc_reserve(hv_of_node, queue_id, NULL);
channel->ivc = (void *)hv_ivc;
if (IS_ERR_OR_NULL(hv_ivc)) {
pr_err("%s: Failed to reserve ivc queue @index %d\n",
__func__, queue_id);
goto request_cleanup;
}
if (hv_ivc->frame_size < MSG_DATA_MIN_SZ) {
pr_err("%s: Frame size is too small\n", __func__);
goto request_cleanup;
}
hv_bpmp_ivc_cookies[cookie_idx++] = hv_ivc;
/* init completion */
init_completion(&channel->completion);
channel->bpmp = bpmp;
if (threaded) {
err = request_threaded_irq(
hv_ivc->irq,
tegra194_hv_bpmp_rx_handler, NULL,
IRQF_NO_SUSPEND,
"bpmp_irq_handler", bpmp);
} else {
err = 0;
}
if (err) {
pr_err("%s: Failed to request irq %d for index %d\n",
__func__, hv_ivc->irq, queue_id);
goto request_cleanup;
}
return 0;
request_cleanup:
return -1;
}
static bool tegra194_bpmp_hv_is_message_ready(struct tegra_bpmp_channel *channel)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(channel->ivc);
void *frame;
frame = tegra_hv_ivc_read_get_next_frame(hv_ivc);
if (IS_ERR(frame)) {
iosys_map_clear(&channel->ib);
return false;
}
iosys_map_set_vaddr(&channel->ib, frame);
return true;
}
static int tegra194_bpmp_hv_ack_message(struct tegra_bpmp_channel *channel)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(channel->ivc);
return tegra_hv_ivc_read_advance(hv_ivc);
}
static bool tegra194_hv_bpmp_is_channel_free(struct tegra_bpmp_channel *channel)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(channel->ivc);
void *frame;
frame = tegra_hv_ivc_write_get_next_frame(hv_ivc);
if (IS_ERR(frame)) {
iosys_map_clear(&channel->ob);
return false;
}
iosys_map_set_vaddr(&channel->ob, frame);
return true;
}
static int tegra194_hv_bpmp_post_message(struct tegra_bpmp_channel *channel)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(channel->ivc);
return tegra_hv_ivc_write_advance(hv_ivc);
}
static void tegra194_hv_bpmp_channel_reset(struct tegra_bpmp_channel *channel)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(channel->ivc);
/* reset the channel state */
tegra_hv_ivc_channel_reset(hv_ivc);
/* sync the channel state with BPMP */
while (tegra_hv_ivc_channel_notified(hv_ivc));
}
static int tegra194_hv_bpmp_resume(struct tegra_bpmp *bpmp)
{
unsigned int i;
/* reset message channels */
tegra194_hv_bpmp_channel_reset(bpmp->tx_channel);
for (i = 0; i < bpmp->threaded.count; i++)
tegra194_hv_bpmp_channel_reset(&bpmp->threaded_channels[i]);
return 0;
}
static int tegra194_hv_ivc_notify(struct tegra_bpmp *bpmp)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(bpmp->tx_channel->ivc);
tegra_hv_ivc_notify(hv_ivc);
return 0;
}
static int tegra194_hv_bpmp_init(struct tegra_bpmp *bpmp)
{
struct device_node *of_node = bpmp->dev->of_node;
int err, index;
uint32_t first_ivc_queue, num_ivc_queues;
/* get starting ivc queue id & ivc queue count */
hv_of_node = of_parse_phandle(of_node, "ivc_queue", 0);
if (!hv_of_node) {
pr_err("%s: Unable to find hypervisor node\n", __func__);
return -EINVAL;
}
of_node_put(hv_of_node);
err = of_property_read_u32_index(of_node, "ivc_queue", 1,
&first_ivc_queue);
if (err) {
pr_err("%s: Failed to read start IVC queue\n",
__func__);
return err;
}
err = of_property_read_u32_index(of_node, "ivc_queue", 2,
&num_ivc_queues);
if (err) {
pr_err("%s: Failed to read range of IVC queues\n",
__func__);
return err;
}
/* verify total queue count meets expectations */
if (num_ivc_queues < (bpmp->soc->channels.thread.count +
bpmp->soc->channels.cpu_tx.count + bpmp->soc->channels.cpu_rx.count)) {
pr_err("%s: no of ivc queues in DT < required no of channels\n", __func__);
return -EINVAL;
}
hv_bpmp_ivc_cookies = kzalloc(sizeof(struct tegra_hv_ivc_cookie *) *
num_ivc_queues, GFP_KERNEL);
if (!hv_bpmp_ivc_cookies) {
pr_err("%s: Failed to allocate memory\n", __func__);
return -ENOMEM;
}
err = tegra194_hv_bpmp_channel_init(bpmp->tx_channel, bpmp,
bpmp->soc->channels.cpu_tx.offset + first_ivc_queue, false);
if (err < 0) {
pr_err("%s: Failed initialize tx channel\n", __func__);
goto cleanup;
}
for (index = 0; index < bpmp->threaded.count; index++) {
unsigned int idx = bpmp->soc->channels.thread.offset + index;
err = tegra194_hv_bpmp_channel_init(&bpmp->threaded_channels[index],
bpmp, idx + first_ivc_queue, true);
if (err < 0) {
pr_err("%s: Failed initialize tx channel\n", __func__);
goto cleanup;
}
}
tegra194_hv_bpmp_resume(bpmp);
return 0;
cleanup:
for (index = 0; index < num_ivc_queues; index++) {
if (hv_bpmp_ivc_cookies[index]) {
tegra_hv_ivc_unreserve(hv_bpmp_ivc_cookies[index]);
hv_bpmp_ivc_cookies[index] = NULL;
}
}
kfree(hv_bpmp_ivc_cookies);
return err;
}
static void tegra194_hv_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel)
{
struct tegra_hv_ivc_cookie *hv_ivc = to_hv_ivc(channel->ivc);
free_irq(hv_ivc->irq, &hv_ivc);
tegra_hv_ivc_unreserve(hv_ivc);
kfree(hv_ivc);
}
static void tegra194_hv_bpmp_deinit(struct tegra_bpmp *bpmp)
{
unsigned int i;
tegra194_hv_bpmp_channel_cleanup(bpmp->tx_channel);
for (i = 0; i < bpmp->threaded.count; i++)
tegra194_hv_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
}
const struct tegra_bpmp_ops tegra194_bpmp_hv_ops = {
.init = tegra194_hv_bpmp_init,
.deinit = tegra194_hv_bpmp_deinit,
.is_response_ready = tegra194_bpmp_hv_is_message_ready,
.is_request_ready = tegra194_bpmp_hv_is_message_ready,
.ack_response = tegra194_bpmp_hv_ack_message,
.ack_request = tegra194_bpmp_hv_ack_message,
.is_response_channel_free = tegra194_hv_bpmp_is_channel_free,
.is_request_channel_free = tegra194_hv_bpmp_is_channel_free,
.post_response = tegra194_hv_bpmp_post_message,
.post_request = tegra194_hv_bpmp_post_message,
.ring_doorbell = tegra194_hv_ivc_notify,
.resume = tegra194_hv_bpmp_resume,
};
+37 -5
View File
@@ -1,10 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
*/
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright (c) 2016-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include <linux/clk/tegra.h>
#include <linux/genalloc.h>
#include <linux/iosys-map.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -557,7 +556,9 @@ static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
struct mrq_ping_response response;
struct mrq_ping_request request;
struct tegra_bpmp_message msg;
#if (!IS_ENABLED(CONFIG_PREEMPT_RT))
unsigned long flags;
#endif
ktime_t start, end;
int err;
@@ -573,11 +574,17 @@ static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
msg.rx.data = &response;
msg.rx.size = sizeof(response);
#if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_irq_save(flags);
start = ktime_get();
err = tegra_bpmp_transfer_atomic(bpmp, &msg);
end = ktime_get();
local_irq_restore(flags);
#else
start = ktime_get();
err = tegra_bpmp_transfer(bpmp, &msg);
end = ktime_get();
#endif
if (!err)
dev_dbg(bpmp->dev,
@@ -678,7 +685,10 @@ void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp)
count = bpmp->soc->channels.thread.count;
busy = bpmp->threaded.busy;
if (tegra_bpmp_is_request_ready(channel)) {
/* Check if supported incoming channel.
* This won't be called for Guest Linux as channel->ib mapping won't be set.
*/
if (iosys_map_is_set(&channel->ib) && tegra_bpmp_is_request_ready(channel)) {
unsigned int mrq = tegra_bpmp_mb_read_field(&channel->ib, code);
tegra_bpmp_handle_mrq(bpmp, mrq, channel);
@@ -884,6 +894,25 @@ static const struct tegra_bpmp_soc tegra210_soc = {
};
#endif
#if IS_ENABLED(CONFIG_TEGRA_HV_DRIVER)
static const struct tegra_bpmp_soc t194_safe_hv_soc = {
.channels = {
.cpu_tx = {
.offset = 3,
.count = 1,
.timeout = 30 * USEC_PER_SEC,
},
.thread = {
.offset = 0,
.count = 3,
.timeout = 30 * USEC_PER_SEC,
},
},
.ops = &tegra194_bpmp_hv_ops,
.num_resets = 193,
};
#endif
static const struct of_device_id tegra_bpmp_match[] = {
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \
IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
@@ -892,6 +921,9 @@ static const struct of_device_id tegra_bpmp_match[] = {
#endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-bpmp", .data = &tegra210_soc },
#endif
#if IS_ENABLED(CONFIG_TEGRA_HV_DRIVER)
{ .compatible = "nvidia,tegra194-safe-bpmp-hv", .data = &t194_safe_hv_soc },
#endif
{ }
};