Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/2222582/?format=api
{ "id": 2222582, "url": "http://patchwork.ozlabs.org/api/1.2/patches/2222582/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260413042041.1318723-1-chandrashekar.devegowda@intel.com/", "project": { "id": 28, "url": "http://patchwork.ozlabs.org/api/1.2/projects/28/?format=api", "name": "Linux PCI development", "link_name": "linux-pci", "list_id": "linux-pci.vger.kernel.org", "list_email": "linux-pci@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260413042041.1318723-1-chandrashekar.devegowda@intel.com>", "list_archive_url": null, "date": "2026-04-13T04:20:40", "name": "[v3] Bluetooth: btintel_pcie: Support Product level reset", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "30941be25fc23f002040f1fe69f0695ac93501c1", "submitter": { "id": 90156, "url": "http://patchwork.ozlabs.org/api/1.2/people/90156/?format=api", "name": "Chandrashekar Devegowda", "email": "chandrashekar.devegowda@intel.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260413042041.1318723-1-chandrashekar.devegowda@intel.com/mbox/", "series": [ { "id": 499642, "url": "http://patchwork.ozlabs.org/api/1.2/series/499642/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/list/?series=499642", "date": "2026-04-13T04:20:40", "name": "[v3] Bluetooth: btintel_pcie: Support Product level reset", "version": 3, "mbox": "http://patchwork.ozlabs.org/series/499642/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2222582/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2222582/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-pci+bounces-52410-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-pci@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256\n header.s=Intel header.b=jqsHJqar;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.105.105.114; helo=tor.lore.kernel.org;\n envelope-from=linux-pci+bounces-52410-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com\n header.b=\"jqsHJqar\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=192.198.163.17", "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=intel.com", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=intel.com" ], "Received": [ "from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fvDpJ39Yvz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 13 Apr 2026 14:24:56 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 8DEA03056244\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 13 Apr 2026 04:21:32 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 23899342524;\n\tMon, 13 Apr 2026 04:21:28 +0000 (UTC)", "from mgamail.intel.com (mgamail.intel.com [192.198.163.17])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id D3FFC1FC101;\n\tMon, 13 Apr 2026 04:21:21 +0000 (UTC)", "from fmviesa005.fm.intel.com ([10.60.135.145])\n by fmvoesa111.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 12 Apr 2026 21:21:20 -0700", "from weba0957.iind.intel.com (HELO WEBA0932.iind.intel.com)\n ([10.224.186.34])\n by fmviesa005.fm.intel.com with ESMTP; 12 Apr 2026 21:21:18 -0700" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776054087; cv=none;\n b=UgCQAOSQMRj9x3l/g9PEHFaHSiwUnvMM0/Ba8+aqV40Xb2PRDZDyylwJvkD6MdU+85jjrSYtg1lT3z7IBBG+4p1TnhNYnXvHPQKEGjy2I3LsbC7rnMsBn7YB54WUgwunZmna8goxHH7dKm8LNGNqyE//I/8zjuXHk6JnTqm7UZY=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776054087; c=relaxed/simple;\n\tbh=UtN5VR7f7/sbMFgJdiW0S0QgBD6DLDOiO3rbJHn3Wxc=;\n\th=From:To:Cc:Subject:Date:Message-ID:MIME-Version;\n b=jKjFKlAr5Eu1/IE9mNAdNLd4rX/FBZavBlg0ydkDwPdMYW5ajNc/iK0WpITRdPrYRfCO6u+vJuXQHz6MBaHajEUGdUlnxH9UBPjPFX57j3zBtOHNUSnsnzsy7A+9oPRvzPHh9/SCG6iHXXq2kCqlbJVm1ZfttB9aawuwHMvo3Ws=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=intel.com;\n spf=pass smtp.mailfrom=intel.com;\n dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com\n header.b=jqsHJqar; arc=none smtp.client-ip=192.198.163.17", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1776054082; x=1807590082;\n h=from:to:cc:subject:date:message-id:mime-version:\n content-transfer-encoding;\n bh=UtN5VR7f7/sbMFgJdiW0S0QgBD6DLDOiO3rbJHn3Wxc=;\n b=jqsHJqarGwM8clyCmcWiopUXft81HwK4+skNYZK2x1vyzOIglvA4jneq\n 3zbwG4FzxW6ZtOgeHFqocb+BBAvpdbjDkfqXh9veX/vTyzkj3TCkZMZAx\n Axihu/VhKaqC0ZVNgCHFuo/q2srUMI+1ZDCCvmmXh1NMQgjWLCbuMjjE3\n ZhwOCjwcSy+03VepBh9UhB6PpdVkpyjSXMAib+Ndm0U5+AAZAKSdVTd8b\n 7LJbAlxZQLNYpLKubyCYvPvXOnZZlhRhxGjKwLv61feLJv0Mx7yxJnxqd\n WAlAeX9Bx/z2nW3epSNu4m+gi0djCczWl3Jon2AoeimnWOWa4qHPwHPVX\n w==;", "X-CSE-ConnectionGUID": [ "D7QrvodlQbeUwaACvgs9DQ==", "Qt62IfLjQtaPEtRFxNByoA==" ], "X-CSE-MsgGUID": [ "qI3cQM5OQUSvzYZZYSI3aQ==", "7lRhtuWETGCBZtuEsWAZnA==" ], "X-IronPort-AV": [ "E=McAfee;i=\"6800,10657,11757\"; a=\"76868676\"", "E=Sophos;i=\"6.23,176,1770624000\";\n d=\"scan'208\";a=\"76868676\"", "E=Sophos;i=\"6.23,176,1770624000\";\n d=\"scan'208\";a=\"234598292\"" ], "X-ExtLoop1": "1", "From": "Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>", "To": "linux-bluetooth@vger.kernel.org", "Cc": "linux-pci@vger.kernel.org,\n\tbhelgaas@google.com,\n\travishankar.srivatsa@intel.com,\n\tchethan.tumkur.narayan@intel.com,\n\tChandrashekar Devegowda <chandrashekar.devegowda@intel.com>", "Subject": "[PATCH v3] Bluetooth: btintel_pcie: Support Product level reset", "Date": "Mon, 13 Apr 2026 09:50:40 +0530", "Message-ID": "<20260413042041.1318723-1-chandrashekar.devegowda@intel.com>", "X-Mailer": "git-send-email 2.43.0", "Precedence": "bulk", "X-Mailing-List": "linux-pci@vger.kernel.org", "List-Id": "<linux-pci.vger.kernel.org>", "List-Subscribe": "<mailto:linux-pci+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-pci+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit" }, "content": "When driver encounters a TOP exception, ACPI methods will be called\nfor Product level reset since Wifi and BT share the same TOP. BT driver\nwill first reprobe the wifi driver and then reprobe BT.\n\nSigned-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>\n---\n\nNotes:\n v3: incorporated review comments\n - Embedded reset_work in btintel_pcie_data instead of dynamic\n btintel_pcie_removal allocation to fix use-after-free race\n between reset work and concurrent driver unbind\n - Added cancel_work_sync in btintel_pcie_remove() with\n current_work() guard to avoid deadlock in PLDR reprobe path\n - Removed struct btintel_pcie_removal\n - Renamed btintel_pcie_removal_work to btintel_pcie_reset_work\n v2: incorporated review comments\n\n drivers/bluetooth/btintel.c | 10 +-\n drivers/bluetooth/btintel.h | 7 ++\n drivers/bluetooth/btintel_pcie.c | 203 ++++++++++++++++++++++++++-----\n drivers/bluetooth/btintel_pcie.h | 7 ++\n 4 files changed, 192 insertions(+), 35 deletions(-)", "diff": "diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c\nindex dcaaa4ca02b9..5e9cac090bd8 100644\n--- a/drivers/bluetooth/btintel.c\n+++ b/drivers/bluetooth/btintel.c\n@@ -67,9 +67,10 @@ static struct {\n \tu32 fw_build_num;\n } coredump_info;\n \n-static const guid_t btintel_guid_dsm =\n+const guid_t btintel_guid_dsm =\n \tGUID_INIT(0xaa10f4e0, 0x81ac, 0x4233,\n \t\t 0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9);\n+EXPORT_SYMBOL_GPL(btintel_guid_dsm);\n \n int btintel_check_bdaddr(struct hci_dev *hdev)\n {\n@@ -2624,7 +2625,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver\n \tkfree_skb(skb);\n }\n \n-static int btintel_acpi_reset_method(struct hci_dev *hdev)\n+int btintel_acpi_reset_method(struct hci_dev *hdev)\n {\n \tint ret = 0;\n \tacpi_status status;\n@@ -2632,14 +2633,14 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev)\n \tstruct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };\n \n \tstatus = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), \"_PRR\", NULL, &buffer);\n-\tif (ACPI_FAILURE(status)) {\n+\tif (ACPI_FAILURE(status) || !buffer.pointer) {\n \t\tbt_dev_err(hdev, \"Failed to run _PRR method\");\n \t\tret = -ENODEV;\n \t\treturn ret;\n \t}\n \tp = buffer.pointer;\n \n-\tif (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) {\n+\tif (p->type != ACPI_TYPE_PACKAGE || p->package.count != 1) {\n \t\tbt_dev_err(hdev, \"Invalid arguments\");\n \t\tret = -EINVAL;\n \t\tgoto exit_on_error;\n@@ -2663,6 +2664,7 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev)\n \tkfree(buffer.pointer);\n \treturn ret;\n }\n+EXPORT_SYMBOL_GPL(btintel_acpi_reset_method);\n \n static void btintel_set_dsm_reset_method(struct hci_dev *hdev,\n \t\t\t\t\t struct intel_version_tlv *ver_tlv)\ndiff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h\nindex 0e9ca99aaaae..70d812ad36a2 100644\n--- a/drivers/bluetooth/btintel.h\n+++ b/drivers/bluetooth/btintel.h\n@@ -79,6 +79,8 @@ struct intel_tlv {\n #define BTINTEL_HWID_SCP2\t0x20\t/* Scorpius Peak2 - Nova Lake */\n #define BTINTEL_HWID_BZRIW\t0x22\t/* BlazarIW - Wildcat Lake */\n \n+extern const guid_t btintel_guid_dsm;\n+\n struct intel_version_tlv {\n \tu32\tcnvi_top;\n \tu32\tcnvr_top;\n@@ -289,6 +291,7 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,\n int btintel_shutdown_combined(struct hci_dev *hdev);\n void btintel_hw_error(struct hci_dev *hdev, u8 code);\n void btintel_print_fseq_info(struct hci_dev *hdev);\n+int btintel_acpi_reset_method(struct hci_dev *hdev);\n #else\n \n static inline int btintel_check_bdaddr(struct hci_dev *hdev)\n@@ -422,4 +425,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)\n static inline void btintel_print_fseq_info(struct hci_dev *hdev)\n {\n }\n+static inline int btintel_acpi_reset_method(struct hci_dev *hdev)\n+{\n+\treturn -ENODEV;\n+}\n #endif\ndiff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c\nindex 05f82bc3f0d7..df073873ec99 100644\n--- a/drivers/bluetooth/btintel_pcie.c\n+++ b/drivers/bluetooth/btintel_pcie.c\n@@ -15,6 +15,7 @@\n #include <linux/wait.h>\n #include <linux/delay.h>\n #include <linux/interrupt.h>\n+#include <linux/acpi.h>\n \n #include <linux/unaligned.h>\n #include <linux/devcoredump.h>\n@@ -100,6 +101,22 @@ enum {\n \tBTINTEL_PCIE_D3\n };\n \n+enum {\n+\tBTINTEL_PCIE_DSM_SET_RESET_TIMING = 1,\n+\tBTINTEL_PCIE_DSM_GET_RESET_TIMING = 2,\n+\tBTINTEL_PCIE_DSM_BT_PLDR_CONFIG = 3,\n+\tBTINTEL_PCIE_DSM_GET_RESET_TYPE = 4,\n+\tBTINTEL_PCIE_DSM_DYNAMIC_PLDR = 5,\n+\tBTINTEL_PCIE_DSM_GET_RESET_METHOD = 6,\n+\tBTINTEL_PCIE_DSM_SET_PLDR_DELAY = 7,\n+};\n+\n+enum btintel_dsm_internal_product_reset_mode {\n+\tBTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET\t= BIT(0),\n+\tBTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR\t\t= BIT(1),\n+\tBTINTEL_PCIE_DSM_PLDR_MODE_EN_BT_OFF_ON\t\t= BIT(2),\n+};\n+\n /* Structure for dbgc fragment buffer\n * @buf_addr_lsb: LSB of the buffer's physical address\n * @buf_addr_msb: MSB of the buffer's physical address\n@@ -126,11 +143,6 @@ struct btintel_pcie_dbgc_ctxt {\n \tstruct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];\n };\n \n-struct btintel_pcie_removal {\n-\tstruct pci_dev *pdev;\n-\tstruct work_struct work;\n-};\n-\n static LIST_HEAD(btintel_pcie_recovery_list);\n static DEFINE_SPINLOCK(btintel_pcie_recovery_lock);\n \n@@ -2254,21 +2266,132 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,\n }\n \n static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);\n+static void btintel_pcie_reset(struct hci_dev *hdev);\n \n-static void btintel_pcie_removal_work(struct work_struct *wk)\n+static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)\n {\n-\tstruct btintel_pcie_removal *removal =\n-\t\tcontainer_of(wk, struct btintel_pcie_removal, work);\n-\tstruct pci_dev *pdev = removal->pdev;\n-\tstruct btintel_pcie_data *data;\n+\tunion acpi_object *obj, argv4;\n+\tacpi_handle handle;\n+\tint ret;\n+\tstruct pldr_mode {\n+\t\t__le16\tcmd_type;\n+\t\t__le16\tcmd_payload;\n+\t} __packed;\n+\n+\t/* set 1 for _PRR mode\n+\t * Product Reset (PLDR Abort flow)\n+\t */\n+\tstatic const struct pldr_mode mode = {\n+\t\t.cmd_type = cpu_to_le16(1),\n+\t\t.cmd_payload = cpu_to_le16(BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET |\n+\t\t\t BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR),\n+\t};\n+\tstruct hci_dev *hdev = data->hdev;\n+\n+\thandle = ACPI_HANDLE(GET_HCIDEV_DEV(data->hdev));\n+\tif (!handle) {\n+\t\tbt_dev_err(data->hdev, \"No support for bluetooth device in ACPI firmware\");\n+\t\treturn -EACCES;\n+\t}\n+\n+\tif (!acpi_has_method(handle, \"_PRR\")) {\n+\t\tbt_dev_err(data->hdev, \"No support for _PRR ACPI method, cold boot\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\targv4.buffer.type = ACPI_TYPE_BUFFER;\n+\targv4.buffer.length = sizeof(mode);\n+\targv4.buffer.pointer = (void *)&mode;\n+\n+\tobj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,\n+\t\t\t\tBTINTEL_PCIE_DSM_DYNAMIC_PLDR, &argv4);\n+\tif (!obj) {\n+\t\tbt_dev_err(data->hdev, \"Failed to call dsm to set reset method\");\n+\t\treturn -EIO;\n+\t}\n+\tACPI_FREE(obj);\n+\n+\tpci_dev_lock(data->pdev);\n+\tpci_save_state(data->pdev);\n+\tret = btintel_acpi_reset_method(hdev);\n+\tif (ret)\n+\t\tbt_dev_err(data->hdev, \"ACPI _PRR reset failed (%d), PLDR incomplete\",\n+\t\t\t ret);\n+\tpci_restore_state(data->pdev);\n+\tpci_dev_unlock(data->pdev);\n+\treturn ret;\n+}\n+\n+static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data)\n+{\n+\tstruct pci_dev *pdev = data->pdev;\n+\tstruct pci_dev *wifi = NULL;\n+\tstruct pci_bus *bus;\n+\tint ret;\n+\t/* on integrated we have to look up by ID (same bus) */\n+\tstatic const struct pci_device_id wifi_device_ids[] = {\n+\t#define WIFI_DEV(_id) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, _id) }\n+\t\tWIFI_DEV(0xA840), /* LNL */\n+\t\tWIFI_DEV(0xE440), /* PTL-P */\n+\t\tWIFI_DEV(0xE340), /* PTL-H */\n+\t\tWIFI_DEV(0xD340), /* NVL-H */\n+\t\tWIFI_DEV(0x6E70), /* NVL-S */\n+\t\tWIFI_DEV(0x4D40), /* WCL */\n+\t\tWIFI_DEV(0xD240), /* RZL-H */\n+\t\tWIFI_DEV(0x6C40), /* RZL-M */\n+\t\t{}\n+\t};\n+\tstruct pci_dev *tmp = NULL;\n+\n+\tbus = pdev->bus;\n+\tif (!bus)\n+\t\treturn;\n+\n+\tlist_for_each_entry(tmp, &bus->devices, bus_list) {\n+\t\tif (pci_match_id(wifi_device_ids, tmp)) {\n+\t\t\twifi = pci_dev_get(tmp);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (wifi)\n+\t\tdevice_release_driver(&wifi->dev);\n+\n+\t/* Wi-Fi is fully unbound before the reset and fully reprobed after\n+\t * the normal PCI probe path handles all state setup from scratch.\n+\t * BT needs pci_save_state()/pci_restore_state() because the BT driver\n+\t * is still partially attached when the _PRR runs (it hasn't been unbound yet).\n+\t * The PCI device needs to remain minimally functional so that\n+\t * device_reprobe(&pdev->dev) can work afterward\n+\t */\n+\tret = btintel_pcie_acpi_reset_method(data);\n+\n+\tif (wifi) {\n+\t\tif (device_reprobe(&wifi->dev))\n+\t\t\tBT_ERR(\"WiFi reprobe failed for BDF:%s\", pci_name(wifi));\n+\t\tpci_dev_put(wifi);\n+\t}\n+\n+\tif (!ret) {\n+\t\tif (device_reprobe(&pdev->dev))\n+\t\t\tBT_ERR(\"BT reprobe failed for BDF:%s\", pci_name(pdev));\n+\t}\n+}\n+\n+static void btintel_pcie_reset_work(struct work_struct *wk)\n+{\n+\tstruct btintel_pcie_data *data =\n+\t\tcontainer_of(wk, struct btintel_pcie_data, reset_work);\n+\tstruct pci_dev *pdev = data->pdev;\n \tint err;\n \n \tpci_lock_rescan_remove();\n \n \tif (!pdev->bus)\n-\t\tgoto error;\n+\t\tgoto out;\n \n-\tdata = pci_get_drvdata(pdev);\n+\tif (!data)\n+\t\tgoto out;\n \n \tbtintel_pcie_disable_interrupts(data);\n \tbtintel_pcie_synchronize_irqs(data);\n@@ -2276,12 +2399,21 @@ static void btintel_pcie_removal_work(struct work_struct *wk)\n \tflush_work(&data->rx_work);\n \n \tbt_dev_dbg(data->hdev, \"Release bluetooth interface\");\n+\tif (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {\n+\t\t/* This function holds pci_lock_rescan_remove(), which acquires\n+\t\t * pci_rescan_remove_lock. This mutex serializes against PCI device\n+\t\t * addition/removal (hotplug), so no device can be added to or\n+\t\t * removed from the bus list while this code runs.\n+\t\t */\n+\t\tbtintel_pcie_perform_pldr(data);\n+\t\tgoto out;\n+\t}\n \tbtintel_pcie_release_hdev(data);\n \n \terr = pci_reset_function(pdev);\n \tif (err) {\n \t\tBT_ERR(\"Failed resetting the pcie device (%d)\", err);\n-\t\tgoto error;\n+\t\tgoto out;\n \t}\n \n \tbtintel_pcie_enable_interrupts(data);\n@@ -2291,7 +2423,7 @@ static void btintel_pcie_removal_work(struct work_struct *wk)\n \tif (err) {\n \t\tBT_ERR(\"Failed to enable bluetooth hardware after reset (%d)\",\n \t\t err);\n-\t\tgoto error;\n+\t\tgoto out;\n \t}\n \n \tbtintel_pcie_reset_ia(data);\n@@ -2301,17 +2433,15 @@ static void btintel_pcie_removal_work(struct work_struct *wk)\n \terr = btintel_pcie_setup_hdev(data);\n \tif (err) {\n \t\tBT_ERR(\"Failed registering hdev (%d)\", err);\n-\t\tgoto error;\n+\t\tgoto out;\n \t}\n-error:\n+out:\n \tpci_dev_put(pdev);\n \tpci_unlock_rescan_remove();\n-\tkfree(removal);\n }\n \n static void btintel_pcie_reset(struct hci_dev *hdev)\n {\n-\tstruct btintel_pcie_removal *removal;\n \tstruct btintel_pcie_data *data;\n \n \tdata = hci_get_drvdata(hdev);\n@@ -2322,14 +2452,8 @@ static void btintel_pcie_reset(struct hci_dev *hdev)\n \tif (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))\n \t\treturn;\n \n-\tremoval = kzalloc_obj(*removal, GFP_ATOMIC);\n-\tif (!removal)\n-\t\treturn;\n-\n-\tremoval->pdev = data->pdev;\n-\tINIT_WORK(&removal->work, btintel_pcie_removal_work);\n-\tpci_dev_get(removal->pdev);\n-\tschedule_work(&removal->work);\n+\tpci_dev_get(data->pdev);\n+\tschedule_work(&data->reset_work);\n }\n \n static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)\n@@ -2339,15 +2463,19 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)\n \tstruct pci_dev *pdev = dev_data->pdev;\n \ttime64_t retry_window;\n \n-\tif (code == 0x13) {\n-\t\tbt_dev_err(hdev, \"Encountered top exception\");\n-\t\treturn;\n-\t}\n+\tbtintel_pcie_dump_debug_registers(hdev);\n \n \tdata = btintel_pcie_get_recovery(pdev, &hdev->dev);\n \tif (!data)\n \t\treturn;\n \n+\tif (code == 0x13)\n+\t\tdev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR;\n+\telse\n+\t\tdev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;\n+\n+\tbt_dev_err(hdev, \"Encountered exception err:0x%x triggering: %s\", code,\n+\t\t dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? \"PLDR\" : \"FLR\");\n \tretry_window = ktime_get_boottime_seconds() - data->last_error;\n \n \tif (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&\n@@ -2500,10 +2628,14 @@ static int btintel_pcie_probe(struct pci_dev *pdev,\n \n \tskb_queue_head_init(&data->rx_skb_q);\n \tINIT_WORK(&data->rx_work, btintel_pcie_rx_work);\n+\tINIT_WORK(&data->reset_work, btintel_pcie_reset_work);\n \n \tdata->boot_stage_cache = 0x00;\n \tdata->img_resp_cache = 0x00;\n-\n+\t/* FLR can be invoked by echoing to debugfs path, so explicitly\n+\t * initialized\n+\t */\n+\tdata->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;\n \terr = btintel_pcie_config_pcie(pdev, data);\n \tif (err)\n \t\tgoto exit_error;\n@@ -2552,6 +2684,14 @@ static void btintel_pcie_remove(struct pci_dev *pdev)\n \n \tdata = pci_get_drvdata(pdev);\n \n+\t/* Cancel pending reset work. Skip only when remove() is called from\n+\t * within the reset work itself (PLDR device_reprobe path) to avoid\n+\t * deadlock. current_work() returns the work_struct of the caller if\n+\t * we are in a workqueue context.\n+\t */\n+\tif (current_work() != &data->reset_work)\n+\t\tcancel_work_sync(&data->reset_work);\n+\n \tbtintel_pcie_disable_interrupts(data);\n \n \tbtintel_pcie_synchronize_irqs(data);\n@@ -2701,6 +2841,7 @@ static int btintel_pcie_resume(struct device *dev)\n \tif (data->pm_sx_event == PM_EVENT_FREEZE ||\n \t data->pm_sx_event == PM_EVENT_HIBERNATE) {\n \t\tset_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);\n+\t\tdata->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;\n \t\tbtintel_pcie_reset(data->hdev);\n \t\treturn 0;\n \t}\ndiff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h\nindex e3d941ffef4a..2a4d15da773d 100644\n--- a/drivers/bluetooth/btintel_pcie.h\n+++ b/drivers/bluetooth/btintel_pcie.h\n@@ -144,6 +144,11 @@ enum msix_mbox_int_causes {\n \tBTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */\n };\n \n+enum btintel_pcie_reset_type {\n+\tBTINTEL_PCIE_IOSF_PRR_FLR = 0,\n+\tBTINTEL_PCIE_IOSF_PRR_PLDR = 1,\n+};\n+\n #define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE\tBIT(7)\n \n /* Minimum and Maximum number of MSI-X Vector\n@@ -503,6 +508,7 @@ struct btintel_pcie_data {\n \tstruct workqueue_struct\t*workqueue;\n \tstruct sk_buff_head\trx_skb_q;\n \tstruct work_struct\trx_work;\n+\tstruct work_struct reset_work;\n \n \tstruct dma_pool\t*dma_pool;\n \tdma_addr_t\tdma_p_addr;\n@@ -514,6 +520,7 @@ struct btintel_pcie_data {\n \tstruct txq\ttxq;\n \tstruct rxq\trxq;\n \tu32\talive_intr_ctxt;\n+\tenum btintel_pcie_reset_type\treset_type;\n \tstruct btintel_pcie_dbgc\tdbgc;\n \tstruct btintel_pcie_dump_header dmp_hdr;\n \tu8\tpm_sx_event;\n", "prefixes": [ "v3" ] }