[PATCH v3 05/12] firmware: tegra: Add BPMP support
From: jonathanh@nvidia.com (Jon Hunter)
Date: 2016-08-22 09:26:50
Also in:
linux-devicetree, linux-tegra
On 19/08/16 18:32, Thierry Reding wrote:
From: Thierry Reding <redacted> The Boot and Power Management Processor (BPMP) is a co-processor found on Tegra SoCs. It is designed to handle the early stages of the boot process and offload power management tasks (such as clocks, resets, powergates, ...) as well as system control services. Compared to the ARM SCPI, the services provided by BPMP are message- based rather than method-based. The BPMP firmware driver provides the services to transmit data to and receive data from the BPMP. Users can also register an MRQ, for which a service routine will be run when a corresponding event is received from the firmware.
MRQ?
quoted hunk
A set of messages, called the BPMP ABI, are specified for a number of different services provided by the BPMP (such as clocks or resets). Based on work by Sivaram Nair [off-list ref] and Joseph Lo [off-list ref]. Signed-off-by: Thierry Reding <redacted> --- Changes in v3: - use a message structure for transfers to avoid long function prototypes - restructure driver for easier maintainability - rename bpmp_abi.h to bpmp-abi.h for consistency drivers/firmware/tegra/Kconfig | 12 + drivers/firmware/tegra/Makefile | 1 + drivers/firmware/tegra/bpmp.c | 880 +++++++++++++++++++++ include/soc/tegra/bpmp-abi.h | 1601 +++++++++++++++++++++++++++++++++++++++ include/soc/tegra/bpmp.h | 122 +++ 5 files changed, 2616 insertions(+) create mode 100644 drivers/firmware/tegra/bpmp.c create mode 100644 include/soc/tegra/bpmp-abi.h create mode 100644 include/soc/tegra/bpmp.hdiff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig index 1fa3e4e136a5..ff2730d5c468 100644 --- a/drivers/firmware/tegra/Kconfig +++ b/drivers/firmware/tegra/Kconfig@@ -10,4 +10,16 @@ config TEGRA_IVC keeps the content is synchronization between host CPU and remote processors. +config TEGRA_BPMP + bool "Tegra BPMP driver" + depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC + help + BPMP (Boot and Power Management Processor) is designed to off-loading + the PM functions which include clock/DVFS/thermal/power from the CPU. + It needs HSP as the HW synchronization and notification module and + IVC module as the message communication protocol. + + This driver manages the IPC interface between host CPU and the + firmware running on BPMP. + endmenudiff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 92e2153e8173..e34a2f79e1ad 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile@@ -1 +1,2 @@ +obj-$(CONFIG_TEGRA_BPMP) += bpmp.o obj-$(CONFIG_TEGRA_IVC) += ivc.odiff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c new file mode 100644 index 000000000000..a09043b1be05 --- /dev/null +++ b/drivers/firmware/tegra/bpmp.c@@ -0,0 +1,880 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#define DEBUG
I don't think we want DEBUG by default, right?
+#include <linux/clk/tegra.h> +#include <linux/mailbox_client.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/semaphore.h>
[snip]
+static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
+{
+ struct mrq_ping_response response;
+ struct mrq_ping_request request;
+ struct tegra_bpmp_message msg;
+ ktime_t start, delta;
+ unsigned long flags;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.challenge = 1;
+
+ memset(&response, 0, sizeof(response));
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_PING;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ start = ktime_get();
+
+ local_irq_save(flags);
+ err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+ local_irq_restore(flags);
+
+ delta = ktime_sub(ktime_get(), start);
+
+ if (!err)
+ dev_info(bpmp->dev,
+ "ping ok: challenge: %u, response: %u, time: %lld\n",
+ request.challenge, response.reply,
+ ktime_to_us(delta));Should this be a dev_dbg? I guess this only happens on probe.
+ return err; +}
[snip]
+static int tegra_bpmp_probe(struct platform_device *pdev)
+{
+ struct tegra_bpmp_channel *channel;
+ struct tegra_bpmp *bpmp;
+ struct device_node *np;
+ struct resource res;
+ unsigned int i;
+ char tag[32];
+ 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;
+
+ np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
+ if (!np)
+ return -ENOENT;
+
+ of_address_to_resource(np, 0, &res);
+ of_node_put(np);
+
+ bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
+ if (IS_ERR(bpmp->tx_base))
+ return PTR_ERR(bpmp->tx_base);
+
+ np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
+ if (!np)
+ return -ENOENT;
+
+ of_address_to_resource(np, 0, &res);
+ of_node_put(np);
+
+ bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
+ if (IS_ERR(bpmp->rx_base))
+ return PTR_ERR(bpmp->rx_base);
+
+ bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
+ bpmp->soc->channels.thread.count +
+ bpmp->soc->channels.cpu_rx.count;
+
+ bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
+ sizeof(*channel), GFP_KERNEL);
+ if (!bpmp->channels)
+ return -ENOMEM;
+
+ /* mbox registration */
+ bpmp->mbox.client.dev = &pdev->dev;
+ bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
+ bpmp->mbox.client.tx_block = false;
+ bpmp->mbox.client.knows_txdone = false;
+
+ bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
+ if (IS_ERR(bpmp->mbox.channel)) {
+ err = PTR_ERR(bpmp->mbox.channel);
+ dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
+ return err;
+ }
+
+ /* message channel initialization */
+ for (i = 0; i < bpmp->num_channels; i++) {
+ struct tegra_bpmp_channel *channel = &bpmp->channels[i];
+
+ err = tegra_bpmp_channel_init(channel, bpmp, i);
+ if (err)
+ return err;
+ }We should make sure we free the mbox if we fail after requesting it. Cheers Jon -- nvpublic