* [PATCH] bluetooth: hci_bcm4377: Implement Runtime PM support
@ 2026-07-15 14:59 gg582
2026-07-15 15:07 ` bluez.test.bot
0 siblings, 1 reply; 2+ messages in thread
From: gg582 @ 2026-07-15 14:59 UTC (permalink / raw)
To: marcel@holtmann.org, luiz.dentz@gmail.com, sven@kernel.org,
j@jannau.net
Cc: neal@gompa.dev, linux-bluetooth@vger.kernel.org,
asahi@lists.linux.dev, linux-kernel@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 6412 bytes --]
From dc47862023158e5a244a0a2b9025dd544e658a13 Mon Sep 17 00:00:00 2001
From: Lee Yunjin <gg582@proton.me>
Date: Fri, 3 Jul 2026 10:12:56 +0900
Subject: [PATCH] bluetooth: hci_bcm4377: Implement Runtime PM support
Signed-off-by: Lee Yunjin <gg582@proton.me>
---
drivers/bluetooth/hci_bcm4377.c | 96 +++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 5f79920c0306..0d5f818bde71 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -15,6 +15,7 @@
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/pci.h>
+#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include <linux/unaligned.h>
@@ -840,6 +841,8 @@ static irqreturn_t bcm4377_irq(int irq, void *data)
struct bcm4377_data *bcm4377 = data;
u32 bootstage, rti_status;
+ pm_runtime_get(&bcm4377->pdev->dev);
+
bootstage = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_BOOTSTAGE);
rti_status = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_STATUS);
@@ -863,6 +866,9 @@ static irqreturn_t bcm4377_irq(int irq, void *data)
bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+ pm_runtime_mark_last_busy(&bcm4377->pdev->dev);
+ pm_runtime_put_autosuspend(&bcm4377->pdev->dev);
+
return IRQ_HANDLED;
}
@@ -892,6 +898,8 @@ static int bcm4377_enqueue(struct bcm4377_data *bcm4377,
if (ring->virtual)
return -EINVAL;
+ pm_runtime_get(&bcm4377->pdev->dev);
+
spin_lock_irqsave(&ring->lock, flags);
head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]);
@@ -974,6 +982,8 @@ static int bcm4377_enqueue(struct bcm4377_data *bcm4377,
spin_unlock_irqrestore(&ring->lock, flags);
}
+ pm_runtime_mark_last_busy(&bcm4377->pdev->dev);
+ pm_runtime_put_autosuspend(&bcm4377->pdev->dev);
return ret;
}
@@ -1309,6 +1319,12 @@ static int bcm4377_hci_open(struct hci_dev *hdev)
struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
int ret;
+ ret = pm_runtime_get_sync(&bcm4377->pdev->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&bcm4377->pdev->dev);
+ return ret;
+ }
+
dev_dbg(&bcm4377->pdev->dev, "creating rings\n");
ret = bcm4377_create_completion_ring(bcm4377,
@@ -1392,6 +1408,8 @@ static int bcm4377_hci_close(struct hci_dev *hdev)
bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring);
bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring);
+ pm_runtime_put_sync_suspend(&bcm4377->pdev->dev);
+
return 0;
}
@@ -2273,6 +2291,14 @@ static void bcm4377_hci_unregister_dev(void *data)
hci_unregister_dev(data);
}
+static void bcm4377_pm_runtime_disable(void *data)
+{
+ struct pci_dev *pdev = data;
+
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_forbid(&pdev->dev);
+}
+
static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct bcm4377_data *bcm4377;
@@ -2414,8 +2440,25 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = hci_register_dev(hdev);
if (ret)
return ret;
- return devm_add_action_or_reset(&pdev->dev, bcm4377_hci_unregister_dev,
- hdev);
+ ret = devm_add_action_or_reset(&pdev->dev, bcm4377_hci_unregister_dev,
+ hdev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev, bcm4377_pm_runtime_disable,
+ pdev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ /* pm_runtime_enable() already called by PCI bus core (pci/bus.c);
+ * calling it again would cause "Unbalanced pm_runtime_enable!"
+ * pm_runtime_irq_safe(&pdev->dev) is removed to avoid lock/timing issues in GNOME bluetooth. */
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
+ return 0;
}
static int bcm4377_suspend(struct device *dev)
@@ -2424,9 +2467,17 @@ static int bcm4377_suspend(struct device *dev)
struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
int ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
ret = hci_suspend_dev(bcm4377->hdev);
- if (ret)
+ if (ret) {
+ pm_runtime_put_sync(dev);
return ret;
+ }
iowrite32(BCM4377_BAR0_SLEEP_CONTROL_QUIESCE,
bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
@@ -2438,15 +2489,50 @@ static int bcm4377_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+ int ret;
iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE,
bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
- return hci_resume_dev(bcm4377->hdev);
+ ret = hci_resume_dev(bcm4377->hdev);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int bcm4377_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_QUIESCE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ pci_save_state(pdev);
+ return 0;
+}
+
+static int bcm4377_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(bcm4377_ops, bcm4377_suspend, bcm4377_resume);
+/* Re-define with runtime PM once callbacks are in place */
+static const struct dev_pm_ops bcm4377_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(bcm4377_suspend, bcm4377_resume)
+ SET_RUNTIME_PM_OPS(bcm4377_runtime_suspend, bcm4377_runtime_resume, NULL)
+};
+
static const struct dmi_system_id bcm4377_dmi_board_table[] = {
{
.matches = {
@@ -2547,7 +2633,7 @@ static struct pci_driver bcm4377_pci_driver = {
.name = "hci_bcm4377",
.id_table = bcm4377_devid_table,
.probe = bcm4377_probe,
- .driver.pm = &bcm4377_ops,
+ .driver.pm = &bcm4377_pm_ops,
};
module_pci_driver(bcm4377_pci_driver);
--
2.55.0
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-bluetooth-hci_bcm4377-Implement-Runtime-PM-support.patch --]
[-- Type: text/x-patch; name=0001-bluetooth-hci_bcm4377-Implement-Runtime-PM-support.patch, Size: 6207 bytes --]
From dc47862023158e5a244a0a2b9025dd544e658a13 Mon Sep 17 00:00:00 2001
From: Lee Yunjin <gg582@proton.me>
Date: Fri, 3 Jul 2026 10:12:56 +0900
Subject: [PATCH] bluetooth: hci_bcm4377: Implement Runtime PM support
Signed-off-by: Lee Yunjin <gg582@proton.me>
---
drivers/bluetooth/hci_bcm4377.c | 96 +++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 5f79920c0306..0d5f818bde71 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -15,6 +15,7 @@
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/pci.h>
+#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include <linux/unaligned.h>
@@ -840,6 +841,8 @@ static irqreturn_t bcm4377_irq(int irq, void *data)
struct bcm4377_data *bcm4377 = data;
u32 bootstage, rti_status;
+ pm_runtime_get(&bcm4377->pdev->dev);
+
bootstage = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_BOOTSTAGE);
rti_status = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_STATUS);
@@ -863,6 +866,9 @@ static irqreturn_t bcm4377_irq(int irq, void *data)
bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+ pm_runtime_mark_last_busy(&bcm4377->pdev->dev);
+ pm_runtime_put_autosuspend(&bcm4377->pdev->dev);
+
return IRQ_HANDLED;
}
@@ -892,6 +898,8 @@ static int bcm4377_enqueue(struct bcm4377_data *bcm4377,
if (ring->virtual)
return -EINVAL;
+ pm_runtime_get(&bcm4377->pdev->dev);
+
spin_lock_irqsave(&ring->lock, flags);
head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]);
@@ -974,6 +982,8 @@ static int bcm4377_enqueue(struct bcm4377_data *bcm4377,
spin_unlock_irqrestore(&ring->lock, flags);
}
+ pm_runtime_mark_last_busy(&bcm4377->pdev->dev);
+ pm_runtime_put_autosuspend(&bcm4377->pdev->dev);
return ret;
}
@@ -1309,6 +1319,12 @@ static int bcm4377_hci_open(struct hci_dev *hdev)
struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
int ret;
+ ret = pm_runtime_get_sync(&bcm4377->pdev->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&bcm4377->pdev->dev);
+ return ret;
+ }
+
dev_dbg(&bcm4377->pdev->dev, "creating rings\n");
ret = bcm4377_create_completion_ring(bcm4377,
@@ -1392,6 +1408,8 @@ static int bcm4377_hci_close(struct hci_dev *hdev)
bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring);
bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring);
+ pm_runtime_put_sync_suspend(&bcm4377->pdev->dev);
+
return 0;
}
@@ -2273,6 +2291,14 @@ static void bcm4377_hci_unregister_dev(void *data)
hci_unregister_dev(data);
}
+static void bcm4377_pm_runtime_disable(void *data)
+{
+ struct pci_dev *pdev = data;
+
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_forbid(&pdev->dev);
+}
+
static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct bcm4377_data *bcm4377;
@@ -2414,8 +2440,25 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = hci_register_dev(hdev);
if (ret)
return ret;
- return devm_add_action_or_reset(&pdev->dev, bcm4377_hci_unregister_dev,
- hdev);
+ ret = devm_add_action_or_reset(&pdev->dev, bcm4377_hci_unregister_dev,
+ hdev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev, bcm4377_pm_runtime_disable,
+ pdev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ /* pm_runtime_enable() already called by PCI bus core (pci/bus.c);
+ * calling it again would cause "Unbalanced pm_runtime_enable!"
+ * pm_runtime_irq_safe(&pdev->dev) is removed to avoid lock/timing issues in GNOME bluetooth. */
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
+ return 0;
}
static int bcm4377_suspend(struct device *dev)
@@ -2424,9 +2467,17 @@ static int bcm4377_suspend(struct device *dev)
struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
int ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
ret = hci_suspend_dev(bcm4377->hdev);
- if (ret)
+ if (ret) {
+ pm_runtime_put_sync(dev);
return ret;
+ }
iowrite32(BCM4377_BAR0_SLEEP_CONTROL_QUIESCE,
bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
@@ -2438,15 +2489,50 @@ static int bcm4377_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+ int ret;
iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE,
bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
- return hci_resume_dev(bcm4377->hdev);
+ ret = hci_resume_dev(bcm4377->hdev);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int bcm4377_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_QUIESCE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ pci_save_state(pdev);
+ return 0;
+}
+
+static int bcm4377_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(bcm4377_ops, bcm4377_suspend, bcm4377_resume);
+/* Re-define with runtime PM once callbacks are in place */
+static const struct dev_pm_ops bcm4377_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(bcm4377_suspend, bcm4377_resume)
+ SET_RUNTIME_PM_OPS(bcm4377_runtime_suspend, bcm4377_runtime_resume, NULL)
+};
+
static const struct dmi_system_id bcm4377_dmi_board_table[] = {
{
.matches = {
@@ -2547,7 +2633,7 @@ static struct pci_driver bcm4377_pci_driver = {
.name = "hci_bcm4377",
.id_table = bcm4377_devid_table,
.probe = bcm4377_probe,
- .driver.pm = &bcm4377_ops,
+ .driver.pm = &bcm4377_pm_ops,
};
module_pci_driver(bcm4377_pci_driver);
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-15 15:07 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 14:59 [PATCH] bluetooth: hci_bcm4377: Implement Runtime PM support gg582
2026-07-15 15:07 ` bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox