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:
committed by
Noah Wager
parent
b1aca4ebc0
commit
72ac5288b5
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
{ }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user