[v2,1/2] firmware: tegra: add suspend hook and reset BPMP IPC early on resume
Commit Message
Add suspend hook and a 'suspended' field in the 'struct tegra_bpmp'
to mark if BPMP is suspended. Also, add a 'flags' field in the
'struct tegra_bpmp_message' whose 'TEGRA_BPMP_MESSAGE_RESET' bit
can be set from the Tegra MC driver to signal that the reset of BPMP
IPC channels is required before sending MRQ to the BPMP FW.
Together both the fields allow us to handle any requests that might
be sent too soon as they can cause hang during system resume.
One case where we see BPMP requests being sent before the BPMP driver
has resumed is the memory bandwidth requests which are triggered by
onlining the CPUs during system resume. The CPUs are onlined before
the BPMP has resumed and we need to reset the BPMP IPC channels to
handle these requests.
The additional check for 'flags' is done to avoid any un-intended BPMP
IPC reset if the tegra_bpmp_transfer*() API gets called during suspend
sequence after the BPMP driver is suspended.
Fixes: f41e1442ac5b ("cpufreq: tegra194: add OPP support and set bandwidth")
Co-developed-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/firmware/tegra/bpmp.c | 30 ++++++++++++++++++++++++++++++
include/soc/tegra/bpmp.h | 6 ++++++
2 files changed, 36 insertions(+)
Comments
On Mon, Oct 09, 2023 at 03:35:56PM +0530, Sumit Gupta wrote:
> Add suspend hook and a 'suspended' field in the 'struct tegra_bpmp'
> to mark if BPMP is suspended. Also, add a 'flags' field in the
> 'struct tegra_bpmp_message' whose 'TEGRA_BPMP_MESSAGE_RESET' bit
> can be set from the Tegra MC driver to signal that the reset of BPMP
> IPC channels is required before sending MRQ to the BPMP FW.
> Together both the fields allow us to handle any requests that might
> be sent too soon as they can cause hang during system resume.
> One case where we see BPMP requests being sent before the BPMP driver
> has resumed is the memory bandwidth requests which are triggered by
> onlining the CPUs during system resume. The CPUs are onlined before
> the BPMP has resumed and we need to reset the BPMP IPC channels to
> handle these requests.
> The additional check for 'flags' is done to avoid any un-intended BPMP
> IPC reset if the tegra_bpmp_transfer*() API gets called during suspend
> sequence after the BPMP driver is suspended.
>
> Fixes: f41e1442ac5b ("cpufreq: tegra194: add OPP support and set bandwidth")
> Co-developed-by: Thierry Reding <treding@nvidia.com>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/firmware/tegra/bpmp.c | 30 ++++++++++++++++++++++++++++++
> include/soc/tegra/bpmp.h | 6 ++++++
> 2 files changed, 36 insertions(+)
Krzysztof,
if you want to pick these up instead of me taking them through the Tegra
tree:
Acked-by: Thierry Reding <treding@nvidia.com>
@@ -313,6 +313,8 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
return __tegra_bpmp_channel_write(channel, mrq, flags, data, size);
}
+static int __maybe_unused tegra_bpmp_resume(struct device *dev);
+
int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
struct tegra_bpmp_message *msg)
{
@@ -325,6 +327,14 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
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)
+ tegra_bpmp_resume(bpmp->dev);
+ else
+ return -EAGAIN;
+ }
+
channel = bpmp->tx_channel;
spin_lock(&bpmp->atomic_tx_lock);
@@ -364,6 +374,14 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
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)
+ tegra_bpmp_resume(bpmp->dev);
+ else
+ return -EAGAIN;
+ }
+
channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data,
msg->tx.size);
if (IS_ERR(channel))
@@ -796,10 +814,21 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
return err;
}
+static int __maybe_unused tegra_bpmp_suspend(struct device *dev)
+{
+ struct tegra_bpmp *bpmp = dev_get_drvdata(dev);
+
+ bpmp->suspended = true;
+
+ return 0;
+}
+
static int __maybe_unused tegra_bpmp_resume(struct device *dev)
{
struct tegra_bpmp *bpmp = dev_get_drvdata(dev);
+ bpmp->suspended = false;
+
if (bpmp->soc->ops->resume)
return bpmp->soc->ops->resume(bpmp);
else
@@ -807,6 +836,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev)
}
static const struct dev_pm_ops tegra_bpmp_pm_ops = {
+ .suspend_noirq = tegra_bpmp_suspend,
.resume_noirq = tegra_bpmp_resume,
};
@@ -102,8 +102,12 @@ struct tegra_bpmp {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_mirror;
#endif
+
+ bool suspended;
};
+#define TEGRA_BPMP_MESSAGE_RESET BIT(0)
+
struct tegra_bpmp_message {
unsigned int mrq;
@@ -117,6 +121,8 @@ struct tegra_bpmp_message {
size_t size;
int ret;
} rx;
+
+ unsigned long flags;
};
#if IS_ENABLED(CONFIG_TEGRA_BPMP)