Linux bluetooth development
 help / color / mirror / Atom feed
* [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