mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows
@ 2026-09-28 17:09 Ravindra
  2026-09-28 17:10 ` [PATCH v6 1/4] Bluetooth: btintel_pcie: fix stale cache in set_dxstate fallback check Ravindra
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Ravindra @ 2026-09-28 17:09 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: lsa.uz, pmenzel, marcel, luiz.dentz, kiran.k,
	chethan.tumkur.narayan, linux-kernel, Ravindra

This series fixes the suspend/resume path of btintel_pcie.

Patch 1 refreshes the boot-stage register before the fallback state check.
Patch 2 distinguishes S0ix from S3/S4 and routes thaw through a normal D0
transition. Patch 3 verifies the state after GP0 and completes any missing
RX or mailbox setup. It also serializes the alive-context claim between the
GP0 handler and the PM fallback so only one path reinitializes the RX ring.
Patch 4 clears the GP0 cause with a direct W1C write.

Ravindra (2):
  Bluetooth: btintel_pcie: fix PM flow for S0ix, S3 and S4
  Bluetooth: btintel_pcie: clear the GP0 cause with a W1C write

Sergey Lebedev (1):
  Bluetooth: btintel_pcie: verify and serialize D-state transitions

Vladimir V. Kondratyev (1):
  Bluetooth: btintel_pcie: fix stale cache in set_dxstate fallback check

 drivers/bluetooth/btintel_pcie.c | 140 +++++++++++++++++++++++--------
 drivers/bluetooth/btintel_pcie.h |   3 +-
 2 files changed, 107 insertions(+), 36 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v6 1/4] Bluetooth: btintel_pcie: fix stale cache in set_dxstate fallback check
  2026-09-28 17:09 [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Ravindra
@ 2026-09-28 17:10 ` Ravindra
  2026-09-28 17:10 ` [PATCH v6 2/4] Bluetooth: btintel_pcie: fix PM flow for S0ix, S3 and S4 Ravindra
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Ravindra @ 2026-09-28 17:10 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: lsa.uz, pmenzel, marcel, luiz.dentz, kiran.k,
	chethan.tumkur.narayan, linux-kernel, Vladimir V. Kondratyev,
	Ravindra

From: "Vladimir V. Kondratyev" <vladimirkondratyev2@gmail.com>

btintel_pcie returns -16 (EBUSY) during suspend, causing the entire
suspend operation to abort on Intel Lunar Lake hardware. The system
immediately resumes after every suspend attempt:

  Bluetooth: hci0: Timeout (200 ms) on alive interrupt for D2 entry, retry count 0
  Bluetooth: hci0: Timeout (200 ms) on alive interrupt for D2 entry, retry count 1
  Bluetooth: hci0: Timeout (200 ms) on alive interrupt for D2 entry, retry count 2
  btintel_pcie 0000:00:14.7: PM: pci_pm_suspend(): btintel_pcie_suspend [btintel_pcie] returns -16
  btintel_pcie 0000:00:14.7: PM: dpm_run_callback(): pci_pm_suspend returns -16
  btintel_pcie 0000:00:14.7: PM: failed to suspend async: error -16
  PM: Some devices failed to suspend, or early wake event detected

btintel_pcie_set_dxstate() falls back to checking the controller state via
btintel_pcie_in_d3/d0() when the alive interrupt is missed. However, these
helpers read boot_stage_cache, which is only updated by the interrupt
handler. As such, if the interrupt was missed, the cache is stale and the
fallback check always fails, exhausting all retries and returning -EBUSY,
causing suspend to abort.

The fix involves re-reading the hardware register before the fallback state
check, consistent with btintel_pcie_resume().

Fixes: 88c6216a52ea ("Bluetooth: btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling")
Link: https://bugzilla.kernel.org/show_bug.cgi?id=221481
Link: https://lore.kernel.org/linux-bluetooth/20260830151550.44687-1-lsa.uz@pm.me/
Signed-off-by: Vladimir V. Kondratyev <vladimirkondratyev2@gmail.com>
Tested-by: Sergey Lebedev <lsa.uz@pm.me>
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
Signed-off-by: Ravindra <ravindra@intel.com>
---
 drivers/bluetooth/btintel_pcie.c | 8 +++++---
 drivers/bluetooth/btintel_pcie.h | 1 +
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 59cf600014bb..677749903173 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -4222,10 +4222,12 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
 					  BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
 					  BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
 
-		/* A hardware bug may cause the alive interrupt to be missed.
-		 * Check if the controller reached the expected state and retry
-		 * the operation only if it hasn't.
+		/* A hardware bug may cause the alive interrupt to be missed. Refresh
+		 * boot_stage_cache from hardware, since only the interrupt handler
+		 * updates it. Finally retry only if the state check still fails.
 		 */
+		data->boot_stage_cache = btintel_pcie_rd_reg32(data,
+							       BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
 		if (dxstate == BTINTEL_PCIE_STATE_D0) {
 			if (btintel_pcie_in_d0(data))
 				return 0;
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index f35f80f800ed..016795fcbccc 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -51,6 +51,7 @@
 #define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED	(BIT(14))
 #define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON	(BIT(16))
 #define BTINTEL_PCIE_CSR_BOOT_STAGE_ALIVE		(BIT(23))
+/* Reflects live D-state. Updated by hardware on every D-state transition. */
 #define BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY	(BIT(24))
 
 #define BTINTEL_PCIE_CSR_DOORBELL_MBOX_READ_CONFIRM	(BIT(4))
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v6 2/4] Bluetooth: btintel_pcie: fix PM flow for S0ix, S3 and S4
  2026-09-28 17:09 [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Ravindra
  2026-09-28 17:10 ` [PATCH v6 1/4] Bluetooth: btintel_pcie: fix stale cache in set_dxstate fallback check Ravindra
@ 2026-09-28 17:10 ` Ravindra
  2026-09-28 17:10 ` [PATCH v6 3/4] Bluetooth: btintel_pcie: verify and serialize D-state transitions Ravindra
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Ravindra @ 2026-09-28 17:10 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: lsa.uz, pmenzel, marcel, luiz.dentz, kiran.k,
	chethan.tumkur.narayan, linux-kernel, Ravindra

Fix two issues in the PM suspend/resume path:

1. S3 was handled the same as S0ix, keeping the controller on the
   D3hot-style path. That caused resume instability because S3 can
   remove power from the controller, unlike s2idle/S0ix. Use
   pm_suspend_target_state to distinguish them: D3_HOT for S0ix and
   D3_COLD for S3/S4. Add .restore to force FLR-based firmware recovery
   after S4 and S3 (PM_SUSPEND_MEM), as power is lost. S0ix resumes via
   a normal D0 transition.

2. During hibernation, .freeze() puts the controller into the D3cold
   state without any loss of power, and the flow normally continues to
   .poweroff(). If hibernation instead fails, .thaw() is called to bring
   the controller back up, and the old code routed it through
   btintel_pcie_resume(), which forced FLR-based firmware recovery
   whenever data->pm_sx_event was PM_EVENT_FREEZE. That check was
   incorrect: since the controller's power was never actually removed on
   this failed-hibernation path, FLR-based recovery is unnecessary. Remove
   pm_sx_event and route .thaw through a normal D0 transition instead;
   FLR-based recovery is retained only in .restore, where genuine S4
   power loss requires it.

Tested with:
  S0ix: sudo sh -c 'echo "+40" > /sys/class/rtc/rtc0/wakealarm' && \
        echo freeze | sudo tee /sys/power/state
  S3:   sudo rtcwake -m mem  -s 60
  S4:   sudo rtcwake -m disk -s 60

Fixes: e57362f4911b ("Bluetooth: btintel_pcie: Add support for _suspend() / _resume()")
Assisted-by: GitHub-Copilot:GPT5
Signed-off-by: Ravindra <ravindra@intel.com>
Tested-by: Sergey Lebedev <lsa.uz@pm.me>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
---
 drivers/bluetooth/btintel_pcie.c | 58 +++++++++++++++++++++-----------
 drivers/bluetooth/btintel_pcie.h |  2 --
 2 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 677749903173..082f5ec8ba71 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -16,6 +16,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/acpi.h>
+#include <linux/suspend.h>
 
 #include <linux/unaligned.h>
 #include <linux/devcoredump.h>
@@ -4199,7 +4200,8 @@ static void btintel_pcie_coredump(struct device *dev)
 
 static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
 {
-	int retry = 0, status;
+	int retry = 0;
+	long status;
 	u32 dx_intr_timeout_ms = 200;
 
 	do {
@@ -4251,18 +4253,23 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
 
 	data = pci_get_drvdata(pdev);
 
-	dxstate = (mesg.event == PM_EVENT_SUSPEND ?
-		   BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
-
-	data->pm_sx_event = mesg.event;
+	/* S0ix (s2idle) uses D3_HOT; S3, freeze and hibernate use D3_COLD. */
+	if (mesg.event == PM_EVENT_SUSPEND &&
+	    pm_suspend_target_state == PM_SUSPEND_TO_IDLE)
+		dxstate = BTINTEL_PCIE_STATE_D3_HOT;
+	else
+		dxstate = BTINTEL_PCIE_STATE_D3_COLD;
 
 	start = ktime_get();
 
 	/* Refer: 6.4.11.7 -> Platform power management */
 	err = btintel_pcie_set_dxstate(data, dxstate);
 
-	if (err)
+	if (err) {
+		bt_dev_err(data->hdev, "Failed to set dxstate:%u (%d)",
+			   dxstate, err);
 		return err;
+	}
 
 	bt_dev_dbg(data->hdev,
 		   "device entered into d3 state from d0 in %lld us",
@@ -4285,7 +4292,7 @@ static int btintel_pcie_freeze(struct device *dev)
 	return btintel_pcie_suspend_late(dev, PMSG_FREEZE);
 }
 
-static int btintel_pcie_resume(struct device *dev)
+static int btintel_pcie_resume_event(struct device *dev, pm_message_t mesg)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct btintel_pcie_data *data;
@@ -4293,19 +4300,15 @@ static int btintel_pcie_resume(struct device *dev)
 	int err;
 
 	data = pci_get_drvdata(pdev);
-	data->gp0_received = false;
 
 	start = ktime_get();
 
-	/* When the system enters S4 (hibernate) mode, bluetooth device loses
-	 * power, which results in the erasure of its loaded firmware.
-	 * Consequently, function level reset (flr) is required on system
-	 * resume to bring the controller back into an operational state by
-	 * initiating a new firmware download.
+	/* S3 and S4 may cut power, erasing the firmware. Force FLR to recover
+	 * instead of a normal D0 transition.
 	 */
-
-	if (data->pm_sx_event == PM_EVENT_FREEZE ||
-	    data->pm_sx_event == PM_EVENT_HIBERNATE) {
+	if (mesg.event == PM_EVENT_RESTORE ||
+	    (mesg.event == PM_EVENT_RESUME &&
+	     pm_suspend_target_state == PM_SUSPEND_MEM)) {
 		set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
 		btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
 		return 0;
@@ -4314,7 +4317,9 @@ static int btintel_pcie_resume(struct device *dev)
 	/* Refer: 6.4.11.7 -> Platform power management */
 	err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0);
 
-	if (err == 0) {
+	if (err) {
+		bt_dev_err(data->hdev, "Failed to set D0 state (%d)", err);
+	} else {
 		bt_dev_dbg(data->hdev,
 			   "device entered into d0 state from d3 in %lld us",
 			   ktime_to_us(ktime_get() - start));
@@ -4339,13 +4344,28 @@ static int btintel_pcie_resume(struct device *dev)
 	return err;
 }
 
+static int btintel_pcie_resume(struct device *dev)
+{
+	return btintel_pcie_resume_event(dev, PMSG_RESUME);
+}
+
+static int btintel_pcie_restore(struct device *dev)
+{
+	return btintel_pcie_resume_event(dev, PMSG_RESTORE);
+}
+
+static int btintel_pcie_thaw(struct device *dev)
+{
+	return btintel_pcie_resume_event(dev, PMSG_THAW);
+}
+
 static const struct dev_pm_ops btintel_pcie_pm_ops = {
 	.suspend = btintel_pcie_suspend,
 	.resume = btintel_pcie_resume,
 	.freeze = btintel_pcie_freeze,
-	.thaw = btintel_pcie_resume,
+	.thaw = btintel_pcie_thaw,
 	.poweroff = btintel_pcie_hibernate,
-	.restore = btintel_pcie_resume,
+	.restore = btintel_pcie_restore,
 };
 
 static struct pci_driver btintel_pcie_driver = {
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 016795fcbccc..3030b4e8b750 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -713,7 +713,6 @@ struct btintel_pcie_ini_dump_info {
  * @txq: TX Queue struct
  * @rxq: RX Queue struct
  * @alive_intr_ctxt: Alive interrupt context
- * @pm_sx_event: PM event on which system got suspended
  */
 struct btintel_pcie_data {
 	struct pci_dev	*pdev;
@@ -773,7 +772,6 @@ struct btintel_pcie_data {
 	struct btintel_pcie_dbgc	dbgc;
 	struct btintel_pcie_mdbgc	mdbgc;
 	struct btintel_pcie_dump_header dmp_hdr;
-	u8	pm_sx_event;
 	u32	debug_evt_addr;
 	u32	debug_evt_size;
 	dma_addr_t	debug_table_addr;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v6 3/4] Bluetooth: btintel_pcie: verify and serialize D-state transitions
  2026-09-28 17:09 [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Ravindra
  2026-09-28 17:10 ` [PATCH v6 1/4] Bluetooth: btintel_pcie: fix stale cache in set_dxstate fallback check Ravindra
  2026-09-28 17:10 ` [PATCH v6 2/4] Bluetooth: btintel_pcie: fix PM flow for S0ix, S3 and S4 Ravindra
@ 2026-09-28 17:10 ` Ravindra
  2026-09-28 17:10 ` [PATCH v6 4/4] Bluetooth: btintel_pcie: clear the GP0 cause with a W1C write Ravindra
  2026-09-28 21:14 ` [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Sergey Lebedev
  4 siblings, 0 replies; 6+ messages in thread
From: Ravindra @ 2026-09-28 17:10 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: lsa.uz, pmenzel, marcel, luiz.dentz, kiran.k,
	chethan.tumkur.narayan, linux-kernel, Ravindra

From: Sergey Lebedev <lsa.uz@pm.me>

The gp0_received flag only reports that the GP0 handler ran. It does not
contain proof that the handler completed the requested D-state transition:
a matched case can leave the state unchanged.

Refresh the boot-stage register before treating the transition as
successful. When the controller is in D0 but the handler did not re-arm the
interface, restore the alive context, reset the IA, restart RX, and
complete the mailbox/alive handshake. Propagate RX setup failures to the PM
caller. Likewise, when the controller is in D3 but the handler did not
record it, restore the alive context so the next transition dispatches
correctly.

Serialize the alive-context claim between the GP0 handler and the
set_dxstate() fallback with irq_lock. Only the path that claims the D0
transition resets the IA and rearms RX, avoiding concurrent updates to the
shared ring state.

Co-developed-by: Ravindra <ravindra@intel.com>
Signed-off-by: Ravindra <ravindra@intel.com>
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
Fixes: 88c6216a52ea ("Bluetooth: btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling")
Tested-by: Sergey Lebedev <lsa.uz@pm.me>
---
 drivers/bluetooth/btintel_pcie.c | 80 +++++++++++++++++++++++++-------
 1 file changed, 64 insertions(+), 16 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 082f5ec8ba71..a5b0576e922c 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1975,6 +1975,10 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
 	submit_rx = false;
 	signal_waitq = false;
 
+	/* Serialize the alive_intr_ctxt claim against set_dxstate()'s
+	 * register-based fallback, which can run concurrently.
+	 */
+	spin_lock(&data->irq_lock);
 	switch (data->alive_intr_ctxt) {
 	case BTINTEL_PCIE_ROM:
 		data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL;
@@ -2030,6 +2034,7 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
 			   data->alive_intr_ctxt);
 		break;
 	}
+	spin_unlock(&data->irq_lock);
 
 	if (submit_rx) {
 		btintel_pcie_reset_ia(data);
@@ -4200,7 +4205,7 @@ static void btintel_pcie_coredump(struct device *dev)
 
 static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
 {
-	int retry = 0;
+	int retry = 0, err;
 	long status;
 	u32 dx_intr_timeout_ms = 200;
 
@@ -4212,30 +4217,73 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
 		status = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
 			msecs_to_jiffies(dx_intr_timeout_ms));
 
-		if (status)
-			return 0;
-
-		bt_dev_warn(data->hdev,
-			   "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
-			   dx_intr_timeout_ms, dxstate, retry);
+		if (!status) {
+			bt_dev_warn(data->hdev,
+				    "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
+				    dx_intr_timeout_ms, dxstate, retry);
 
-		/* clear gp0 cause */
-		btintel_pcie_clr_reg_bits(data,
-					  BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
-					  BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
+			/* clear gp0 cause */
+			btintel_pcie_clr_reg_bits(data,
+						  BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
+						  BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
+		}
 
-		/* A hardware bug may cause the alive interrupt to be missed. Refresh
-		 * boot_stage_cache from hardware, since only the interrupt handler
-		 * updates it. Finally retry only if the state check still fails.
+		/* gp0_received is set at the top of the handler, before the switch on
+		 * alive_intr_ctxt. Error and lockdown are filtered out above it, but a
+		 * matched case can still complete without doing anything - D3 breaks
+		 * unchanged while the controller has not reached D0 - and a hardware
+		 * bug may drop the interrupt outright. Either way the flag says a gp0
+		 * was handled, not that the transition completed, and only the register
+		 * knows. Refresh the cache here and retry only if the state check still
+		 * fails.
 		 */
 		data->boot_stage_cache = btintel_pcie_rd_reg32(data,
 							       BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
 		if (dxstate == BTINTEL_PCIE_STATE_D0) {
-			if (btintel_pcie_in_d0(data))
+			if (btintel_pcie_in_d0(data)) {
+				bool armed;
+
+				/* Claim the transition under irq_lock so a
+				 * concurrent gp0 handler run cannot also arm RX.
+				 */
+				spin_lock(&data->irq_lock);
+				armed = data->alive_intr_ctxt == BTINTEL_PCIE_D0;
+				if (!armed)
+					data->alive_intr_ctxt = BTINTEL_PCIE_D0;
+				spin_unlock(&data->irq_lock);
+
+				if (armed)
+					return 0;
+
+				/* Do what the handler's D3 -> D0 branch
+				 * would have done, unless it already has.
+				 */
+				btintel_pcie_reset_ia(data);
+				err = btintel_pcie_start_rx(data);
+				if (err)
+					return err;
+
+				/* Complete the mbox<->alive handshake */
+				if (test_and_clear_bit(BTINTEL_PCIE_MBOX_PARSE_PENDING,
+						       &data->flags)) {
+					set_bit(BTINTEL_PCIE_MBOX_PARSE_READY, &data->flags);
+					wake_up(&data->mbox_parse_wait_q);
+				}
+
 				return 0;
+			}
 		} else {
-			if (btintel_pcie_in_d3(data))
+			if (btintel_pcie_in_d3(data)) {
+				/* Do what the handler's D0 -> D3 branch
+				 * would have done, unless it already has.
+				 */
+				spin_lock(&data->irq_lock);
+				if (data->alive_intr_ctxt != BTINTEL_PCIE_D3)
+					data->alive_intr_ctxt = BTINTEL_PCIE_D3;
+				spin_unlock(&data->irq_lock);
+
 				return 0;
+			}
 		}
 
 	} while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v6 4/4] Bluetooth: btintel_pcie: clear the GP0 cause with a W1C write
  2026-09-28 17:09 [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Ravindra
                   ` (2 preceding siblings ...)
  2026-09-28 17:10 ` [PATCH v6 3/4] Bluetooth: btintel_pcie: verify and serialize D-state transitions Ravindra
@ 2026-09-28 17:10 ` Ravindra
  2026-09-28 21:14 ` [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Sergey Lebedev
  4 siblings, 0 replies; 6+ messages in thread
From: Ravindra @ 2026-09-28 17:10 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: lsa.uz, pmenzel, marcel, luiz.dentz, kiran.k,
	chethan.tumkur.narayan, linux-kernel, Ravindra

MSIX_HW_INT_CAUSES is a write-one-to-clear register. Using a
read-modify-write helper writes zero to GP0, leaving the cause uncleared,
and can acknowledge unrelated pending causes through the read value.

Write only the GP0 bit directly, matching the interrupt handler's W1C
acknowledgment convention.

Fixes: 88c6216a52ea ("Bluetooth: btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling")
Signed-off-by: Ravindra <ravindra@intel.com>
Tested-by: Sergey Lebedev <lsa.uz@pm.me>
Reviewed-by: Sergey Lebedev <lsa.uz@pm.me>
---
 drivers/bluetooth/btintel_pcie.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index a5b0576e922c..9d3b5adfa847 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -4222,10 +4222,12 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
 				    "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
 				    dx_intr_timeout_ms, dxstate, retry);
 
-			/* clear gp0 cause */
-			btintel_pcie_clr_reg_bits(data,
-						  BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
-						  BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
+			/* MSIX_HW_INT_CAUSES is W1C. Write only GP0 so other
+			 * pending causes are not acknowledged here.
+			 */
+			btintel_pcie_wr_reg32(data,
+					      BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
+					      BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
 		}
 
 		/* gp0_received is set at the top of the handler, before the switch on
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows
  2026-09-28 17:09 [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Ravindra
                   ` (3 preceding siblings ...)
  2026-09-28 17:10 ` [PATCH v6 4/4] Bluetooth: btintel_pcie: clear the GP0 cause with a W1C write Ravindra
@ 2026-09-28 21:14 ` Sergey Lebedev
  4 siblings, 0 replies; 6+ messages in thread
From: Sergey Lebedev @ 2026-09-28 21:14 UTC (permalink / raw)
  To: Ravindra, linux-bluetooth
  Cc: Paul Menzel, Marcel Holtmann, Luiz Augusto von Dentz, Kiran K,
	Chethan Tumkur Narayan, Vladimir V. Kondratyev, linux-kernel

Ravindra,

Thank you for taking the race on and fixing it in 3/4.

I ran v6 on the Surface Pro 11 bench (BE201 8086:a876, s2idle, the same
base as for v4), with an instrumented copy that forces each path:

- plain cycles: the handler records D3 and re-arms RX, as before
- a gp0 dropped on the way down: one 200 ms timeout, the register read
  again, D3 recorded
- the handler's D0 or D3 case forced to break: the fallback records D3,
  or claims D0 and re-arms RX
- the race: the handler held in case D3 for 300 ms after deciding to
  claim D0, past the 200 ms wait. With v5 the fallback re-armed RX at
  203 ms and the handler again at 300 ms. With v6 the fallback waited on
  irq_lock, found D0 claimed and returned, and RX was re-armed once.

After every cycle Bluetooth answered an HCI command and scanned.

Tested-by: Sergey Lebedev <lsa.uz@pm.me>

Sergey


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-28 21:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-28 17:09 [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Ravindra
2026-09-28 17:10 ` [PATCH v6 1/4] Bluetooth: btintel_pcie: fix stale cache in set_dxstate fallback check Ravindra
2026-09-28 17:10 ` [PATCH v6 2/4] Bluetooth: btintel_pcie: fix PM flow for S0ix, S3 and S4 Ravindra
2026-09-28 17:10 ` [PATCH v6 3/4] Bluetooth: btintel_pcie: verify and serialize D-state transitions Ravindra
2026-09-28 17:10 ` [PATCH v6 4/4] Bluetooth: btintel_pcie: clear the GP0 cause with a W1C write Ravindra
2026-09-28 21:14 ` [PATCH v6 0/4] Bluetooth: btintel_pcie: fix D-state transitions and PM flows Sergey Lebedev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®