* RE: Bluetooth: add hardware rfkill handling
2026-10-02 1:43 [PATCH v2 1/2] Bluetooth: Add interface to allow stack to communicate hw rfkill events Sai Teja Aluvala
@ 2026-10-02 3:14 ` bluez.test.bot
0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-10-02 3:14 UTC (permalink / raw)
To: linux-bluetooth, aluvala.sai.teja
[-- Attachment #1: Type: text/plain, Size: 2225 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1178028/
---Test result---
Test Summary:
CheckPatch PASS 1.50 seconds
VerifyFixes PASS 0.86 seconds
VerifySignedoff PASS 0.74 seconds
GitLint PASS 0.56 seconds
SubjectPrefix PASS 0.17 seconds
BuildKernel PASS 22.65 seconds
CheckAllWarning PASS 26.26 seconds
CheckSparse PASS 27.89 seconds
BuildKernel32 PASS 22.88 seconds
CheckKernelLLVM PASS 25.29 seconds
TestRunnerSetup PASS 566.67 seconds
TestRunner_l2cap-tester PASS 13.50 seconds
TestRunner_iso-tester PASS 9.48 seconds
TestRunner_bnep-tester PASS 2.74 seconds
TestRunner_mgmt-tester FAIL 56.99 seconds
TestRunner_rfcomm-tester PASS 4.01 seconds
TestRunner_sco-tester PASS 5.04 seconds
TestRunner_ioctl-tester PASS 4.14 seconds
TestRunner_mesh-tester FAIL 7.64 seconds
TestRunner_smp-tester PASS 3.91 seconds
TestRunner_userchan-tester PASS 7.88 seconds
TestRunner_6lowpan-tester PASS 5.01 seconds
IncrementalBuild PASS 23.03 seconds
Details
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 503, Passed: 502 (99.8%), Failed: 1, Not Run: 0
Failed Test Cases
Pairing Acceptor - LE Security Level Changed Failed 0.020 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 2.668 seconds
Mesh - Send cancel - 2 Timed out 1.998 seconds
https://github.com/bluez/bluetooth-next/pull/849
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: Bluetooth: add hardware rfkill handling
2026-10-14 6:19 [PATCH v3 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
@ 2026-10-14 7:09 ` bluez.test.bot
0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-10-14 7:09 UTC (permalink / raw)
To: linux-bluetooth, aluvala.sai.teja
[-- Attachment #1: Type: text/plain, Size: 1605 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1185286/
---Test result---
Test Summary:
CheckPatch PASS 2.13 seconds
VerifyFixes PASS 0.12 seconds
VerifySignedoff PASS 0.12 seconds
GitLint PASS 0.65 seconds
SubjectPrefix PASS 0.24 seconds
BuildKernel PASS 29.57 seconds
CheckAllWarning PASS 33.50 seconds
CheckSparse PASS 36.49 seconds
BuildKernel32 PASS 29.60 seconds
CheckKernelLLVM PASS 33.23 seconds
TestRunnerSetup PASS 714.03 seconds
TestRunner_l2cap-tester PASS 12.59 seconds
TestRunner_iso-tester PASS 16.83 seconds
TestRunner_bnep-tester PASS 3.14 seconds
TestRunner_mgmt-tester PASS 87.37 seconds
TestRunner_rfcomm-tester PASS 4.46 seconds
TestRunner_sco-tester PASS 5.61 seconds
TestRunner_ioctl-tester PASS 7.76 seconds
TestRunner_mesh-tester PASS 5.85 seconds
TestRunner_smp-tester PASS 4.35 seconds
TestRunner_userchan-tester PASS 3.18 seconds
TestRunner_6lowpan-tester PASS 4.42 seconds
IncrementalBuild PASS 29.63 seconds
https://github.com/bluez/bluetooth-next/pull/929
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: Bluetooth: add hardware rfkill handling
2026-10-14 8:26 [PATCH v4 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
@ 2026-10-14 12:52 ` bluez.test.bot
0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-10-14 12:52 UTC (permalink / raw)
To: linux-bluetooth, aluvala.sai.teja
[-- Attachment #1: Type: text/plain, Size: 1605 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1185391/
---Test result---
Test Summary:
CheckPatch PASS 1.56 seconds
VerifyFixes PASS 0.08 seconds
VerifySignedoff PASS 0.09 seconds
GitLint PASS 0.46 seconds
SubjectPrefix PASS 0.27 seconds
BuildKernel PASS 22.51 seconds
CheckAllWarning PASS 25.95 seconds
CheckSparse PASS 27.42 seconds
BuildKernel32 PASS 23.15 seconds
CheckKernelLLVM PASS 24.83 seconds
TestRunnerSetup PASS 512.02 seconds
TestRunner_l2cap-tester PASS 13.40 seconds
TestRunner_iso-tester PASS 9.38 seconds
TestRunner_bnep-tester PASS 2.78 seconds
TestRunner_mgmt-tester PASS 82.92 seconds
TestRunner_rfcomm-tester PASS 4.05 seconds
TestRunner_sco-tester PASS 5.05 seconds
TestRunner_ioctl-tester PASS 10.17 seconds
TestRunner_mesh-tester PASS 5.09 seconds
TestRunner_smp-tester PASS 3.97 seconds
TestRunner_userchan-tester PASS 2.82 seconds
TestRunner_6lowpan-tester PASS 4.01 seconds
IncrementalBuild PASS 23.08 seconds
https://github.com/bluez/bluetooth-next/pull/931
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v9 0/2] Bluetooth: add hardware rfkill handling
@ 2026-10-15 18:18 Sai Teja Aluvala
2026-10-15 18:18 ` [PATCH v9 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
2026-10-15 18:18 ` [PATCH v9 2/2] Bluetooth: btintel_pcie: add HW rfkill interrupt/doorbell flow Sai Teja Aluvala
0 siblings, 2 replies; 7+ messages in thread
From: Sai Teja Aluvala @ 2026-10-15 18:18 UTC (permalink / raw)
To: Luiz Augusto von Dentz, linux-bluetooth
Cc: kiran.k, chethan.tumkur.narayan, Sai Teja Aluvala
This series adds hardware rfkill handling for the Intel PCIe transport
and the Bluetooth-side notification path.
The resulting flow is:
transport hardware event
-> hci_rfkill_set_hw_state()
-> rfkill hardware state
-> hdev->rfkill_block work
-> HCI_RFKILLED state and device power handling
For Intel PCIe controllers, the series adds GP2 and HW_RFKILL MSI-X
handling, IPC_CONTROL doorbell synchronization, delayed state checking,
reset/shutdown/remove/suspend lifecycle handling, and notification of
the generic HCI rfkill layer.
Patches:
1. Bluetooth: Add HCI interface for HW rfkill events
2. Bluetooth: btintel_pcie: add HW rfkill interrupt/doorbell flow
Changes since v8:
Sashiko's automated review of v8 is available at:
https://sashiko.dev/#/patchset/20261015175036.1690457-1-aluvala.sai.teja%40intel.com
- A HW rfkill block while a user channel was open returned -EBUSY
and left HCI_RFKILLED clear, so the device could be powered on
once the user channel was closed. The set_block callback still
refuses soft rfkill for a user channel, but hdev->rfkill_block
now sets HCI_RFKILLED and only skips the power off.
The irq_lock deadlock concern was reviewed; no functional change was
made. The MSI-X handler is a threaded IRQ with no primary handler, so
irq_lock is never taken in hardirq context. A comment now documents
this.
Changes since v7:
Sashiko's automated review of v7 is available at:
https://sashiko.dev/#/patchset/20261015160215.1658054-1-aluvala.sai.teja%40intel.com
- rfkill_wait_state/rfkill_wait_done were set without a lock, so a
late GP2 acknowledgment of a timed-out doorbell could complete the
next doorbell before firmware handled it. Both are now set under
ipc_lock, which the GP2 handler also takes.
The shutdown concern was reviewed; no functional change was made.
Firmware accepts HCI reset regardless of the HW rfkill state, and an
assertion not yet seen by the worker is handled like one while HCI is
down: the worker still reports the block and handles the release. A
comment in btintel_pcie_shutdown() now documents this.
Changes since v6:
Sashiko's automated review of v6 is available at:
https://sashiko.dev/#/patchset/20261014172114.1426239-1-aluvala.sai.teja%40intel.com
- After shutdown acknowledged an assertion, IPC_CONTROL still held
that acknowledgment while the switch was asserted, so a later
worker run (e.g. after s2idle resume) treated it as a release and
reported an unblock. A release is now only handled once FUNC_CTRL
shows the switch released.
Not reported by Sashiko, also fixed in v7:
- The check added in v5 to keep HCI_RFKILLED set on a soft unblock
while HW rfkill is asserted used rfkill_blocked(), which inside
set_block still reports the soft block being removed. A soft
block followed by a soft unblock therefore left HCI_RFKILLED set
on every controller. The hardware state is now tracked in a new
HCI_HW_RFKILLED flag instead.
- hdev->rfkill_block also used rfkill_blocked(), so if it ran just
after a soft unblock it could block the device again while rfkill
reported it unblocked. It now combines HCI_HW_RFKILLED with
rfkill_soft_blocked().
- With CONFIG_RFKILL=n, hci_rfkill_set_hw_state() returned early,
so a HW rfkill assertion neither powered off nor blocked the
device. The hardware state is now enforced without an rfkill
device too.
Rebased onto the latest bluetooth-next, which added YoYo debug
support to btintel_pcie. The HW rfkill worker is now also disabled
in the new btintel_pcie_probe_cleanup(), which replaces the old
probe failure reset.
Changes since v5:
Sashiko's automated review of v5 is available at:
https://sashiko.dev/#/patchset/20261014165156.1413500-1-aluvala.sai.teja%40intel.com
The following concerns it raised have been addressed in v6:
- hci_rfkill_set_hw_state() checked the soft block state and cleared
HCI_RFKILLED without hdev->rfkill_lock, racing with a concurrent
soft block; it now runs under hdev->rfkill_lock.
- hdev->rfkill_block read rfkill_blocked() before taking
hdev->rfkill_lock, so a concurrent soft unblock could leave a stale
block applied; the state is now read under the lock.
- The worker's doorbell(false) and shutdown's doorbell(true) could
overlap and consume each other's GP2 acknowledgment; doorbell
transitions are now serialized with a new rfkill_doorbell_lock.
- HW_RFKILL is unmasked again during the setup firmware download
retry; the worker now does nothing until setup has completed, and
the setup retry path is left as in upstream.
Changes since v4:
Sashiko's automated review of v4 is available at:
https://sashiko.dev/#/patchset/20261014082650.1270679-1-aluvala.sai.teja%40intel.com
Two genuine issues were found in the flush_work() added to
hci_rfkill_set_hw_state() in v3, and are fixed in v5:
- Flushing hdev->rfkill_block under hdev->unregister_lock could
deadlock against hci_dev_close_sync() taking hdev->lock and a
mgmt command holding hdev->lock in hci_cmd_sync_submit().
- The flush did not order the work before rfkill core's async
event, so userspace could still see a stale HCI_RFKILLED flag.
The flush is removed. On release, HCI_RFKILLED is now cleared before
rfkill_set_hw_state() if the device is not soft blocked.
Not reported by Sashiko, also fixed in v5:
- A soft unblock cleared HCI_RFKILLED while the hardware switch was
still asserted, allowing the controller to be powered on under a
hard block; the flag is now kept set in that case.
- hci_unregister_dev() no longer takes hdev->unregister_lock around
rfkill_unregister(), which created a lock-order inversion with
rfkill_global_mutex. HCI_UNREGISTER, already set under that lock,
stops further hci_rfkill_set_hw_state() calls.
- btintel_pcie_shutdown() no longer masks HW_RFKILL, so HW rfkill
events are still reported to the stack while HCI is down.
The remaining concerns were reviewed; no further changes were made.
Changes since v3:
Sashiko's automated review of v3 is available at:
https://sashiko.dev/#/patchset/20261014061947.1238279-1-aluvala.sai.teja%40intel.com
Of the concerns raised, one was a genuine bug and is fixed in v4:
- A masked HW_RFKILL MSI-X cause still latches, so an unrelated
interrupt during remove could requeue the HW rfkill worker after
it was canceled, leading to a use-after-free. The handler now only
queues the worker while HW_RFKILL is unmasked, and remove and the
probe failure path disable the worker instead of canceling it.
The remaining concerns were reviewed; no further changes were made.
Changes since v2:
Sashiko's automated review of v2 is available at:
https://sashiko.dev/#/patchset/cover.1790905146.git.aluvala.sai.teja%40intel.com
The following genuine concerns it raised against this series have been
addressed:
- hci_rfkill_set_hw_state() could race with hci_unregister_dev() and
let hdev->rfkill_block run after hdev->rfkill was destroyed; the
work is now scheduled while still holding hdev->unregister_lock.
- The asynchronous hardware rfkill work item and the rfkill core's
synchronous set_block callback could race on HCI_RFKILLED and
controller power state; both paths are now serialized with a new
hdev->rfkill_lock.
- The HW rfkill worker could dereference data->hdev before probe had
published it; an early NULL check was added.
- The worker and HW_RFKILL MSI-X cause could still run or fire during
shutdown, reset, probe failure, remove, and suspend, racing with
those lifecycle paths; the cause is now masked and the worker
drained before each of them, and unmasked again after setup/resume.
- A hardware rfkill release during suspend was not reported after
resume because the initial state check unconditionally suppressed
the first "off" result; it is now only suppressed when rfkill was
not previously asserted.
- GP2 completion could be falsely triggered by a stale interrupt
that happened to match the current register state; the firmware
acknowledgment is now checked against the specific requested
transition.
Not reported by Sashiko, but found during this review and also fixed:
- hci_rfkill_set_hw_state() now flushes hdev->rfkill_block before
returning, closing a race where a userspace power-on could see a
stale HCI_RFKILLED flag.
- btintel_pcie_shutdown() keeps HW_RFKILL monitoring enabled when
shutdown is itself caused by an active assertion, so a later
de-assertion can still be detected.
The rfkill registration-failure path was also reviewed and confirmed
safe (hdev->rfkill is only published after hci_register_dev()
succeeds).
Pre-existing issues flagged by Sashiko that are unrelated to this
series (a workqueue leak on an existing probe failure path, and an
existing hci_unregister_dev() resource-ordering concern) were left
unchanged, as requested.
v2 also dropped the Bluetooth-specific debugfs test hook, the redundant
hdev->rfkill pointer-clear patch, and the separate RFKILL-core helper
dependency that were present in v1.
The series has been prepared against bluetooth-next/master.
Sai Teja Aluvala (2):
Bluetooth: Add HCI interface for HW rfkill events
Bluetooth: btintel_pcie: add HW rfkill interrupt/doorbell flow
drivers/bluetooth/btintel_pcie.c | 224 ++++++++++++++++++++++++++++++-
drivers/bluetooth/btintel_pcie.h | 33 +++++
include/net/bluetooth/hci.h | 2 +
include/net/bluetooth/hci_core.h | 4 +
net/bluetooth/hci_core.c | 97 ++++++++++++-
5 files changed, 348 insertions(+), 12 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v9 1/2] Bluetooth: Add HCI interface for HW rfkill events
2026-10-15 18:18 [PATCH v9 0/2] Bluetooth: add hardware rfkill handling Sai Teja Aluvala
@ 2026-10-15 18:18 ` Sai Teja Aluvala
2026-10-15 20:21 ` Bluetooth: add hardware rfkill handling bluez.test.bot
2026-10-15 18:18 ` [PATCH v9 2/2] Bluetooth: btintel_pcie: add HW rfkill interrupt/doorbell flow Sai Teja Aluvala
1 sibling, 1 reply; 7+ messages in thread
From: Sai Teja Aluvala @ 2026-10-15 18:18 UTC (permalink / raw)
To: Luiz Augusto von Dentz, linux-bluetooth
Cc: kiran.k, chethan.tumkur.narayan, Sai Teja Aluvala
Add hci_rfkill_set_hw_state() so that transport drivers can report
hardware rfkill switch changes to the HCI stack through the same
path used for software rfkill.
hci_rfkill_set_hw_state() checks HCI_UNREGISTER under
hdev->unregister_lock and schedules a new hdev->rfkill_block work
item while still holding that lock, so hci_unregister_dev() cannot
destroy hdev->rfkill before the work runs. The hardware state is
enforced even when no rfkill device exists (e.g. CONFIG_RFKILL=n).
On release, if not soft blocked, HCI_RFKILLED is cleared before
rfkill core sends its async event, so a userspace power-on triggered
by that event does not observe a stale flag.
A new hdev->rfkill_lock serializes hci_rfkill_set_hw_state(),
hdev->rfkill_block and the rfkill core's set_block callback. The
rfkill state is read and HCI_RFKILLED updated under that lock, so a
hardware rfkill event and a concurrent userspace soft block/unblock
cannot leave a stale state applied. The reported hardware state is
tracked in a new HCI_HW_RFKILLED flag, and a soft unblock keeps
HCI_RFKILLED set while it is set, so the controller cannot be
powered on under a hard block. hdev->rfkill_block combines that flag
with rfkill_soft_blocked(). rfkill_blocked() is not used, as around
set_block it still reports the soft block being removed.
While a user channel is open, the set_block callback still refuses
soft rfkill with -EBUSY. A HW rfkill block cannot be refused, so
hdev->rfkill_block sets HCI_RFKILLED but leaves the device up for the
user channel; once it is closed, the flag blocks the next power on.
Signed-off-by: Sai Teja Aluvala <aluvala.sai.teja@intel.com>
---
include/net/bluetooth/hci.h | 2 +
include/net/bluetooth/hci_core.h | 4 ++
net/bluetooth/hci_core.c | 97 ++++++++++++++++++++++++++++++--
3 files changed, 97 insertions(+), 6 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 9dc714d9145e..f1217ced2831 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -473,6 +473,8 @@ enum {
HCI_MESH,
HCI_MESH_SENDING,
+ HCI_HW_RFKILLED,
+
__HCI_NUM_FLAGS,
};
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa04dec45153..8f32c860d492 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -503,6 +503,7 @@ struct hci_dev {
struct list_head cmd_sync_work_list;
struct mutex cmd_sync_work_lock;
struct mutex unregister_lock;
+ struct mutex rfkill_lock;
struct work_struct cmd_sync_cancel_work;
struct work_struct reenable_adv_work;
@@ -586,6 +587,7 @@ struct hci_dev {
struct device dev;
struct rfkill *rfkill;
+ struct work_struct rfkill_block;
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
hci_conn_flags_t conn_flags;
@@ -1822,6 +1824,8 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, u8 src_type);
struct hci_dev *hci_alloc_dev_priv(int sizeof_priv);
+void hci_rfkill_set_hw_state(struct hci_dev *hdev, bool blocked);
+
static inline struct hci_dev *hci_alloc_dev(void)
{
return hci_alloc_dev_priv(0);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 209244fd9391..87ea117f8d9a 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -863,15 +863,13 @@ static int hci_dev_do_poweroff(struct hci_dev *hdev)
return err;
}
-static int hci_rfkill_set_block(void *data, bool blocked)
+static int __hci_rfkill_set_block(struct hci_dev *hdev, bool blocked)
{
- struct hci_dev *hdev = data;
int err;
- BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
+ lockdep_assert_held(&hdev->rfkill_lock);
- if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
- return -EBUSY;
+ BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
if (blocked == hci_dev_test_flag(hdev, HCI_RFKILLED))
return 0;
@@ -879,8 +877,12 @@ static int hci_rfkill_set_block(void *data, bool blocked)
if (blocked) {
hci_dev_set_flag(hdev, HCI_RFKILLED);
+ /* Leave a user channel up; it closes the device on release
+ * and HCI_RFKILLED then blocks the next power on.
+ */
if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
- !hci_dev_test_flag(hdev, HCI_CONFIG)) {
+ !hci_dev_test_flag(hdev, HCI_CONFIG) &&
+ !hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
err = hci_dev_do_poweroff(hdev);
if (err) {
bt_dev_err(hdev, "Error when powering off device on rfkill (%d)",
@@ -894,12 +896,92 @@ static int hci_rfkill_set_block(void *data, bool blocked)
}
}
} else {
+ /* Stay blocked while HW rfkill is still asserted. Do not use
+ * rfkill_blocked() here: inside set_block it still reports
+ * the soft block being removed.
+ */
+ if (hci_dev_test_flag(hdev, HCI_HW_RFKILLED))
+ return 0;
+
hci_dev_clear_flag(hdev, HCI_RFKILLED);
}
return 0;
}
+static int hci_rfkill_set_block(void *data, bool blocked)
+{
+ struct hci_dev *hdev = data;
+ int err;
+
+ mutex_lock(&hdev->rfkill_lock);
+ /* A user channel owns the device, so refuse soft rfkill. HW rfkill
+ * cannot be refused and is applied by hdev->rfkill_block instead.
+ */
+ if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
+ err = -EBUSY;
+ else
+ err = __hci_rfkill_set_block(hdev, blocked);
+ mutex_unlock(&hdev->rfkill_lock);
+
+ return err;
+}
+
+/**
+ * hci_rfkill_set_hw_state - report a hardware rfkill switch change
+ * @hdev: HCI device
+ * @blocked: true if the hardware switch is asserted
+ *
+ * May sleep. Must not be called with hdev->req_lock held, as a block
+ * powers the device off under hdev->rfkill_lock.
+ */
+void hci_rfkill_set_hw_state(struct hci_dev *hdev, bool blocked)
+{
+ mutex_lock(&hdev->rfkill_lock);
+ mutex_lock(&hdev->unregister_lock);
+ if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ goto unlock;
+
+ if (blocked)
+ hci_dev_set_flag(hdev, HCI_HW_RFKILLED);
+ else
+ hci_dev_clear_flag(hdev, HCI_HW_RFKILLED);
+
+ if (!IS_ERR_OR_NULL(hdev->rfkill)) {
+ /* Clear HCI_RFKILLED before rfkill core notifies userspace,
+ * so a power-on triggered by the unblock event does not see
+ * a stale flag.
+ */
+ if (!blocked && !rfkill_soft_blocked(hdev->rfkill))
+ hci_dev_clear_flag(hdev, HCI_RFKILLED);
+
+ rfkill_set_hw_state(hdev->rfkill, blocked);
+ }
+
+ /* Enforce the hardware state even without an rfkill device */
+ schedule_work(&hdev->rfkill_block);
+unlock:
+ mutex_unlock(&hdev->unregister_lock);
+ mutex_unlock(&hdev->rfkill_lock);
+}
+EXPORT_SYMBOL(hci_rfkill_set_hw_state);
+
+static void hci_rfkill_block_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev, rfkill_block);
+ bool blocked;
+
+ /* Use only state settled under rfkill_lock. rfkill_blocked() may
+ * still report a soft block that set_block has just removed.
+ */
+ mutex_lock(&hdev->rfkill_lock);
+ blocked = hci_dev_test_flag(hdev, HCI_HW_RFKILLED) ||
+ (!IS_ERR_OR_NULL(hdev->rfkill) &&
+ rfkill_soft_blocked(hdev->rfkill));
+ __hci_rfkill_set_block(hdev, blocked);
+ mutex_unlock(&hdev->rfkill_lock);
+}
+
static const struct rfkill_ops hci_rfkill_ops = {
.set_block = hci_rfkill_set_block,
};
@@ -2495,6 +2577,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
mutex_init(&hdev->req_lock);
mutex_init(&hdev->mgmt_pending_lock);
mutex_init(&hdev->remote_oob_lock);
+ mutex_init(&hdev->rfkill_lock);
ida_init(&hdev->unset_handle_ida);
@@ -2518,6 +2601,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
INIT_LIST_HEAD(&hdev->monitored_devices);
INIT_LIST_HEAD(&hdev->local_codecs);
+ INIT_WORK(&hdev->rfkill_block, hci_rfkill_block_work);
INIT_WORK(&hdev->rx_work, hci_rx_work);
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
INIT_WORK(&hdev->tx_work, hci_tx_work);
@@ -2683,6 +2767,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
disable_work_sync(&hdev->tx_work);
disable_work_sync(&hdev->power_on);
disable_work_sync(&hdev->error_reset);
+ disable_work_sync(&hdev->rfkill_block);
disable_delayed_work_sync(&hdev->cmd_timer);
disable_delayed_work_sync(&hdev->ncmd_timer);
hci_devcd_shutdown(hdev);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v9 2/2] Bluetooth: btintel_pcie: add HW rfkill interrupt/doorbell flow
2026-10-15 18:18 [PATCH v9 0/2] Bluetooth: add hardware rfkill handling Sai Teja Aluvala
2026-10-15 18:18 ` [PATCH v9 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
@ 2026-10-15 18:18 ` Sai Teja Aluvala
1 sibling, 0 replies; 7+ messages in thread
From: Sai Teja Aluvala @ 2026-10-15 18:18 UTC (permalink / raw)
To: Luiz Augusto von Dentz, linux-bluetooth
Cc: kiran.k, chethan.tumkur.narayan, Sai Teja Aluvala
Add host-driven HW rfkill handling for Intel PCIe Bluetooth
controllers and report state changes to the Bluetooth stack through
hci_rfkill_set_hw_state(). The GP2 and HW_RFKILL MSI-X hardware
causes are enabled so that the host can detect a HW rfkill assertion
and hand its acknowledgment back to firmware.
On a HW_RFKILL interrupt, a delayed worker reads the FUNC_CTRL and
IPC_CONTROL registers to determine whether the switch is asserted or
released. On assertion, the worker marks HW rfkill as active and
reports a blocked state directly. On release, it clears the
IPC_CONTROL HW rfkill doorbell bit, waits for the firmware's GP2
acknowledgment that matches the requested transition, and reports
an unblocked state to the stack only once that acknowledgment
arrives. A release is only handled once FUNC_CTRL shows the switch
released, as IPC_CONTROL may still hold the acknowledgment sent by
shutdown while the switch is asserted. IPC_CONTROL register
read-modify-write updates and the wait state/done flags are
serialized with a new ipc_lock, which the GP2 handler also takes, so
a late acknowledgment of a timed-out doorbell cannot complete the
next one. A new rfkill_doorbell_lock serializes the doorbell
transitions of the worker and shutdown.
The worker does nothing until setup has completed, as firmware is
not ready for the handshake before then. After a successful setup
or resume, an initial state check is scheduled. That check reports
an already-asserted HW rfkill state, and also reports a release if
rfkill was asserted before suspend and cleared while the controller
was asleep; otherwise it is a no-op so it cannot suppress a later
genuine transition.
The HW_RFKILL MSI-X cause is masked before synchronizing interrupts
and canceling or draining the worker across reset, probe failure,
remove, and suspend, and is unmasked again after setup and resume
succeed. GP2 is left enabled throughout.
Monitoring stays enabled across shutdown, so HW rfkill events are
still reported while HCI is down. On an active assertion, shutdown
sets the doorbell bit and waits for the GP2 acknowledgment right
before the HCI reset, since firmware accepts no other command after
it. An assertion while HCI is down needs no doorbell, as the
controller is already off. Firmware accepts HCI reset in any HW
rfkill state, so shutdown relies on the cached state; an assertion
the worker has not seen yet is handled like one while HCI is down.
Since a masked cause still latches, the MSI-X handler only queues the
worker while HW_RFKILL is unmasked, and remove and probe failure
disable the worker so it cannot be requeued after data is freed.
Signed-off-by: Sai Teja Aluvala <aluvala.sai.teja@intel.com>
---
drivers/bluetooth/btintel_pcie.c | 224 ++++++++++++++++++++++++++++++-
drivers/bluetooth/btintel_pcie.h | 33 +++++
2 files changed, 251 insertions(+), 6 deletions(-)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index a280741fcb7e..b0f590f5e903 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2887,15 +2887,149 @@ static inline bool btintel_pcie_is_txackq_empty(struct btintel_pcie_data *data)
return data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] == data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
}
+static bool btintel_pcie_hw_rfkill_doorbell(struct btintel_pcie_data *data,
+ bool state)
+{
+ unsigned long flags;
+ u32 reg;
+ int err;
+
+ /* Set under ipc_lock so a late GP2 for a previous doorbell cannot
+ * complete this one.
+ */
+ spin_lock_irqsave(&data->ipc_lock, flags);
+ WRITE_ONCE(data->rfkill_wait_state, state);
+ WRITE_ONCE(data->rfkill_wait_done, false);
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
+ if (state)
+ reg |= BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL;
+ else
+ reg &= ~BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL;
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG, reg);
+ spin_unlock_irqrestore(&data->ipc_lock, flags);
+
+ err = wait_event_timeout(data->rfkill_wait_q,
+ READ_ONCE(data->rfkill_wait_done),
+ msecs_to_jiffies(BTINTEL_PCIE_HW_RFKILL_INTERRUPT_TIMEOUT));
+ if (!err) {
+ bt_dev_err(data->hdev, "Timeout (%u ms) on alive interrupt for RFKILL",
+ BTINTEL_PCIE_HW_RFKILL_INTERRUPT_TIMEOUT);
+ return false;
+ }
+
+ return true;
+}
+
+static void btintel_pcie_hw_rfkill_delay_work(struct work_struct *wk)
+{
+ struct btintel_pcie_data *data =
+ container_of(wk, struct btintel_pcie_data, hw_rfkill_work.work);
+ u32 func_ctrl, ipc_ctrl;
+ bool set_rfkill, first_check, released;
+
+ if (!data->hdev)
+ return;
+
+ /* Firmware is not ready for the rfkill handshake before setup
+ * completes; setup schedules its own state check afterwards.
+ */
+ if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
+ return;
+
+ /* Do not report an initial clear when rfkill was never asserted.
+ * After resume, however, a previously asserted rfkill must be
+ * cleared and reported to the HCI stack.
+ */
+ first_check = test_and_clear_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK,
+ &data->flags);
+
+ func_ctrl = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ ipc_ctrl = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
+
+ set_rfkill = !(func_ctrl & BTINTEL_PCIE_CSR_FUNC_CTRL_HW_RFKILL) &&
+ !(ipc_ctrl & BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL);
+ if (set_rfkill) {
+ bt_dev_dbg(data->hdev, "hw rfkill on");
+ set_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags);
+ hci_rfkill_set_hw_state(data->hdev, true);
+ } else {
+ bt_dev_dbg(data->hdev, "hw rfkill off");
+ /* Nothing changed at the initial post-setup check, so there
+ * is no transition to report.
+ */
+ if (first_check &&
+ !test_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags))
+ return;
+
+ /* Switch still asserted; IPC_CONTROL only holds the ack that
+ * shutdown sent, so this is not a release.
+ */
+ if (!(func_ctrl & BTINTEL_PCIE_CSR_FUNC_CTRL_HW_RFKILL))
+ return;
+
+ mutex_lock(&data->rfkill_doorbell_lock);
+ released = btintel_pcie_hw_rfkill_doorbell(data, false);
+ if (released)
+ clear_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags);
+ mutex_unlock(&data->rfkill_doorbell_lock);
+
+ if (released)
+ hci_rfkill_set_hw_state(data->hdev, false);
+ }
+}
+
+static void btintel_delayed_hw_rfkill(struct btintel_pcie_data *data,
+ bool delay)
+{
+ mod_delayed_work(system_wq, &data->hw_rfkill_work,
+ delay ? msecs_to_jiffies(BTINTEL_PCIE_HW_RFKILL_DELAY) : 0);
+}
+
+static void btintel_pcie_msix_gp2_handler(struct btintel_pcie_data *data)
+{
+ unsigned long flags;
+ u32 reg, boot_stage_reg;
+ bool state, done;
+
+ spin_lock_irqsave(&data->ipc_lock, flags);
+ boot_stage_reg = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG);
+ state = READ_ONCE(data->rfkill_wait_state);
+
+ if (state &&
+ (reg & BTINTEL_PCIE_CSR_IPC_STATUS_HW_RFKILL) &&
+ (boot_stage_reg & BTINTEL_PCIE_CSR_BOOT_STAGE_HW_RFKILL_STATE)) {
+ bt_dev_dbg(data->hdev,
+ "firmware done handling HW RF-KILL SET state update");
+ WRITE_ONCE(data->rfkill_wait_done, true);
+ }
+
+ if (!state &&
+ !(reg & BTINTEL_PCIE_CSR_IPC_STATUS_HW_RFKILL) &&
+ !(boot_stage_reg & BTINTEL_PCIE_CSR_BOOT_STAGE_HW_RFKILL_STATE)) {
+ bt_dev_dbg(data->hdev,
+ "firmware done handling HW RF-KILL CLEAR state update");
+ WRITE_ONCE(data->rfkill_wait_done, true);
+ }
+
+ done = READ_ONCE(data->rfkill_wait_done);
+ spin_unlock_irqrestore(&data->ipc_lock, flags);
+
+ if (done)
+ wake_up(&data->rfkill_wait_q);
+}
+
static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
{
struct msix_entry *entry = dev_id;
struct btintel_pcie_data *data = btintel_pcie_get_data(entry);
- u32 intr_fh, intr_hw;
+ u32 intr_fh, intr_hw, hw_mask;
spin_lock(&data->irq_lock);
intr_fh = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES);
intr_hw = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES);
+ hw_mask = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK);
/* Clear causes registers to avoid being handling the same cause */
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES, intr_fh);
@@ -2917,6 +3051,8 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1)
btintel_pcie_msix_gp1_handler(data);
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP2)
+ btintel_pcie_msix_gp2_handler(data);
/* For TX */
if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0) {
@@ -2935,6 +3071,11 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG)
btintel_pcie_msix_fw_trigger_handler(data);
+ /* A masked cause still latches; ignore it once masked. */
+ if ((intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL) &&
+ !(hw_mask & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL))
+ btintel_delayed_hw_rfkill(data, false);
+
/* This interrupt is triggered by the firmware after updating
* boot_stage register and image_response register
*/
@@ -3012,8 +3153,10 @@ static struct btintel_pcie_causes_list causes_list[] = {
{ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x00 },
{ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP2, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x22 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x25 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x27 },
};
/* This function configures the interrupt masks for both HW_INT_CAUSES and
@@ -3535,6 +3678,25 @@ static void btintel_pcie_disable_interrupts(struct btintel_pcie_data *data)
spin_unlock(&data->irq_lock);
}
+/* irq_lock is never taken in hardirq context: the MSI-X handler is a
+ * threaded IRQ with no primary handler, so plain spin_lock() is enough.
+ */
+static void btintel_pcie_mask_hw_rfkill(struct btintel_pcie_data *data)
+{
+ spin_lock(&data->irq_lock);
+ btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK,
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL);
+ spin_unlock(&data->irq_lock);
+}
+
+static void btintel_pcie_unmask_hw_rfkill(struct btintel_pcie_data *data)
+{
+ spin_lock(&data->irq_lock);
+ btintel_pcie_clr_reg_bits(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK,
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL);
+ spin_unlock(&data->irq_lock);
+}
+
static void btintel_pcie_enable_interrupts(struct btintel_pcie_data *data)
{
spin_lock(&data->irq_lock);
@@ -3560,6 +3722,7 @@ static void btintel_pcie_probe_cleanup(struct btintel_pcie_data *data)
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
disable_work_sync(&data->mbox_work);
+ disable_delayed_work_sync(&data->hw_rfkill_work);
flush_work(&data->rx_work);
@@ -3761,8 +3924,12 @@ static int btintel_pcie_setup(struct hci_dev *hdev)
btintel_pcie_start_rx(data);
}
- if (!err)
+ if (!err) {
set_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags);
+ set_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK, &data->flags);
+ btintel_pcie_unmask_hw_rfkill(data);
+ btintel_delayed_hw_rfkill(data, true);
+ }
return err;
}
@@ -3990,18 +4157,20 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
if (!data)
goto out;
+ btintel_pcie_mask_hw_rfkill(data);
+ btintel_pcie_synchronize_irqs(data);
+ disable_delayed_work_sync(&data->hw_rfkill_work);
btintel_pcie_disable_interrupts(data);
btintel_pcie_synchronize_irqs(data);
flush_work(&data->rx_work);
- /* Drain any in-flight dump workers and block new ones across reset.
- * Safe from self-deadlock: they all run on a separate wq.
+ /* Drain any in-flight dump/rfkill workers and block new ones across
+ * reset. Safe from self-deadlock: they all run on a separate wq.
*/
disable_work_sync(&data->coredump_work);
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
disable_work_sync(&data->mbox_work);
-
bt_dev_dbg(data->hdev, "Release bluetooth interface");
/* Both reset paths follow the same contract: on success they
@@ -4026,6 +4195,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
enable_work(&data->hwexp_work);
enable_work(&data->fwtrigger_work);
enable_work(&data->mbox_work);
+ enable_delayed_work(&data->hw_rfkill_work);
}
out:
@@ -4216,6 +4386,27 @@ static struct hci_drv btintel_pcie_hci_drv = {
.specific_handlers = btintel_pcie_hci_drv_specific_handlers,
};
+static int btintel_pcie_shutdown(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+ /* Keep HW rfkill monitoring while HCI is down. On an active
+ * assertion, ack it right before the mandatory HCI reset.
+ *
+ * The cached flag is enough: firmware accepts HCI reset in any
+ * HW rfkill state, also in the middle of the doorbell flow. An
+ * assertion the worker has not seen yet is then handled like one
+ * while HCI is down; the worker still reports the block and
+ * handles the release.
+ */
+ mutex_lock(&data->rfkill_doorbell_lock);
+ if (test_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags))
+ btintel_pcie_hw_rfkill_doorbell(data, true);
+ mutex_unlock(&data->rfkill_doorbell_lock);
+
+ return btintel_shutdown_combined(hdev);
+}
+
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
{
int err;
@@ -4235,7 +4426,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
hdev->close = btintel_pcie_close;
hdev->send = btintel_pcie_send_frame;
hdev->setup = btintel_pcie_setup;
- hdev->shutdown = btintel_shutdown_combined;
+ hdev->shutdown = btintel_pcie_shutdown;
hdev->hw_error = btintel_pcie_hw_error;
hdev->set_diag = btintel_set_diag;
hdev->set_bdaddr = btintel_set_bdaddr;
@@ -4277,6 +4468,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
spin_lock_init(&data->irq_lock);
spin_lock_init(&data->hci_rx_lock);
+ spin_lock_init(&data->ipc_lock);
+ mutex_init(&data->rfkill_doorbell_lock);
init_waitqueue_head(&data->gp0_wait_q);
data->gp0_received = false;
@@ -4284,6 +4477,9 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
init_waitqueue_head(&data->tx_wait_q);
data->tx_wait_done = false;
+ init_waitqueue_head(&data->rfkill_wait_q);
+ data->rfkill_wait_done = false;
+
init_waitqueue_head(&data->mbox_parse_wait_q);
data->workqueue = alloc_ordered_workqueue(KBUILD_MODNAME, WQ_HIGHPRI);
@@ -4298,6 +4494,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
skb_queue_head_init(&data->rx_skb_q);
INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
+ INIT_DELAYED_WORK(&data->hw_rfkill_work,
+ btintel_pcie_hw_rfkill_delay_work);
INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
@@ -4405,6 +4603,10 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
if (current_work() != &data->reset_work)
disable_work_sync(&data->reset_work);
+ btintel_pcie_mask_hw_rfkill(data);
+ btintel_pcie_synchronize_irqs(data);
+ disable_delayed_work_sync(&data->hw_rfkill_work);
+
btintel_pcie_yoyo_cleanup(&data->yoyo);
btintel_pcie_disable_interrupts(data);
@@ -4549,6 +4751,10 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
data = pci_get_drvdata(pdev);
+ btintel_pcie_mask_hw_rfkill(data);
+ btintel_pcie_synchronize_irqs(data);
+ cancel_delayed_work_sync(&data->hw_rfkill_work);
+
/* 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)
@@ -4562,6 +4768,9 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
err = btintel_pcie_set_dxstate(data, dxstate);
if (err) {
+ btintel_pcie_unmask_hw_rfkill(data);
+ set_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK, &data->flags);
+ btintel_delayed_hw_rfkill(data, true);
bt_dev_err(data->hdev, "Failed to set dxstate:%u (%d)",
dxstate, err);
return err;
@@ -4616,6 +4825,9 @@ static int btintel_pcie_resume_event(struct device *dev, pm_message_t mesg)
if (err) {
bt_dev_err(data->hdev, "Failed to set D0 state (%d)", err);
} else {
+ btintel_pcie_unmask_hw_rfkill(data);
+ set_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK, &data->flags);
+ btintel_delayed_hw_rfkill(data, true);
bt_dev_dbg(data->hdev,
"device entered into d0 state from d3 in %lld us",
ktime_to_us(ktime_get() - start));
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 35c6f271eee5..fff31ac8f87a 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -35,6 +35,7 @@
#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT (BIT(6))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT (BIT(7))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS (BIT(20))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_HW_RFKILL (BIT(27))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ (BIT(21))
@@ -42,6 +43,12 @@
#define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON (BIT(29))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET (BIT(31))
+/* BTINTEL PCIE CSR IPC Control Register */
+#define BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL (BIT(7))
+
+/* BTINTEL PCIE CSR IPC Status Register */
+#define BTINTEL_PCIE_CSR_IPC_STATUS_HW_RFKILL (BIT(7))
+
/* Value for BTINTEL_PCIE_CSR_BOOT_STAGE register */
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM (BIT(0))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_IML (BIT(1))
@@ -55,6 +62,7 @@
#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_BOOT_STAGE_HW_RFKILL_STATE (BIT(25))
#define BTINTEL_PCIE_CSR_DOORBELL_MBOX_READ_CONFIRM (BIT(4))
@@ -137,8 +145,10 @@ enum msix_fh_int_causes {
enum msix_hw_int_causes {
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP2 = BIT(2), /* cause 34 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG = BIT(5), /* cause 37 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL = BIT(7), /* cause 39 */
};
/* PCIe device states
@@ -157,6 +167,8 @@ enum {
BTINTEL_PCIE_COREDUMP_INPROGRESS,
BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
+ BTINTEL_PCIE_HWRFKILL_ON,
+ BTINTEL_PCIE_HW_RFKILL_STATE_CHECK,
BTINTEL_PCIE_SETUP_DONE,
BTINTEL_PCIE_MAIL_BOX_INTR,
BTINTEL_PCIE_MBOX_PARSE_PENDING,
@@ -217,6 +229,12 @@ enum btintel_pcie_mbox_msg {
#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3
+/* HW rfkill interrupt timeout */
+#define BTINTEL_PCIE_HW_RFKILL_INTERRUPT_TIMEOUT 200
+
+/* HW rfkill check timeout */
+#define BTINTEL_PCIE_HW_RFKILL_DELAY 1000
+
/* The number of descriptors in TX queues */
#define BTINTEL_PCIE_TX_DESCS_COUNT 32
@@ -688,6 +706,7 @@ struct btintel_pcie_ini_dump_info {
* @flags: driver state
* @irq_lock: spinlock for MSI-X
* @hci_rx_lock: spinlock for HCI RX flow
+ * @ipc_lock: protects IPC control writes and the rfkill wait state
* @base_addr: pci base address (from BAR)
* @msix_entries: array of MSI-X entries
* @msix_enabled: true if MSI-X is enabled;
@@ -704,6 +723,11 @@ struct btintel_pcie_ini_dump_info {
* @gp0_wait_q: wait_q for gp0 interrupt
* @tx_wait_done: condition for tx interrupt
* @tx_wait_q: wait_q for tx interrupt
+ * @rfkill_wait_done: condition for HW rfkill GP2 interrupt
+ * @rfkill_wait_state: requested HW rfkill transition (set or clear)
+ * being waited on, used to validate the GP2 acknowledgment
+ * @rfkill_wait_q: wait_q for HW rfkill GP2 interrupt
+ * @rfkill_doorbell_lock: serializes HW rfkill doorbell transitions
* @workqueue: workqueue for RX work
* @rx_skb_q: SKB queue for RX packet
* @rx_work: RX work struct to process the RX packet in @rx_skb_q
@@ -731,6 +755,8 @@ struct btintel_pcie_data {
spinlock_t irq_lock;
/* lock to serialize rx events */
spinlock_t hci_rx_lock;
+ /* lock to serialize IPC control register updates */
+ spinlock_t ipc_lock;
void __iomem *base_addr;
@@ -757,9 +783,16 @@ struct btintel_pcie_data {
bool tx_wait_done;
wait_queue_head_t tx_wait_q;
+ bool rfkill_wait_done;
+ bool rfkill_wait_state;
+ wait_queue_head_t rfkill_wait_q;
+ /* serializes HW rfkill doorbell transitions */
+ struct mutex rfkill_doorbell_lock;
+
struct workqueue_struct *workqueue;
struct sk_buff_head rx_skb_q;
struct work_struct rx_work;
+ struct delayed_work hw_rfkill_work;
struct work_struct reset_work;
struct workqueue_struct *dump_workqueue;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* RE: Bluetooth: add hardware rfkill handling
2026-10-15 18:18 ` [PATCH v9 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
@ 2026-10-15 20:21 ` bluez.test.bot
0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-10-15 20:21 UTC (permalink / raw)
To: linux-bluetooth, aluvala.sai.teja
[-- Attachment #1: Type: text/plain, Size: 1605 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1186713/
---Test result---
Test Summary:
CheckPatch PASS 4.01 seconds
VerifyFixes PASS 0.12 seconds
VerifySignedoff PASS 0.24 seconds
GitLint PASS 0.87 seconds
SubjectPrefix PASS 0.26 seconds
BuildKernel PASS 29.73 seconds
CheckAllWarning PASS 34.04 seconds
CheckSparse PASS 36.84 seconds
BuildKernel32 PASS 29.69 seconds
CheckKernelLLVM PASS 37.17 seconds
TestRunnerSetup PASS 707.49 seconds
TestRunner_l2cap-tester PASS 12.57 seconds
TestRunner_iso-tester PASS 10.66 seconds
TestRunner_bnep-tester PASS 3.11 seconds
TestRunner_mgmt-tester PASS 87.68 seconds
TestRunner_rfcomm-tester PASS 4.49 seconds
TestRunner_sco-tester PASS 5.62 seconds
TestRunner_ioctl-tester PASS 4.66 seconds
TestRunner_mesh-tester PASS 5.51 seconds
TestRunner_smp-tester PASS 4.34 seconds
TestRunner_userchan-tester PASS 3.18 seconds
TestRunner_6lowpan-tester PASS 4.42 seconds
IncrementalBuild PASS 30.18 seconds
https://github.com/bluez/bluetooth-next/pull/953
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-15 20:21 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15 18:18 [PATCH v9 0/2] Bluetooth: add hardware rfkill handling Sai Teja Aluvala
2026-10-15 18:18 ` [PATCH v9 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
2026-10-15 20:21 ` Bluetooth: add hardware rfkill handling bluez.test.bot
2026-10-15 18:18 ` [PATCH v9 2/2] Bluetooth: btintel_pcie: add HW rfkill interrupt/doorbell flow Sai Teja Aluvala
-- strict thread matches above, loose matches on Subject: below --
2026-10-14 8:26 [PATCH v4 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
2026-10-14 12:52 ` Bluetooth: add hardware rfkill handling bluez.test.bot
2026-10-14 6:19 [PATCH v3 1/2] Bluetooth: Add HCI interface for HW rfkill events Sai Teja Aluvala
2026-10-14 7:09 ` Bluetooth: add hardware rfkill handling bluez.test.bot
2026-10-02 1:43 [PATCH v2 1/2] Bluetooth: Add interface to allow stack to communicate hw rfkill events Sai Teja Aluvala
2026-10-02 3:14 ` Bluetooth: add hardware rfkill handling 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