{"id":2222582,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2222582/?format=json","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=json","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=json","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=json","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"]}