summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie15
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/bluetooth/btintel_pcie.c38
3 files changed, 54 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie b/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
new file mode 100644
index 000000000000..cceec6ac96bc
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
@@ -0,0 +1,15 @@
+What: /sys/bus/pci/devices/<BDF>/vendor_reset
+Date: 22-Jul-2026
+KernelVersion: 6.17
+Contact: linux-bluetooth@vger.kernel.org
+Description: This read-write attribute allows userspace to trigger a
+ Product Level Device Reset (PLDR) on Intel PCIe Bluetooth
+ controllers. Reading the attribute displays the supported
+ reset type. Writing integer 0 triggers PLDR. Any other
+ input is rejected with -EINVAL.
+
+ PLDR resets the entire on-chip platform shared between
+ Bluetooth and WiFi. This means any driver attached to
+ the WiFi device that shares hardware with this Bluetooth
+ device will be released, the platform will be reset, and
+ both the Bluetooth and WiFi devices will be re-probed.
diff --git a/MAINTAINERS b/MAINTAINERS
index 08e43bc09735..891c064a881b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4719,6 +4719,7 @@ S: Supported
W: http://www.bluez.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
+F: Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
F: Documentation/devicetree/bindings/net/bluetooth/
F: drivers/bluetooth/
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index ef42b8d11d4d..005c77a4f5eb 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2790,6 +2790,43 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
+static ssize_t vendor_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data = pci_get_drvdata(pdev);
+
+ if (!data || !data->hdev)
+ return -ENODEV;
+
+ if (kstrtouint(buf, 10, &val) || val != 0) {
+ bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
+ return -EINVAL;
+ }
+
+ bt_dev_info(data->hdev, "PLDR triggered via sysfs");
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
+
+ return count;
+}
+
+static ssize_t vendor_reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0 - PLDR\n");
+}
+
+static DEVICE_ATTR_RW(vendor_reset);
+
+static struct attribute *btintel_pcie_attrs[] = {
+ &dev_attr_vendor_reset.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(btintel_pcie);
+
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
{
struct btintel_pcie_dev_recovery *rec;
@@ -3250,6 +3287,7 @@ static struct pci_driver btintel_pcie_driver = {
.probe = btintel_pcie_probe,
.remove = btintel_pcie_remove,
.driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
+ .dev_groups = btintel_pcie_groups,
#ifdef CONFIG_DEV_COREDUMP
.driver.coredump = btintel_pcie_coredump
#endif