Linux bluetooth development
 help / color / mirror / Atom feed
* RE: btmrvl: Fix hdev dangling pointer and error code in register_hdev
From: bluez.test.bot @ 2026-06-25 17:52 UTC (permalink / raw)
  To: linux-bluetooth, vulab
In-Reply-To: <20260625160607.81615-1-vulab@iscas.ac.cn>

[-- Attachment #1: Type: text/plain, Size: 1348 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/project/bluetooth/list/?series=1116653

---Test result---

Test Summary:
CheckPatch                    PASS      1.04 seconds
VerifyFixes                   PASS      0.21 seconds
VerifySignedoff               PASS      0.20 seconds
GitLint                       PASS      0.46 seconds
SubjectPrefix                 FAIL      0.20 seconds
BuildKernel                   PASS      26.32 seconds
CheckAllWarning               PASS      29.01 seconds
CheckSparse                   PASS      27.79 seconds
BuildKernel32                 PASS      25.65 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      490.64 seconds
IncrementalBuild              PASS      25.84 seconds

Details
##############################
Test: SubjectPrefix - FAIL
Desc: Check subject contains "Bluetooth" prefix
Output:
"Bluetooth: " prefix is not specified in the subject
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/353

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: btintel_pcie: Clear hdev pointer in error path of btintel_pcie_setup_hdev
From: bluez.test.bot @ 2026-06-25 17:26 UTC (permalink / raw)
  To: linux-bluetooth, vulab
In-Reply-To: <20260625155149.79838-1-vulab@iscas.ac.cn>

[-- Attachment #1: Type: text/plain, Size: 567 bytes --]

This is an automated email and please do not reply to this email.

Dear Submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.

----- Output -----

error: patch failed: drivers/bluetooth/btintel_pcie.c:2478
error: drivers/bluetooth/btintel_pcie.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch

Please resolve the issue and submit the patches again.


---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH] btmrvl: Fix hdev dangling pointer and error code in register_hdev
From: Wentao Liang @ 2026-06-25 16:06 UTC (permalink / raw)
  To: marcel, luiz.dentz; +Cc: linux-bluetooth, linux-kernel, Wentao Liang, stable

In btmrvl_register_hdev(), when hci_register_dev() fails, the
function frees the hci_dev via hci_free_dev() but leaves
priv->btmrvl_dev.hcidev as a dangling pointer. While the subsequent
cleanup code does not currently access it, setting it to NULL is a
defensive fix that prevents potential use-after-free.

Additionally, the function always returns -ENOMEM on the
hci_register_dev() failure path, discarding the actual error code.
Fix this by preserving and returning the original error code.

Cc: stable@vger.kernel.org
Fixes: 132ff4e5fa8d ("Bluetooth: Add btmrvl driver for Marvell Bluetooth devices")
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
 drivers/bluetooth/btmrvl_main.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index d6f0ad0b4b6e..3a4c8abae05b 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -683,7 +683,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
 	ret = hci_register_dev(hdev);
 	if (ret < 0) {
 		BT_ERR("Can not register HCI device");
-		goto err_hci_register_dev;
+		goto err_hci_register_dev_free;
 	}
 
 #ifdef CONFIG_DEBUG_FS
@@ -692,8 +692,9 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
 
 	return 0;
 
-err_hci_register_dev:
+err_hci_register_dev_free:
 	hci_free_dev(hdev);
+	priv->btmrvl_dev.hcidev = NULL;
 
 err_hdev:
 	/* Stop the thread servicing the interrupts */
@@ -702,7 +703,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
 	btmrvl_free_adapter(priv);
 	kfree(priv);
 
-	return -ENOMEM;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(btmrvl_register_hdev);
 
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply related

* [PATCH] btintel_pcie: Clear hdev pointer in error path of btintel_pcie_setup_hdev
From: Wentao Liang @ 2026-06-25 15:51 UTC (permalink / raw)
  To: marcel, luiz.dentz; +Cc: linux-bluetooth, linux-kernel, Wentao Liang, stable

In btintel_pcie_setup_hdev(), if hci_register_dev() fails, the
function frees the hci_dev via hci_free_dev() but leaves
data->hdev as a dangling pointer. If a subsequent error handler in
the probe function accesses data->hdev, it would result in a
use-after-free.

Although the current probe error path (btintel_pcie_reset_bt) does
not access data->hdev, setting it to NULL after freeing is a
defensive fix that prevents potential future bugs.

Cc: stable@vger.kernel.org
Fixes: 6e65a09f9275 ("Bluetooth: btintel_pcie: Add *setup* function to download firmware")
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
 drivers/bluetooth/btintel_pcie.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 37e050763633..56d24467b7d5 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2478,6 +2478,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
 
 exit_error:
 	hci_free_dev(hdev);
+	data->hdev = NULL;
 	return err;
 }
 
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply related

* RE: Add support for IPQ5018 Bluetooth
From: bluez.test.bot @ 2026-06-25 15:47 UTC (permalink / raw)
  To: linux-bluetooth, george.moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-1-d999be0e04f7@outlook.com>

[-- Attachment #1: Type: text/plain, Size: 5640 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/project/bluetooth/list/?series=1116589

---Test result---

Test Summary:
CheckPatch                    FAIL      10.08 seconds
VerifyFixes                   PASS      0.27 seconds
VerifySignedoff               PASS      0.31 seconds
GitLint                       FAIL      3.87 seconds
SubjectPrefix                 FAIL      1.71 seconds
BuildKernel                   PASS      25.31 seconds
CheckAllWarning               PASS      27.86 seconds
CheckSparse                   PASS      28.46 seconds
BuildKernel32                 PASS      24.83 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      459.56 seconds
TestRunner_l2cap-tester       PASS      58.15 seconds
TestRunner_iso-tester         PASS      74.50 seconds
TestRunner_bnep-tester        PASS      19.39 seconds
TestRunner_mgmt-tester        FAIL      211.62 seconds
TestRunner_rfcomm-tester      PASS      25.51 seconds
TestRunner_sco-tester         PASS      32.73 seconds
TestRunner_ioctl-tester       PASS      25.67 seconds
TestRunner_mesh-tester        FAIL      26.17 seconds
TestRunner_smp-tester         PASS      23.93 seconds
TestRunner_userchan-tester    PASS      19.86 seconds
TestRunner_6lowpan-tester     PASS      22.74 seconds
IncrementalBuild              PASS      37.70 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#108: 
new file mode 100644

total: 0 errors, 1 warnings, 72 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14645041.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


[2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#147: 
new file mode 100644

total: 0 errors, 1 warnings, 286 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14645042.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


[4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#106: 
new file mode 100644

total: 0 errors, 1 warnings, 63 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14645044.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


[5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#150: 
new file mode 100644

total: 0 errors, 1 warnings, 963 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14645046.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller

1: T1 Title exceeds max length (81>80): "[4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller"
##############################
Test: SubjectPrefix - FAIL
Desc: Check subject contains "Bluetooth" prefix
Output:
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 488 (98.8%), Failed: 2, Not Run: 4

Failed Test Cases
Read Exp Feature - Success                           Failed       0.245 seconds
LL Privacy - Unpair 1                                Timed out    1.861 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.742 seconds
Mesh - Send cancel - 2                               Timed out    1.989 seconds


https://github.com/bluez/bluetooth-next/pull/352

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: George Moussalem @ 2026-06-25 14:24 UTC (permalink / raw)
  To: Philipp Zabel, Jens Axboe, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
	Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
	Konrad Dybcio, Mathieu Poirier
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <439f76c3fcafdfb91cca426fcae17ef776776eab.camel@pengutronix.de>

Thanks, that was quick!

On 6/25/26 18:18, Philipp Zabel wrote:
> On Do, 2026-06-25 at 18:10 +0400, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add support to bring up the M0 core of the bluetooth subsystem found in
>> the IPQ5018 SoC.
>>
>> The signed firmware loaded is authenticated by TrustZone. If successful,
>> the M0 core boots the firmware and the peripheral is taken out of reset
>> using a Secure Channel Manager call to TrustZone.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>>  drivers/remoteproc/Kconfig            |  12 ++
>>  drivers/remoteproc/Makefile           |   1 +
>>  drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
>>  3 files changed, 274 insertions(+)
>>
> [...]
>> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
>> new file mode 100644
>> index 000000000000..7168e270e4d4
>> --- /dev/null
>> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
>> @@ -0,0 +1,261 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/elf.h>
>> +#include <linux/firmware/qcom/qcom_scm.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/soc/qcom/mdt_loader.h>
>> +
>> +#include "qcom_common.h"
>> +
>> +#define BTSS_PAS_ID	0xc
>> +
>> +struct m0_btss {
>> +	struct device *dev;
>> +	phys_addr_t mem_phys;
>> +	phys_addr_t mem_reloc;
>> +	void __iomem *mem_region;
>> +	size_t mem_size;
>> +	struct reset_control *btss_reset;
> 
> Why is this stored here? It doesn't seem to be used.

will remove it and use devm_reset_control_get_exclusive_deasserted as
suggested below.

> 
> [...]
>> +static int m0_btss_pil_probe(struct platform_device *pdev)
>> +{
>> +	// struct reset_control *btss_reset;
> 
> It looks like this shouldn't be commented out.
> 
>> +	struct device *dev = &pdev->dev;
>> +	const char *fw_name = NULL;
>> +	struct m0_btss *desc;
>> +	struct clk *lpo_clk;
>> +	struct rproc *rproc;
>> +	int ret;
>> +
>> +	ret = of_property_read_string(dev->of_node, "firmware-name",
>> +				      &fw_name);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
>> +				 fw_name, sizeof(*desc));
>> +	if (!rproc) {
>> +		dev_err(dev, "failed to allocate rproc\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	desc = rproc->priv;
>> +	desc->dev = dev;
>> +
>> +	ret = m0_btss_alloc_memory_region(desc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
>> +	if (IS_ERR(lpo_clk))
>> +		return dev_err_probe(dev, PTR_ERR(lpo_clk),
>> +				     "Failed to get lpo clock\n");
>> +
>> +	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
> 
> Please use devm_reset_control_get_exclusive() directly.
> 
>> +	if (IS_ERR_OR_NULL(desc->btss_reset))
>> +		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
>> +				     "unable to acquire btss_reset\n");
>> +
>> +	ret = reset_control_deassert(desc->btss_reset);
>> +	if (ret)
>> +		return dev_err_probe(rproc->dev.parent, ret,
>> +				     "Failed to deassert reset\n");
> 
> Shouldn't this be asserted on remove? Otherwise after an unbind/bind
> cycle probe will enable the clock with reset already deasserted.
> That may or may not be problematic.
> 
> Consider using devm_reset_control_get_exclusive_deasserted().
> 
> 
> regards
> Philipp

Regards,
George


^ permalink raw reply

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: Philipp Zabel @ 2026-06-25 14:18 UTC (permalink / raw)
  To: george.moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
	Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
	Konrad Dybcio, Mathieu Poirier
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <20260625-ipq5018-bluetooth-v1-2-d999be0e04f7@outlook.com>

On Do, 2026-06-25 at 18:10 +0400, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add support to bring up the M0 core of the bluetooth subsystem found in
> the IPQ5018 SoC.
> 
> The signed firmware loaded is authenticated by TrustZone. If successful,
> the M0 core boots the firmware and the peripheral is taken out of reset
> using a Secure Channel Manager call to TrustZone.
> 
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  drivers/remoteproc/Kconfig            |  12 ++
>  drivers/remoteproc/Makefile           |   1 +
>  drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
>  3 files changed, 274 insertions(+)
> 
[...]
> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
> new file mode 100644
> index 000000000000..7168e270e4d4
> --- /dev/null
> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
> @@ -0,0 +1,261 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/elf.h>
> +#include <linux/firmware/qcom/qcom_scm.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/soc/qcom/mdt_loader.h>
> +
> +#include "qcom_common.h"
> +
> +#define BTSS_PAS_ID	0xc
> +
> +struct m0_btss {
> +	struct device *dev;
> +	phys_addr_t mem_phys;
> +	phys_addr_t mem_reloc;
> +	void __iomem *mem_region;
> +	size_t mem_size;
> +	struct reset_control *btss_reset;

Why is this stored here? It doesn't seem to be used.

[...]
> +static int m0_btss_pil_probe(struct platform_device *pdev)
> +{
> +	// struct reset_control *btss_reset;

It looks like this shouldn't be commented out.

> +	struct device *dev = &pdev->dev;
> +	const char *fw_name = NULL;
> +	struct m0_btss *desc;
> +	struct clk *lpo_clk;
> +	struct rproc *rproc;
> +	int ret;
> +
> +	ret = of_property_read_string(dev->of_node, "firmware-name",
> +				      &fw_name);
> +	if (ret < 0)
> +		return ret;
> +
> +	rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
> +				 fw_name, sizeof(*desc));
> +	if (!rproc) {
> +		dev_err(dev, "failed to allocate rproc\n");
> +		return -ENOMEM;
> +	}
> +
> +	desc = rproc->priv;
> +	desc->dev = dev;
> +
> +	ret = m0_btss_alloc_memory_region(desc);
> +	if (ret)
> +		return ret;
> +
> +	lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
> +	if (IS_ERR(lpo_clk))
> +		return dev_err_probe(dev, PTR_ERR(lpo_clk),
> +				     "Failed to get lpo clock\n");
> +
> +	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");

Please use devm_reset_control_get_exclusive() directly.

> +	if (IS_ERR_OR_NULL(desc->btss_reset))
> +		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
> +				     "unable to acquire btss_reset\n");
> +
> +	ret = reset_control_deassert(desc->btss_reset);
> +	if (ret)
> +		return dev_err_probe(rproc->dev.parent, ret,
> +				     "Failed to deassert reset\n");

Shouldn't this be asserted on remove? Otherwise after an unbind/bind
cycle probe will enable the clock with reset already deasserted.
That may or may not be problematic.

Consider using devm_reset_control_get_exclusive_deasserted().


regards
Philipp

^ permalink raw reply

* [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>

From: George Moussalem <george.moussalem@outlook.com>

Add driver support for the Qualcomm IPQ5018 bluetooth chip.
The firmware runs on the M0 co-processor.

The host and the M0 core use a shared memory carveout for transport
using ring buffers. This driver implements the transport layer between
the HCI core and the Bluetooth subsystem running on the M0 core.

Notifications of host and M0 core events are triggered by an IPC
register BIT and an interrupt line respectfully.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/bluetooth/Kconfig     |  11 +
 drivers/bluetooth/Makefile    |   1 +
 drivers/bluetooth/btqcomipc.c | 939 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 951 insertions(+)

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..6b8bed6a6ffd 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -413,6 +413,17 @@ config BT_MTKUART
 	  Say Y here to compile support for MediaTek Bluetooth UART devices
 	  into the kernel or say M to compile it as module (btmtkuart).
 
+config BT_QCOMIPC
+	tristate "Qualcomm IPQ5018 IPC based HCI support"
+	select BT_QCA
+	help
+	  Qualcomm IPQ5018 IPC based HCI driver.
+	  This driver is used to bridge HCI data onto shared memory between
+	  the host and the M0 BTSS core.
+
+	  Say Y here to compile support for HCI over Qualcomm IPC into the
+	  kernel or say M to compile as a module.
+
 config BT_QCOMSMD
 	tristate "Qualcomm SMD based HCI support"
 	depends on RPMSG || (COMPILE_TEST && RPMSG=n)
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index e6b1c1180d1d..05f19047bed0 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_MRVL)		+= btmrvl.o
 obj-$(CONFIG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
 obj-$(CONFIG_BT_MTKSDIO)	+= btmtksdio.o
 obj-$(CONFIG_BT_MTKUART)	+= btmtkuart.o
+obj-$(CONFIG_BT_QCOMIPC)	+= btqcomipc.o
 obj-$(CONFIG_BT_QCOMSMD)	+= btqcomsmd.o
 obj-$(CONFIG_BT_BCM)		+= btbcm.o
 obj-$(CONFIG_BT_RTL)		+= btrtl.o
diff --git a/drivers/bluetooth/btqcomipc.c b/drivers/bluetooth/btqcomipc.c
new file mode 100644
index 000000000000..662a75b6c4a9
--- /dev/null
+++ b/drivers/bluetooth/btqcomipc.c
@@ -0,0 +1,939 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/remoteproc.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btqca.h"
+
+/** Message header format.
+ *
+ *        ----------------------------------------------------------------
+ * BitPos |    15    | 14 | 13 | 12 | 11 | 10 |  9  |  8  |    7 - 0     |
+ *         ---------------------------------------------------------------
+ * Field  | long_msg |ACK |        RFU        |  pkt_type |    cmd       |
+ *        ----------------------------------------------------------------
+ *
+ * - long_msg   : If set, indicates that the payload is larger than the
+ *                IPC_MSG_PLD_SZ. The payload instead contains a pointer to the
+ *                long message buffer in the shared BTSS memory space.
+ *
+ * - ACK       : Set if sending ACK if required by sending acknowledegement
+ *                to sender i.e. send an ack IPC interrupt if set.
+ *
+ * - RFU        : Reserved for future use.
+ *
+ * - pkt_type   : IPC Packet Type
+ *
+ * - cmd        : Contains unique command ID
+ */
+
+#define IPC_MSG_HDR_SZ		4
+#define IPC_MSG_PLD_SZ		40
+#define IPC_TOTAL_MSG_SZ	(IPC_MSG_HDR_SZ + IPC_MSG_PLD_SZ)
+
+/* Message Header */
+#define IPC_HDR_LONG_MSG	BIT(15)
+#define IPC_HDR_REQ_ACK		BIT(14)
+#define IPC_HDR_PKT_TYPE_MASK	GENMASK(9, 8)
+#define  IPC_HDR_PKT_TYPE_CUST	0
+#define  IPC_HDR_PKT_TYPE_HCI	1
+#define  IPC_HDR_PKT_TYPE_AUDIO	2
+#define  IPC_HDR_PKT_TYPE_RFU	3
+#define IPC_HDR_CMD_MASK	GENMASK(7, 0)
+
+#define IPC_CMD_STOP		1
+#define IPC_CMD_SWITCH_TO_UART	2
+#define IPC_CMD_PREPARE_DUMP	3
+#define IPC_CMD_COLLECT_DUMP	4
+#define IPC_CMD_START		5
+
+#define IPC_TX_QSIZE		32
+
+#define	TO_APPS_ADDR(a)		(desc->mem_region + (int)(uintptr_t)a)
+#define	TO_BT_ADDR(a)		(a - desc->mem_region)
+#define IPC_LBUF_SZ(w, x, y, z)	(((TO_BT_ADDR((void *)w) + w->x) - w->y) / w->z)
+
+#define	GET_NO_OF_BLOCKS(a, b) ((a + b - 1) / b)
+
+#define GET_RX_INDEX_FROM_BUF(x, y)	((x - desc->rx_ctxt->lring_buf) / y)
+
+#define GET_TX_INDEX_FROM_BUF(x, y)	((x - desc->tx_ctxt->lring_buf) / y)
+
+#define IS_RX_MEM_NON_CONTIGIOUS(buf, len, sz)		\
+	((buf + len) > (desc->rx_ctxt->lring_buf +	\
+	(sz * desc->rx_ctxt->lmsg_buf_cnt)))
+
+#define POWER_CONTROL_DELAY_MS	50
+
+#define BTSS_PAS_ID	0xc
+
+struct long_msg_info {
+	__le16 smsg_free_cnt;
+	__le16 lmsg_free_cnt;
+	u8 ridx;
+	u8 widx;
+} __packed;
+
+struct ipc_aux_ptr {
+	__le32 len;
+	__le32 buf;
+} __packed;
+
+struct ring_buffer {
+	__le16 msg_hdr;
+	__le16 len;
+	union {
+		u8 smsg_data[IPC_MSG_PLD_SZ];
+		__le32 lmsg_data;
+	} payload;
+} __packed;
+
+struct ring_buffer_info {
+	__le32 rbuf;
+	u8 ring_buf_cnt;
+	u8 ridx;
+	u8 widx;
+	u8 tidx;
+	__le32 next;
+} __packed;
+
+struct context_info {
+	__le16 total_size;
+	u8 lmsg_buf_cnt;
+	u8 smsg_buf_cnt;
+	struct ring_buffer_info sring_buf_info;
+	__le32 sring_buf;
+	__le32 lring_buf;
+	__le32 reserved;
+} __packed;
+
+struct qcom_btss {
+	struct device *dev;
+	struct rproc *rproc;
+	struct hci_dev *hdev;
+
+	struct regmap *regmap;
+	u32 offset;
+	u32 bit;
+	int irq;
+
+	void *mem_region;
+	phys_addr_t mem_phys;
+	phys_addr_t mem_reloc;
+	size_t mem_size;
+
+	struct sk_buff_head tx_q;
+	struct workqueue_struct *wq;
+	struct work_struct work;
+	wait_queue_head_t wait_q;
+	spinlock_t lock;
+
+	struct context_info *tx_ctxt;
+	struct context_info *rx_ctxt;
+	struct long_msg_info lmsg_ctxt;
+
+	bool running;
+};
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb);
+
+static void *btss_alloc_lmsg(struct qcom_btss *desc, u32 len,
+			     struct ipc_aux_ptr *aux_ptr, bool *is_lbuf_full)
+{
+	struct device *dev = desc->dev;
+	u8 idx, blks, blks_consumed;
+	void *ret_ptr;
+	u32 lsz;
+
+	if (desc->tx_ctxt->lring_buf == 0) {
+		dev_err(dev, "no long message buffer initialized\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf, lmsg_buf_cnt);
+	blks = GET_NO_OF_BLOCKS(len, lsz);
+
+	if (!desc->lmsg_ctxt.lmsg_free_cnt ||
+			(blks > desc->lmsg_ctxt.lmsg_free_cnt))
+		return ERR_PTR(-EAGAIN);
+
+	idx = desc->lmsg_ctxt.widx;
+
+	if ((desc->lmsg_ctxt.widx + blks) > desc->tx_ctxt->lmsg_buf_cnt) {
+		blks_consumed = desc->tx_ctxt->lmsg_buf_cnt - idx;
+		aux_ptr->len = len - (blks_consumed * lsz);
+		aux_ptr->buf = desc->tx_ctxt->lring_buf;
+	}
+
+	desc->lmsg_ctxt.widx = (desc->lmsg_ctxt.widx + blks) %
+		desc->tx_ctxt->lmsg_buf_cnt;
+
+	desc->lmsg_ctxt.lmsg_free_cnt -= blks;
+
+	if (desc->lmsg_ctxt.lmsg_free_cnt <=
+			((desc->tx_ctxt->lmsg_buf_cnt * 20) / 100))
+		*is_lbuf_full = true;
+
+	ret_ptr = TO_APPS_ADDR(desc->tx_ctxt->lring_buf) + (idx * lsz);
+
+	return ret_ptr;
+}
+
+static struct ring_buffer_info *btss_get_tx_rbuf(struct qcom_btss *desc,
+						 bool *is_sbuf_full)
+{
+	u8 idx;
+	struct ring_buffer_info *rinfo;
+
+	for (rinfo = &(desc->tx_ctxt->sring_buf_info);	rinfo != NULL;
+		rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+		idx = (rinfo->widx + 1) % (desc->tx_ctxt->smsg_buf_cnt);
+
+		if (idx != rinfo->tidx) {
+			desc->lmsg_ctxt.smsg_free_cnt--;
+
+			if (desc->lmsg_ctxt.smsg_free_cnt <=
+				((desc->tx_ctxt->smsg_buf_cnt * 20) / 100))
+				*is_sbuf_full = true;
+
+			return rinfo;
+		}
+	}
+
+	return ERR_PTR(-EAGAIN);
+}
+
+static int btss_send(struct qcom_btss *desc, u16 msg_hdr,
+		     struct sk_buff *skb)
+{
+	struct hci_dev *hdev = desc->hdev;
+	struct ring_buffer_info *rinfo;
+	struct ipc_aux_ptr aux_ptr;
+	struct ring_buffer *rbuf;
+	bool is_lbuf_full = false;
+	bool is_sbuf_full = false;
+	u16 hdr = msg_hdr;
+	void *ptr_buf;
+	u32 len;
+
+	/* Account for HCI packet type as it's not included in the skb payload */
+	len = skb->len + 1;
+	memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+	if (len > IPC_MSG_PLD_SZ) {
+		hdr |= IPC_HDR_LONG_MSG;
+
+		ptr_buf = btss_alloc_lmsg(desc, len,
+					  &aux_ptr, &is_lbuf_full);
+		if (IS_ERR(ptr_buf)) {
+			bt_dev_err(hdev, "long msg buf full");
+			hdev->stat.err_tx++;
+			return PTR_ERR(ptr_buf);
+		}
+	}
+
+	rinfo = btss_get_tx_rbuf(desc, &is_sbuf_full);
+	if (IS_ERR(rinfo)) {
+		bt_dev_err(hdev, "short msg buf full");
+		hdev->stat.err_tx++;
+		return PTR_ERR(rinfo);
+	}
+
+	rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[rinfo->widx];
+
+	if (len > IPC_MSG_PLD_SZ)
+		rbuf->payload.lmsg_data = cpu_to_le32(TO_BT_ADDR(ptr_buf));
+	else
+		ptr_buf = rbuf->payload.smsg_data;
+
+	/* if it's a short message, the aux len and buf are NULL */
+	memcpy_toio(ptr_buf, &hci_skb_pkt_type(skb), 1);
+	memcpy_toio((u8 *)ptr_buf + 1, skb->data, skb->len - aux_ptr.len);
+	if (aux_ptr.buf) {
+		memcpy_toio(TO_APPS_ADDR(aux_ptr.buf),
+			    (skb->data + (skb->len - aux_ptr.len)), aux_ptr.len);
+	}
+
+	if (is_sbuf_full || is_lbuf_full)
+		hdr |= IPC_HDR_REQ_ACK;
+
+	rbuf->msg_hdr = cpu_to_le16(hdr);
+	rbuf->len = cpu_to_le16(len);
+
+	rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+	regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+	return 0;
+}
+
+static void btss_process_tx_queue(struct qcom_btss *desc)
+{
+	struct sk_buff *skb;
+	u16 hdr;
+	int ret;
+
+	while ((skb = skb_dequeue(&desc->tx_q))) {
+		hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+
+		ret = btss_send(desc, hdr, skb);
+		if (ret) {
+			bt_dev_err(desc->hdev, "Failed to send message");
+			skb_queue_head(&desc->tx_q, skb);
+			break;
+		}
+
+		btqcomipc_update_stats(desc->hdev, skb);
+		kfree_skb(skb);
+	}
+}
+
+static void btss_free_lmsg(struct qcom_btss *desc, u32 lmsg, u16 len)
+{
+	u8 idx;
+	u8 blks;
+	u32 lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf,
+				   lmsg_buf_cnt);
+
+	idx = GET_TX_INDEX_FROM_BUF(lmsg, lsz);
+
+	if (idx != desc->lmsg_ctxt.ridx)
+		return;
+
+	blks = GET_NO_OF_BLOCKS(len, lsz);
+
+	desc->lmsg_ctxt.ridx  = (desc->lmsg_ctxt.ridx  + blks) %
+		desc->tx_ctxt->lmsg_buf_cnt;
+
+	desc->lmsg_ctxt.lmsg_free_cnt += blks;
+}
+
+static int btss_send_ctrl(struct qcom_btss *desc, u16 msg_hdr)
+{
+	struct ring_buffer_info *rinfo;
+	struct ring_buffer *rbuf;
+
+	rinfo = btss_get_tx_rbuf(desc, NULL);
+	if (IS_ERR(rinfo))
+		return PTR_ERR(rinfo);
+
+	rbuf = &((struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf))[rinfo->widx];
+	rbuf->msg_hdr = cpu_to_le16(msg_hdr);
+	rbuf->len = 0;
+
+	rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+	regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+	return 0;
+}
+
+static void btss_recv_cust_frame(struct qcom_btss *desc, u8 cmd)
+{
+	u16 msg_hdr = 0;
+	int ret;
+
+	msg_hdr |= cmd;
+
+	switch (cmd) {
+	case IPC_CMD_STOP:
+		bt_dev_info(desc->hdev, "BTSS stopped, gracefully stopping APSS IPC");
+		break;
+	case IPC_CMD_START:
+		desc->tx_ctxt = (struct context_info *)((void *)desc->rx_ctxt +
+				le16_to_cpu(desc->rx_ctxt->total_size));
+		desc->lmsg_ctxt.widx = 0;
+		desc->lmsg_ctxt.ridx = 0;
+		desc->lmsg_ctxt.smsg_free_cnt = desc->tx_ctxt->smsg_buf_cnt;
+		desc->lmsg_ctxt.lmsg_free_cnt = desc->tx_ctxt->lmsg_buf_cnt;
+		WRITE_ONCE(desc->running, true);
+		wake_up(&desc->wait_q);
+
+		bt_dev_info(desc->hdev, "BTSS started, initializing APSS BT IPC");
+		return;
+	default:
+		bt_dev_err(desc->hdev, "Unsupported CMD ID: %u", cmd);
+		return;
+	}
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		bt_dev_err(desc->hdev, "BTSS not initialized, no message sent");
+		return;
+	}
+
+	WRITE_ONCE(desc->running, false);
+
+	ret = btss_send_ctrl(desc, msg_hdr);
+	if (ret)
+		bt_dev_err(desc->hdev, "Failed to send control message");
+}
+
+static inline int btss_recv_hci_frame(struct qcom_btss *desc, const u8 *data, size_t len)
+{
+	unsigned char pkt_type;
+	struct sk_buff *skb;
+	size_t pkt_len;
+
+	if (len < 1)
+		return -EPROTO;
+
+	pkt_type = data[0];
+
+	switch (pkt_type) {
+	case HCI_EVENT_PKT:
+	{
+		if (len < HCI_EVENT_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_event_hdr *hdr = (struct hci_event_hdr *)(data + 1);
+
+		pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+		break;
+	}
+	case HCI_COMMAND_PKT: {
+		if (len < HCI_COMMAND_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_command_hdr *hdr = (struct hci_command_hdr *)(data + 1);
+
+		pkt_len = HCI_COMMAND_HDR_SIZE + le16_to_cpu(hdr->plen);
+		break;
+	}
+	case HCI_ACLDATA_PKT:
+	{
+		if (len < HCI_ACL_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_acl_hdr *hdr = (struct hci_acl_hdr *)(data + 1);
+
+		pkt_len = HCI_ACL_HDR_SIZE + le16_to_cpu(hdr->dlen);
+		break;
+	}
+	case HCI_SCODATA_PKT:
+	{
+		if (len < HCI_SCO_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_sco_hdr *hdr = (struct hci_sco_hdr *)(data + 1);
+
+		pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+		break;
+	}
+	default:
+		return -EPROTO;
+	}
+
+	if (pkt_len > len)
+		return -EINVAL;
+
+	skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+	if (!skb) {
+		desc->hdev->stat.err_rx++;
+		return -ENOMEM;
+	}
+
+	skb->dev = (void *)desc->hdev;
+	hci_skb_pkt_type(skb) = pkt_type;
+	skb_put_data(skb, data + 1, pkt_len);
+
+	hci_recv_frame(desc->hdev, skb);
+	desc->hdev->stat.byte_rx += pkt_len;
+
+	return 0;
+}
+
+static inline int btss_process_rx(struct qcom_btss *desc,
+				  struct ring_buffer_info *rinfo,
+				  bool *ack, u8 *rx_count)
+{
+	u8 ridx, lbuf_idx, blks_consumed, pkt_type, cmd;
+	struct ipc_aux_ptr aux_ptr;
+	struct ring_buffer *rbuf;
+	uint8_t *rxbuf = NULL;
+	unsigned char *buf;
+	u32 lsz;
+	int ret;
+
+	ridx = rinfo->ridx;
+
+	while (ridx != rinfo->widx) {
+		memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+		rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[ridx];
+
+		if (rbuf->msg_hdr & IPC_HDR_LONG_MSG) {
+			rxbuf = TO_APPS_ADDR(rbuf->payload.lmsg_data);
+			lsz = IPC_LBUF_SZ(desc->rx_ctxt, total_size, lring_buf,
+				   lmsg_buf_cnt);
+
+			if (IS_RX_MEM_NON_CONTIGIOUS(rbuf->payload.lmsg_data,
+						     rbuf->len, lsz)) {
+				lbuf_idx = GET_RX_INDEX_FROM_BUF(
+						rbuf->payload.lmsg_data, lsz);
+
+				blks_consumed = desc->rx_ctxt->lmsg_buf_cnt -
+					lbuf_idx;
+				aux_ptr.len = rbuf->len - (blks_consumed * lsz);
+				aux_ptr.buf = desc->rx_ctxt->lring_buf;
+			}
+		} else {
+			rxbuf = rbuf->payload.smsg_data;
+		}
+
+		*ack = (rbuf->msg_hdr & IPC_HDR_REQ_ACK);
+
+		pkt_type = FIELD_GET(IPC_HDR_PKT_TYPE_MASK, rbuf->msg_hdr);
+
+		switch (pkt_type) {
+		case IPC_HDR_PKT_TYPE_HCI:
+			buf = kmalloc(rbuf->len, GFP_ATOMIC);
+			if (!buf) {
+				rinfo->ridx = ridx;
+				return -ENOMEM;
+			}
+
+			memcpy_fromio(buf, rxbuf, rbuf->len - aux_ptr.len);
+
+			if (aux_ptr.buf)
+				memcpy_fromio(buf + (rbuf->len - aux_ptr.len),
+					      TO_APPS_ADDR(aux_ptr.buf), aux_ptr.len);
+
+			ret = btss_recv_hci_frame(desc, buf, rbuf->len);
+			if (ret)
+				bt_dev_err(desc->hdev, "Failed to process HCI frame: %d", ret);
+			kfree(buf);
+			break;
+		case IPC_HDR_PKT_TYPE_CUST:
+			cmd = FIELD_GET(IPC_HDR_CMD_MASK, rbuf->msg_hdr);
+			btss_recv_cust_frame(desc, cmd);
+			break;
+		default:
+			break;
+		}
+
+		ridx = (ridx + 1) % rinfo->ring_buf_cnt;
+
+		if (rx_count)
+			(*rx_count)++;
+
+		rinfo->ridx = ridx;
+	}
+
+	return 0;
+}
+
+static void btss_process_ack(struct qcom_btss *desc)
+{
+	struct ring_buffer_info *rinfo;
+	struct ring_buffer *rbuf;
+	u8 tidx;
+
+	for (rinfo = &desc->tx_ctxt->sring_buf_info; rinfo != NULL;
+		rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+		tidx = rinfo->tidx;
+		rbuf = (struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf);
+
+		while (tidx != rinfo->ridx) {
+			if (rbuf[tidx].msg_hdr & IPC_HDR_LONG_MSG) {
+				btss_free_lmsg(desc,
+					       rbuf[tidx].payload.lmsg_data,
+					       rbuf[tidx].len);
+			}
+
+			tidx = (tidx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+			desc->lmsg_ctxt.smsg_free_cnt++;
+		}
+
+		rinfo->tidx = tidx;
+
+		btss_process_tx_queue(desc);
+	}
+}
+
+static void btss_worker(struct work_struct *work)
+{
+	struct qcom_btss *desc = container_of(work, struct qcom_btss, work);
+	struct ring_buffer_info *rinfo;
+	bool ack = false;
+	u32 offset;
+	int ret;
+
+	if (desc->rproc->state != RPROC_RUNNING)
+		return;
+
+	spin_lock(&desc->lock);
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		// FW sets offset of RX context info at start of memory region upon boot
+		offset = readl(desc->mem_region);
+		dev_dbg(desc->dev, "offset after M0 boot: 0x%08x\n", offset);
+		desc->rx_ctxt = (struct context_info *)(desc->mem_region + offset);
+	} else {
+		btss_process_ack(desc);
+	}
+
+	for (rinfo = &(desc->rx_ctxt->sring_buf_info);
+	     rinfo != NULL;
+	     rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+		ret = btss_process_rx(desc, rinfo, &ack,
+				      &desc->rx_ctxt->smsg_buf_cnt);
+		if (ret) {
+			bt_dev_err(desc->hdev, "Failed to process peer msgs: %d", ret);
+			goto spin_unlock;
+		}
+	}
+
+	if (ack)
+		regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+spin_unlock:
+	spin_unlock(&desc->lock);
+}
+
+static irqreturn_t btss_irq_handler(int irq, void *data)
+{
+	struct qcom_btss *desc = data;
+
+	queue_work(desc->wq, &desc->work);
+
+	return IRQ_HANDLED;
+}
+
+static int btss_init(struct qcom_btss *desc)
+{
+	struct device *dev = desc->dev;
+	int ret;
+
+	init_waitqueue_head(&desc->wait_q);
+	spin_lock_init(&desc->lock);
+	skb_queue_head_init(&desc->tx_q);
+
+	desc->wq = create_singlethread_workqueue("btss_wq");
+	if (!desc->wq) {
+		dev_err(dev, "Failed to initialize workqueue\n");
+		return -EAGAIN;
+	}
+
+	INIT_WORK(&desc->work, btss_worker);
+
+	ret = devm_request_threaded_irq(dev, desc->irq, NULL, btss_irq_handler,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					"btss_irq", desc);
+
+	if (ret)
+		dev_err(dev, "error registering irq[%d] ret = %d\n",
+			desc->irq, ret);
+
+	return ret;
+}
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	u8 pkt_type = hci_skb_pkt_type(skb);
+
+	hdev->stat.byte_tx += skb->len;
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	default:
+		break;
+	}
+}
+
+static int btqcomipc_open(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	int ret;
+
+	ret = btss_init(desc);
+	if (ret) {
+		bt_dev_err(hdev, "Failed initializing BTSS: %d", ret);
+		return ret;
+	}
+
+	/* wait 2 seconds for filesystem to become available */
+	msleep(2000);
+
+	/* Boot M0 firmware */
+	ret = rproc_boot(desc->rproc);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to boot M0 processor: %d", ret);
+		return ret;
+	}
+
+	msleep(POWER_CONTROL_DELAY_MS);
+	ret = wait_event_timeout(desc->wait_q, READ_ONCE(desc->running),
+				 msecs_to_jiffies(1000));
+
+	if (!ret) {
+		bt_dev_err(hdev, "Timeout waiting for BTSS start");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int btqcomipc_close(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	rproc_shutdown(desc->rproc);
+	msleep(POWER_CONTROL_DELAY_MS);
+
+	return 0;
+}
+
+static int btqcomipc_shutdown(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	WRITE_ONCE(desc->running, false);
+	if (desc->wq != NULL) {
+		disable_irq(desc->irq);
+		flush_workqueue(desc->wq);
+		devm_free_irq(desc->dev, desc->irq, desc);
+		skb_queue_purge(&desc->tx_q);
+		destroy_workqueue(desc->wq);
+		desc->wq = NULL;
+	}
+
+	return 0;
+}
+
+static int btqcomipc_setup(struct hci_dev *hdev)
+{
+	struct qca_btsoc_version ver;
+	int ret;
+
+	/*
+	 * Enable controller to do both LE scan and BR/EDR inquiry
+	 * simultaneously.
+	 */
+	hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+
+	/*
+	 * Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute
+	 * setup for every hci up.
+	 */
+	hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+	ret = qca_read_soc_version(hdev, &ver, QCA_IPQ5018);
+	if (ret)
+		return -EINVAL;
+
+	ret = qca_uart_setup(hdev, 0, QCA_IPQ5018, ver, NULL);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to setup UART: %d\n", ret);
+		return ret;
+	}
+
+	bt_dev_info(hdev, "QCA Build Info: %s", hdev->fw_info);
+
+	/* Obtain and set BD address from NVMEM cell */
+	hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+	hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+
+	return 0;
+}
+
+static int btqcomipc_send(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	u16 hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+	int ret;
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		bt_dev_err(hdev, "BTSS not initialized, failed to send message");
+		ret = -ENODEV;
+		goto free_skb;
+	}
+
+	ret = btss_send(desc, hdr, skb);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			if (skb_queue_len(&desc->tx_q) >= IPC_TX_QSIZE) {
+				bt_dev_err(hdev, "TX queue full, dropping message");
+				hdev->stat.err_tx++;
+				ret = -ENOBUFS;
+			} else {
+				skb_queue_tail(&desc->tx_q, skb);
+				return 0;
+			}
+		} else {
+			bt_dev_err(hdev, "Failed to send message: %d", ret);
+			hdev->stat.err_tx++;
+		}
+	}
+
+	btqcomipc_update_stats(desc->hdev, skb);
+
+free_skb:
+	kfree_skb(skb);
+
+	return ret;
+}
+
+static int btqcomipc_flush(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	skb_queue_purge(&desc->tx_q);
+	return 0;
+}
+
+static int btqcomipc_alloc_memory_region(struct qcom_btss *desc)
+{
+	struct device *dev = desc->dev;
+	struct resource res;
+	int ret;
+
+	/* lookup reserved-memory region of the remoteproc node */
+	ret = of_reserved_mem_region_to_resource(desc->rproc->dev.parent->of_node, 0, &res);
+	if (ret) {
+		dev_err(dev, "unable to acquire memory-region resource\n");
+		return ret;
+	}
+
+	desc->mem_phys = res.start;
+	desc->mem_reloc = res.start;
+	desc->mem_size = resource_size(&res);
+	desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+	if (!desc->mem_region) {
+		dev_err(dev, "unable to map memory region: %pR\n", &res);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void btqcomipc_rproc_put(void *data)
+{
+	struct rproc *rproc = data;
+
+	rproc_put(rproc);
+}
+
+static int btqcomipc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct qcom_btss *desc;
+	phandle rproc_phandle;
+	struct hci_dev *hdev;
+	unsigned int args[2];
+	int ret;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->dev = dev;
+
+	if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle))
+		return dev_err_probe(dev, -ENOENT, "Failed to get remoteproc handle\n");
+
+	desc->rproc = rproc_get_by_phandle(rproc_phandle);
+	if (!desc->rproc)
+		return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get remoteproc\n");
+
+	devm_add_action_or_reset(dev, btqcomipc_rproc_put, desc->rproc);
+
+	ret = btqcomipc_alloc_memory_region(desc);
+	if (ret)
+		return ret;
+
+	desc->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "qcom,ipc", 2, args);
+	if (IS_ERR(desc->regmap))
+		return PTR_ERR(desc->regmap);
+
+	desc->offset = args[0];
+	desc->bit = args[1];
+
+	desc->irq = platform_get_irq(pdev, 0);
+	if (desc->irq < 0)
+		return dev_err_probe(dev, desc->irq, "Failed to acquire IRQ\n");
+
+	hdev = hci_alloc_dev();
+	if (!hdev)
+		return -ENOMEM;
+
+	hci_set_drvdata(hdev, desc);
+	desc->hdev = hdev;
+	SET_HCIDEV_DEV(hdev, &pdev->dev);
+	hdev->bus = HCI_IPC;
+
+	hdev->open = btqcomipc_open;
+	hdev->close = btqcomipc_close;
+	hdev->shutdown = btqcomipc_shutdown;
+	hdev->setup = btqcomipc_setup;
+	hdev->send = btqcomipc_send;
+	hdev->flush = btqcomipc_flush;
+	hdev->set_bdaddr = qca_set_bdaddr;
+
+	ret = hci_register_dev(hdev);
+	if (ret < 0) {
+		hci_free_dev(hdev);
+		return dev_err_probe(dev, -EBUSY, "Failed to register hdev\n");
+	}
+
+	platform_set_drvdata(pdev, desc);
+
+	return 0;
+}
+
+static void btqcomipc_remove(struct platform_device *pdev)
+{
+	struct qcom_btss *desc = platform_get_drvdata(pdev);
+
+	if (!desc || !desc->hdev)
+		return;
+
+	hci_unregister_dev(desc->hdev);
+	hci_free_dev(desc->hdev);
+}
+
+static const struct of_device_id btqcomipc_of_match[] = {
+	{ .compatible = "qcom,ipq5018-bt" },
+	{ /* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, btqcomipc_of_match);
+
+static struct platform_driver btqcomipc_driver = {
+	.probe = btqcomipc_probe,
+	.remove = btqcomipc_remove,
+	.driver = {
+		.name = "btqcomipc",
+		.of_match_table = btqcomipc_of_match,
+	},
+};
+
+module_platform_driver(btqcomipc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Bluetooth IPC Driver");
+MODULE_LICENSE("GPL");

-- 
2.53.0



^ permalink raw reply related

* [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>

From: George Moussalem <george.moussalem@outlook.com>

Add nodes for the M0 remoteproc, reserved memory carveout, and Bluetooth
to bring up the M0 core and enable the Bluetooth Subsystem.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 arch/arm64/boot/dts/qcom/ipq5018.dtsi | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 6f8004a22a1f..4fdf20c87b0a 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -17,6 +17,17 @@ / {
 	#address-cells = <2>;
 	#size-cells = <2>;
 
+	bluetooth: bluetooth {
+		compatible = "qcom,ipq5018-bt";
+
+		qcom,ipc = <&apcs_glb 8 23>;
+		interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+		qcom,rproc = <&m0_btss>;
+
+		status = "disabled";
+	};
+
 	clocks {
 		gephy_rx_clk: gephy-rx-clk {
 			compatible = "fixed-clock";
@@ -131,11 +142,31 @@ psci {
 		method = "smc";
 	};
 
+	m0_btss: remoteproc {
+		compatible = "qcom,ipq5018-btss-pil";
+
+		firmware-name = "qca/bt_fw_patch.mbn";
+
+		clocks = <&gcc GCC_BTSS_LPO_CLK>;
+		clock-names = "btss_lpo_clk";
+		resets = <&gcc GCC_BTSS_BCR>;
+		reset-names = "btss_reset";
+
+		memory-region = <&btss_region>;
+
+		status = "disabled";
+	};
+
 	reserved-memory {
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges;
 
+		btss_region: bluetooth@7000000 {
+			reg = <0x0 0x07000000 0x0 0x58000>;
+			no-map;
+		};
+
 		bootloader@4a800000 {
 			reg = <0x0 0x4a800000 0x0 0x200000>;
 			no-map;
@@ -647,7 +678,8 @@ watchdog: watchdog@b017000 {
 
 		apcs_glb: mailbox@b111000 {
 			compatible = "qcom,ipq5018-apcs-apps-global",
-				     "qcom,ipq6018-apcs-apps-global";
+				     "qcom,ipq6018-apcs-apps-global",
+				     "syscon";
 			reg = <0x0b111000 0x1000>;
 			#clock-cells = <1>;
 			clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;

-- 
2.53.0



^ permalink raw reply related

* [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>

From: George Moussalem <george.moussalem@outlook.com>

Document the Qualcomm IPQ5018 Bluetooth controller.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
new file mode 100644
index 000000000000..afd33f851858
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ5018 Bluetooth
+
+maintainers:
+  - George Moussalem <george.moussalem@outlook.com>
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq5018-bt
+
+  interrupts:
+    items:
+      - description:
+          Interrupt line from the M0 Bluetooth Subsystem to the host processor
+          to notify it of events such as re
+
+  qcom,ipc:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: phandle to a syscon node representing the APCS registers
+          - description: u32 representing offset to the register within the syscon
+          - description: u32 representing the ipc bit within the register
+    description: |
+      These entries specify the outgoing IPC bit used for signaling the remote
+      M0 BTSS core of a host event or for sending an ACK if the remote processor
+      expects it.
+
+  qcom,rproc:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the remote processor node representing the M0 BTSS core.
+
+required:
+  - compatible
+  - interrupts
+  - qcom,ipc
+  - qcom,rproc
+
+allOf:
+  - $ref: bluetooth-controller.yaml#
+  - $ref: qcom,bluetooth-common.yaml
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    bluetooth: bluetooth {
+      compatible = "qcom,ipq5018-bt";
+
+      qcom,ipc = <&apcs_glb 8 23>;
+      interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+      qcom,rproc = <&m0_btss>;
+    };

-- 
2.53.0



^ permalink raw reply related

* [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>

From: George Moussalem <george.moussalem@outlook.com>

Add the IPQ5018 SoC type and support for loading its firmware.

The firmware tested has been taken from GPL sources of various router
boards. Firmware files needed are:
- qca/bt_fw_patch.mbn
- qca/mpnv10.bin

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/bluetooth/btqca.c | 16 ++++++++++++++++
 drivers/bluetooth/btqca.h |  3 +++
 2 files changed, 19 insertions(+)

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 04ebe290bc78..e136e91976cf 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -380,6 +380,9 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
 		break;
 
 	case TLV_TYPE_NVM:
+		if (soc_type == QCA_IPQ5018)
+			break;
+
 		if (fw_size < sizeof(struct tlv_type_hdr))
 			return -EINVAL;
 
@@ -794,6 +797,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	else
 		rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
 
+	if (soc_type == QCA_IPQ5018)
+		goto download_nvm;
+
 	if (soc_type == QCA_WCN6750)
 		qca_send_patch_config_cmd(hdev);
 
@@ -881,6 +887,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
 		qca_read_fw_board_id(hdev, &boardid);
 
+download_nvm:
 	/* Download NVM configuration */
 	config.type = TLV_TYPE_NVM;
 	if (firmware_name) {
@@ -939,6 +946,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 			qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
 				 "hmtnv", soc_type, ver, rom_ver, boardid);
 			break;
+		case QCA_IPQ5018:
+			snprintf(config.fwname, sizeof(config.fwname),
+				 "qca/mpnv%02x.bin", rom_ver);
+			break;
 		default:
 			snprintf(config.fwname, sizeof(config.fwname),
 				 "qca/nvm_%08x.bin", soc_ver);
@@ -958,6 +969,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 		return err;
 	}
 
+	if (soc_type == QCA_IPQ5018)
+		msleep(NVM_READY_DELAY_MS);
+
 	switch (soc_type) {
 	case QCA_QCA2066:
 	case QCA_QCA6390:
@@ -965,6 +979,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	case QCA_WCN6750:
 	case QCA_WCN6855:
 	case QCA_WCN7850:
+	case QCA_IPQ5018:
 		err = qca_disable_soc_logging(hdev);
 		if (err < 0)
 			return err;
@@ -1001,6 +1016,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	case QCA_WCN6750:
 	case QCA_WCN6855:
 	case QCA_WCN7850:
+	case QCA_IPQ5018:
 		/* get fw build info */
 		err = qca_read_fw_build_info(hdev);
 		if (err < 0)
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 8f3c1b1c77b3..343cd62d1137 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -54,6 +54,8 @@
 #define QCA_HSP_GF_SOC_ID		0x1200
 #define QCA_HSP_GF_SOC_MASK		0x0000ff00
 
+#define NVM_READY_DELAY_MS		1500
+
 enum qca_baudrate {
 	QCA_BAUDRATE_115200	= 0,
 	QCA_BAUDRATE_57600,
@@ -158,6 +160,7 @@ enum qca_btsoc_type {
 	QCA_WCN6750,
 	QCA_WCN6855,
 	QCA_WCN7850,
+	QCA_IPQ5018,
 };
 
 #if IS_ENABLED(CONFIG_BT_QCA)

-- 
2.53.0



^ permalink raw reply related

* [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>

From: George Moussalem <george.moussalem@outlook.com>

Add support to bring up the M0 core of the bluetooth subsystem found in
the IPQ5018 SoC.

The signed firmware loaded is authenticated by TrustZone. If successful,
the M0 core boots the firmware and the peripheral is taken out of reset
using a Secure Channel Manager call to TrustZone.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/remoteproc/Kconfig            |  12 ++
 drivers/remoteproc/Makefile           |   1 +
 drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
 3 files changed, 274 insertions(+)

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index c521c744e7db..6b52f78f1427 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -163,6 +163,18 @@ config PRU_REMOTEPROC
 	  processors on various TI SoCs. It's safe to say N here if you're
 	  not interested in the PRU or if you are unsure.
 
+config QCOM_M0_BTSS_PIL
+	tristate "Qualcomm M0 BTSS Peripheral Image Loader"
+	depends on OF && ARCH_QCOM
+	select QCOM_MDT_LOADER
+	select QCOM_RPROC_COMMON
+	select QCOM_SCM
+	help
+	  Say y here to support the Secure Peripheral Imager Loader for the
+	  Qualcomm Bluetooth Subsystem running on the M0 remote processor found
+	  in the IPQ5018 SoC. The M0 core is started and stopped using a
+	  Secure Channel Manager call to TrustZone.
+
 config QCOM_PIL_INFO
 	tristate
 
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 1c7598b8475d..df80faf8d0df 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
 obj-$(CONFIG_KEYSTONE_REMOTEPROC)	+= keystone_remoteproc.o
 obj-$(CONFIG_MESON_MX_AO_ARC_REMOTEPROC)+= meson_mx_ao_arc.o
 obj-$(CONFIG_PRU_REMOTEPROC)		+= pru_rproc.o
+obj-$(CONFIG_QCOM_M0_BTSS_PIL)		+= qcom_m0_btss_pil.o
 obj-$(CONFIG_QCOM_PIL_INFO)		+= qcom_pil_info.o
 obj-$(CONFIG_QCOM_RPROC_COMMON)		+= qcom_common.o
 obj-$(CONFIG_QCOM_Q6V5_COMMON)		+= qcom_q6v5.o
diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
new file mode 100644
index 000000000000..7168e270e4d4
--- /dev/null
+++ b/drivers/remoteproc/qcom_m0_btss_pil.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "qcom_common.h"
+
+#define BTSS_PAS_ID	0xc
+
+struct m0_btss {
+	struct device *dev;
+	phys_addr_t mem_phys;
+	phys_addr_t mem_reloc;
+	void __iomem *mem_region;
+	size_t mem_size;
+	struct reset_control *btss_reset;
+};
+
+static int m0_btss_start(struct rproc *rproc)
+{
+	int ret;
+
+	if (!qcom_scm_pas_supported(BTSS_PAS_ID)) {
+		dev_err(rproc->dev.parent,
+			"PAS is not available for peripheral: 0x%x\n",
+			BTSS_PAS_ID);
+		return -ENODEV;
+	}
+
+	ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
+	if (ret) {
+		dev_err(rproc->dev.parent, "Failed to start rproc: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int m0_btss_stop(struct rproc *rproc)
+{
+	int ret;
+
+	if (rproc->state == RPROC_RUNNING || rproc->state == RPROC_CRASHED) {
+		ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
+		if (ret) {
+			dev_err(rproc->dev.parent, "Failed to stop rproc: %d\n",
+				ret);
+			return ret;
+		}
+
+		dev_info(rproc->dev.parent, "Successfully stopped rproc\n");
+	}
+
+	return 0;
+}
+
+static int m0_btss_load(struct rproc *rproc, const struct firmware *fw)
+{
+	struct m0_btss *desc = rproc->priv;
+	const struct elf32_phdr *phdrs;
+	const struct firmware *seg_fw;
+	const struct elf32_phdr *phdr;
+	const struct elf32_hdr *ehdr;
+	void __iomem *metadata;
+	size_t metadata_size;
+	int i, ret;
+
+	ehdr = (const struct elf32_hdr *)fw->data;
+	phdrs = (const struct elf32_phdr *)(ehdr + 1);
+
+	ret = request_firmware(&fw, rproc->firmware, rproc->dev.parent);
+	if (ret) {
+		dev_err(rproc->dev.parent, "Failed to request firmware: %d\n",
+			ret);
+		return ret;
+	}
+
+	metadata = qcom_mdt_read_metadata(fw, &metadata_size, rproc->firmware,
+					  rproc->dev.parent);
+	if (IS_ERR(metadata)) {
+		ret = PTR_ERR(metadata);
+		dev_err(rproc->dev.parent,
+			"Failed to read firmware metadata: %d\n", ret);
+		goto release_fw;
+	}
+
+	ret = qcom_scm_pas_init_image(BTSS_PAS_ID, metadata,
+				      metadata_size, NULL);
+	if (ret) {
+		dev_err(rproc->dev.parent, "PAS init image failed: %d\n", ret);
+		goto free_metadata;
+	}
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		char *seg_name __free(kfree) = kstrdup(rproc->firmware,
+						       GFP_KERNEL);
+		if (!seg_name)
+			return -ENOMEM;
+
+		phdr = &phdrs[i];
+
+		/* Only process valid loadable data segments */
+		if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
+			continue;
+
+		if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {
+			dev_err(rproc->dev.parent,
+				"Segment data exceeds the reserved memory area!\n");
+			goto free_metadata;
+		}
+
+		/* Check if firmware is split across multiple segment files */
+		if (phdr->p_offset > fw->size ||
+		    phdr->p_offset + phdr->p_filesz > fw->size) {
+			sprintf(seg_name + strlen(seg_name) - 3, "b%02d", i);
+			ret = request_firmware(&seg_fw, seg_name,
+					       rproc->dev.parent);
+			if (ret) {
+				dev_err(rproc->dev.parent,
+					"Could not find split segment binary: %s\n",
+					seg_name);
+				goto free_metadata;
+			}
+
+			/*
+			 * Use the virtual instead of the physical address as
+			 * the offset
+			 */
+			memcpy_toio(desc->mem_region + phdr->p_vaddr,
+				    seg_fw->data, phdr->p_filesz);
+
+			release_firmware(seg_fw);
+		} else {
+			memcpy_toio(desc->mem_region + phdr->p_vaddr,
+				    fw->data + phdr->p_offset, phdr->p_filesz);
+		}
+	}
+
+	return 0;
+
+free_metadata:
+	kfree(metadata);
+release_fw:
+	release_firmware(fw);
+	return ret;
+}
+
+static const struct rproc_ops m0_btss_ops = {
+	.start = m0_btss_start,
+	.stop = m0_btss_stop,
+	.load = m0_btss_load,
+	.get_boot_addr = rproc_elf_get_boot_addr,
+};
+
+static int m0_btss_alloc_memory_region(struct m0_btss *desc)
+{
+	struct device *dev = desc->dev;
+	struct resource res;
+	int ret;
+
+	ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+	if (ret) {
+		dev_err(dev, "unable to acquire memory-region resource\n");
+		return ret;
+	}
+
+	desc->mem_phys = res.start;
+	desc->mem_reloc = res.start;
+	desc->mem_size = resource_size(&res);
+	desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+	if (!desc->mem_region) {
+		dev_err(dev, "unable to map memory region: %pR\n", &res);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int m0_btss_pil_probe(struct platform_device *pdev)
+{
+	// struct reset_control *btss_reset;
+	struct device *dev = &pdev->dev;
+	const char *fw_name = NULL;
+	struct m0_btss *desc;
+	struct clk *lpo_clk;
+	struct rproc *rproc;
+	int ret;
+
+	ret = of_property_read_string(dev->of_node, "firmware-name",
+				      &fw_name);
+	if (ret < 0)
+		return ret;
+
+	rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
+				 fw_name, sizeof(*desc));
+	if (!rproc) {
+		dev_err(dev, "failed to allocate rproc\n");
+		return -ENOMEM;
+	}
+
+	desc = rproc->priv;
+	desc->dev = dev;
+
+	ret = m0_btss_alloc_memory_region(desc);
+	if (ret)
+		return ret;
+
+	lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
+	if (IS_ERR(lpo_clk))
+		return dev_err_probe(dev, PTR_ERR(lpo_clk),
+				     "Failed to get lpo clock\n");
+
+	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
+	if (IS_ERR_OR_NULL(desc->btss_reset))
+		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
+				     "unable to acquire btss_reset\n");
+
+	ret = reset_control_deassert(desc->btss_reset);
+	if (ret)
+		return dev_err_probe(rproc->dev.parent, ret,
+				     "Failed to deassert reset\n");
+
+	rproc->auto_boot = false;
+	ret = devm_rproc_add(dev, rproc);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, rproc);
+
+	return 0;
+}
+
+static const struct of_device_id m0_btss_of_match[] = {
+	{ .compatible = "qcom,ipq5018-btss-pil" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, m0_btss_of_match);
+
+static struct platform_driver m0_btss_pil_driver = {
+	.probe = m0_btss_pil_probe,
+	.driver = {
+		.name = "qcom-m0-btss-pil",
+		.of_match_table = m0_btss_of_match,
+	},
+};
+
+module_platform_driver(m0_btss_pil_driver);
+
+MODULE_DESCRIPTION("Qualcomm M0 Bluetooth Subsystem Peripheral Image Loader");
+MODULE_LICENSE("GPL");

-- 
2.53.0



^ permalink raw reply related

* [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem
In-Reply-To: <20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com>

From: George Moussalem <george.moussalem@outlook.com>

Document the M0 Bluetooth Subsystem remote processor core found in the
Qualcomm IPQ5018 SoC. Firmware loaded is authenticated via TrustZone.
The firmware running on the M0 core provides bluetooth functionality.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 .../bindings/remoteproc/qcom,m0-btss-pil.yaml      | 72 ++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
new file mode 100644
index 000000000000..397bb6815d71
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/qcom,m0-btss-pil.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm M0 BTSS Peripheral Image Loader
+
+maintainers:
+  - George Moussalem <george.moussalem@outlook.com>
+
+description:
+  Qualcomm M0 BTSS Peripheral Secure Image Loader loads firmware and powers up
+  the M0 BTSS remote processor core on the Qualcomm IPQ5018 SoC.
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq5018-btss-pil
+
+  firmware-name:
+    maxItems: 1
+    description: Firmware name for the M0 Bluetooth Subsystem core
+
+  clocks:
+    items:
+      - description: M0 BTSS low power oscillator clock
+
+  clock-names:
+    items:
+      - const: btss_lpo_clk
+
+  memory-region:
+    items:
+      - description: M0 BTSS reserved memory carveout
+
+  resets:
+    items:
+      - description: M0 BTSS reset
+
+  reset-names:
+    items:
+      - const: btss_reset
+
+required:
+  - compatible
+  - firmware-name
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+    #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+
+    remoteproc {
+      compatible = "qcom,ipq5018-btss-pil";
+
+      firmware-name = "qca/bt_fw_patch.mbn";
+
+      clocks = <&gcc GCC_BTSS_LPO_CLK>;
+      clock-names = "btss_lpo_clk";
+      resets = <&gcc GCC_BTSS_BCR>;
+      reset-names = "btss_reset";
+
+      memory-region = <&btss_region>;
+    };

-- 
2.53.0



^ permalink raw reply related

* [PATCH 0/6] Add support for IPQ5018 Bluetooth
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

Hello,

This patch series introduces Bluetooth support for IPQ5018.

Bluetooth firmware on the IPQ5018 platform runs on the M0 co-processor.
The firmware is loaded by the host into a dedicated reserved memory
carveout and authenticated by TrustZone. A Secure Channel Manager (SCM)
call safely brings the peripheral core out of reset.

A shared memory ring buffer topology handles runtime data frame
transport between the host APSS and the M0 core. An outgoing APCS IPC
bit and an incoming GIC interrupt handle host/guest signaling.

This series has been tested and verified on various IPQ5018 router
boards utilizing firmware extracted from GPL distributions, using both
mdt and mbn file formats.

[   14.781511] Bluetooth: hci0: QCA Product ID   :0x00000016
[   14.781583] Bluetooth: hci0: QCA SOC Version  :0x20180100
[   14.785926] Bluetooth: hci0: QCA ROM Version  :0x00000100
[   14.791546] Bluetooth: hci0: QCA Patch Version:0x00003ded
[   14.796698] Bluetooth: hci0: QCA controller version 0x01000100
[   14.802217] Bluetooth: hci0: QCA Downloading qca/mpnv10.bin
[   16.393850] Bluetooth: hci0: QCA Build Info: BTFW.MAPLE.1.0.0-00102-MPL_ROM_PATCHZ-1

Best regards,
George Moussalem

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
George Moussalem (6):
      dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
      remoteproc: qcom: Add M0 BTSS secure PIL driver
      Bluetooth: btqca: Add IPQ5018 support
      dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
      Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
      arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support

 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    |  63 ++
 .../bindings/remoteproc/qcom,m0-btss-pil.yaml      |  72 ++
 arch/arm64/boot/dts/qcom/ipq5018.dtsi              |  34 +-
 drivers/bluetooth/Kconfig                          |  11 +
 drivers/bluetooth/Makefile                         |   1 +
 drivers/bluetooth/btqca.c                          |  16 +
 drivers/bluetooth/btqca.h                          |   3 +
 drivers/bluetooth/btqcomipc.c                      | 939 +++++++++++++++++++++
 drivers/remoteproc/Kconfig                         |  12 +
 drivers/remoteproc/Makefile                        |   1 +
 drivers/remoteproc/qcom_m0_btss_pil.c              | 261 ++++++
 11 files changed, 1412 insertions(+), 1 deletion(-)
---
base-commit: 4d1ab324fcb7d20df5a071edb0304461846fdc12
change-id: 20260625-ipq5018-bluetooth-06ff66c9d753
prerequisite-message-id: <20260612-block-as-nvmem-v5-0-95e0b30fff90@oss.qualcomm.com>
prerequisite-patch-id: 6ce8686c1683f468d86b4502f5ec9d19c392a382
prerequisite-patch-id: e362f7fcbacff716b7ef720e6780786a7d88c013
prerequisite-patch-id: 9168930e40551e842c8171d5433a6f39ad4b78a4
prerequisite-patch-id: 64fecfbd1e085d7d2ab0ae23295ca34ec8e14c5e
prerequisite-patch-id: 566804aaa690ee9aa285d0fd75fd16d94fbadebf
prerequisite-patch-id: dc18bec338f54b3051f4523f9d1d3c0566a20ccd
prerequisite-patch-id: b6b3eb46429936ab49423d295433daf47981db0f
prerequisite-patch-id: 75caa99e3bbcdf41b6462b9f5f703bea1d4a65fa
prerequisite-patch-id: 35e9968f482f78ca233eb0306d9c5fdbff093175

Best regards,
-- 
George Moussalem <george.moussalem@outlook.com>



^ permalink raw reply

* Re: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag support
From: Manivannan Sadhasivam @ 2026-06-25 13:57 UTC (permalink / raw)
  To: Sherry Sun
  Cc: Frank Li (OSS), Sherry Sun (OSS), robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, Frank Li,
	s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
	Amitkumar Karwar, Neeraj Sanjay Kale, marcel@holtmann.org,
	luiz.dentz@gmail.com, Hongxing Zhu, l.stach@pengutronix.de,
	lpieralisi@kernel.org, kwilczynski@kernel.org,
	bhelgaas@google.com, brgl@kernel.org, imx@lists.linux.dev,
	linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-bluetooth@vger.kernel.org, linux-pm@vger.kernel.org
In-Reply-To: <VI0PR04MB12114BCBD405505888063284492EC2@VI0PR04MB12114.eurprd04.prod.outlook.com>

On Thu, Jun 25, 2026 at 07:25:46AM +0000, Sherry Sun wrote:
> > Subject: Re: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag support
> > 
> > On Wed, Jun 24, 2026 at 07:09:26AM +0000, Sherry Sun wrote:
> > > > Subject: Re: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag
> > > > support
> > > >
> > > > On Tue, Jun 23, 2026 at 11:07:28AM +0800, Sherry Sun (OSS) wrote:
> > > > > From: Sherry Sun <sherry.sun@nxp.com>
> > > > >
> > > > > Use dw_pcie_rp::skip_pwrctrl_off to avoid powering off devices
> > > > > during suspend to preserve wakeup capability of the devices and
> > > > > also not to power on the devices in the init path.
> > > > > This allows controller power-off to be skipped when some devices(e.g.
> > > > > M.2 cards key E without auxiliary power) required to support PCIe
> > > > > L2 link state and wake-up mechanisms.
> > > > >
> > > > > Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> > > > > ---
> > > > >  drivers/pci/controller/dwc/pci-imx6.c | 36
> > > > > +++++++++++++++++----------
> > > > >  1 file changed, 23 insertions(+), 13 deletions(-)
> > > > >
> > > > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> > > > > b/drivers/pci/controller/dwc/pci-imx6.c
> > > > > index 0fa716d1ed75..ff5a9565dbbf 100644
> > > > > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > > > > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > > > > @@ -1382,16 +1382,20 @@ static int imx_pcie_host_init(struct
> > > > > dw_pcie_rp
> > > > *pp)
> > > > >  		}
> > > > >  	}
> > > > >
> > > > > -	ret = pci_pwrctrl_create_devices(dev);
> > > > > -	if (ret) {
> > > > > -		dev_err(dev, "failed to create pwrctrl devices\n");
> > > > > -		goto err_reg_disable;
> > > > > +	if (!pci->suspended) {
> > > > > +		ret = pci_pwrctrl_create_devices(dev);
> > > >
> > > > Is possible move pci_pwrctrl_create_devices() of
> > > > pci_pwrctrl_create_devices
> > > >
> > > > and call it direct at probe() function, like other regulator_get function.
> > > >
> > >
> > > Hi Frank,
> > > That makes sense. However, if we move pci_pwrctrl_create_devices () to
> > > probe(), we may need to add the following goto err_pwrctrl_destroy
> > > path in imx_pcie_probe() to properly handle errors from
> > > pci_pwrctrl_power_on_devices(), is that acceptable?
> > 
> > Can you add a API devm_pci_pwrctrl_create_devices() ?
> > 
> 
> Hi Frank, we cannot unconditionally destroy the pwrctrl devices
> when probing fails by using devm API.
> Since we need to check the return value of
> pci_pwrctrl_power_on_devices() for example EPROBE_DEFER to decide
> whether to destroy the pwrctrl devices to avoid the deferred probe loop.
> 
> You can find more related discussion here.
> https://lore.kernel.org/all/tutxwjciedqoje5wxvtin4h637auni5zzpvb7rtfg4uticxoux@yfl6xg7oht7t/
> 

Yes. Ideally we should try to do a blocking wait in the pwrctrl core until all
the pwrctrl drivers are probed, instead of deferring probe. Hopefully, I'll get
to it soon.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* RE: [v2] Bluetooth: btusb: Add USB ID 13d3:3625 for MediaTek MT7922
From: bluez.test.bot @ 2026-06-25 10:16 UTC (permalink / raw)
  To: linux-bluetooth, monesss315
In-Reply-To: <20260625083230.300651-1-monesss315@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1449 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/project/bluetooth/list/?series=1116370

---Test result---

Test Summary:
CheckPatch                    PASS      1.08 seconds
VerifyFixes                   PASS      0.27 seconds
VerifySignedoff               PASS      0.21 seconds
GitLint                       FAIL      0.52 seconds
SubjectPrefix                 PASS      0.24 seconds
BuildKernel                   PASS      27.04 seconds
CheckAllWarning               PASS      29.69 seconds
CheckSparse                   PASS      29.46 seconds
BuildKernel32                 PASS      26.30 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      500.16 seconds
IncrementalBuild              PASS      25.76 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[v2] Bluetooth: btusb: Add USB ID 13d3:3625 for MediaTek MT7922

63: B1 Line exceeds max length (82>80): "    - Add /sys/kernel/debug/usb/devices output to the commit message (Paul Menzel)"
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/351

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH v2] Bluetooth: btusb: Add USB ID 13d3:3625 for MediaTek MT7922
From: Gustavo Evgucci @ 2026-06-25  8:32 UTC (permalink / raw)
  To: Luiz Augusto von Dentz, Marcel Holtmann
  Cc: linux-bluetooth, linux-kernel, Paul Menzel, Gustavo Evgucci
In-Reply-To: <20260623114937.6385-1-monesss315@gmail.com>

The IMC Networks MT7922 Bluetooth adapter with USB ID 13d3:3625 is not
recognized as a MediaTek device because it is missing from the btusb
device ID table. As a result, btmtk firmware loading is never triggered
and the HCI reset command times out with -ETIMEDOUT.

Add the device with BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH flags,
consistent with the neighboring 13d3:3627, 13d3:3628 and 13d3:3630
entries which use the same chip.

Tested on: MediaTek MT7922 (Wi-Fi 6E combo card, IMC Networks BT USB
interface), kernel 7.0.11-arch1-1.

/sys/kernel/debug/usb/devices:

T:  Bus=01 Lev=01 Prnt=01 Port=12 Cnt=03 Dev#=  4 Spd=480  MxCh= 0
D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=13d3 ProdID=3625 Rev= 1.00
S:  Manufacturer=MediaTek Inc.
S:  Product=Wireless_Device
S:  SerialNumber=000000000
C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA
A:  FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01
I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=81(I) Atr=03(Int.) MxPS=  16 Ivl=125us
E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   0 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   0 Ivl=1ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   9 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   9 Ivl=1ms
I:  If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  17 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  17 Ivl=1ms
I:  If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  25 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  25 Ivl=1ms
I:  If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  33 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  33 Ivl=1ms
I:  If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  49 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  49 Ivl=1ms
I:  If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  63 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  63 Ivl=1ms
I:  If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=8a(I) Atr=03(Int.) MxPS=  64 Ivl=125us
E:  Ad=0a(O) Atr=03(Int.) MxPS=  64 Ivl=125us
I:* If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=8a(I) Atr=03(Int.) MxPS= 512 Ivl=125us
E:  Ad=0a(O) Atr=03(Int.) MxPS= 512 Ivl=125us

Signed-off-by: Gustavo Evgucci <monesss315@gmail.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
---

Notes:
    v2:
    - Add /sys/kernel/debug/usb/devices output to the commit message (Paul Menzel)
    - Add Reviewed-by: Paul Menzel
    - Resend to all maintainers from scripts/get_maintainer.pl

 drivers/bluetooth/btusb.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 7f14ce9..f829203 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -798,6 +798,8 @@ static const struct usb_device_id quirks_table[] = {
 						     BTUSB_WIDEBAND_SPEECH },
 	{ USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK |
 						     BTUSB_WIDEBAND_SPEECH },
+	{ USB_DEVICE(0x13d3, 0x3625), .driver_info = BTUSB_MEDIATEK |
+						     BTUSB_WIDEBAND_SPEECH },
 	{ USB_DEVICE(0x13d3, 0x3627), .driver_info = BTUSB_MEDIATEK |
 						     BTUSB_WIDEBAND_SPEECH },
 	{ USB_DEVICE(0x13d3, 0x3628), .driver_info = BTUSB_MEDIATEK |
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v2] Bluetooth: virtio_bt: fix cleanup paths
From: Dan Carpenter @ 2026-06-25  8:06 UTC (permalink / raw)
  To: Haoxiang Li
  Cc: marcel, luiz.dentz, yangyingliang, mst, linux-bluetooth,
	linux-kernel, stable
In-Reply-To: <20260625020159.3446736-1-haoxiang_li2024@163.com>

On Thu, Jun 25, 2026 at 10:01:59AM +0800, Haoxiang Li wrote:
> virtbt_probe() registers the HCI device before opening the virtio
> Bluetooth device. If virtbt_open_vdev() fails, the error path frees
> the HCI device without unregistering it first. The probe error paths
> also leak the virtio_bluetooth structure after it has been allocated.
> 
> Rework the probe error handling into an unwind ladder so each failure
> path releases the resources acquired earlier. Also close the virtio
> device before unregistering the HCI device in virtbt_remove(), matching
> the cleanup order used by the probe failure path.
> 
> Fixes: afd2daa26c7a ("Bluetooth: Add support for virtio transport driver")
> Fixes: dc65b4b0f90a ("Bluetooth: virtio_bt: fix device removal")
> Cc: stable@vger.kernel.org
> Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
> ---
> Changes in v2:
>  - Rework virtbt_probe() error paths into an unwind ladder.
>  - Free vbt on probe failures.
>  - Reset the virtio device and unregister the HCI device before freeing it
>    when virtbt_open_vdev() fails.
>  - Close the virtio device before unregistering the HCI device in remove().
> 
>    Thanks Dan for the suggestions. The blog is very helpful.
> ---
>  drivers/bluetooth/virtio_bt.c | 23 ++++++++++++++---------
>  1 file changed, 14 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
> index 140ab55c9fc5..4ca9b76f6410 100644
> --- a/drivers/bluetooth/virtio_bt.c
> +++ b/drivers/bluetooth/virtio_bt.c
> @@ -311,12 +311,12 @@ static int virtbt_probe(struct virtio_device *vdev)
>  
>  	err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, vqs_info, NULL);
>  	if (err)
> -		return err;
> +		goto err_free_vbt;
>  
>  	hdev = hci_alloc_dev();
>  	if (!hdev) {
>  		err = -ENOMEM;
> -		goto failed;
> +		goto err_del_vqs;
>  	}
>  
>  	vbt->hdev = hdev;
> @@ -383,23 +383,28 @@ static int virtbt_probe(struct virtio_device *vdev)
>  	if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT))
>  		hci_set_aosp_capable(hdev);
>  
> -	if (hci_register_dev(hdev) < 0) {
> -		hci_free_dev(hdev);
> +	err = hci_register_dev(hdev);
> +	if (err < 0) {
>  		err = -EBUSY;
> -		goto failed;
> +		goto err_free_hdev;
>  	}
>  
>  	virtio_device_ready(vdev);
>  	err = virtbt_open_vdev(vbt);
>  	if (err)
> -		goto open_failed;
> +		goto err_reset_vdev;
>  
>  	return 0;
>  
> -open_failed:
> +err_reset_vdev:
> +	virtio_reset_device(vdev);

I'm not sure that this reset is necessary.  I suspect that it isn't,
but I don't know for sure.  In a situation like this, I'd probably
err on the side of only fixing things which I know and leaving the
rest as a leak or whatever.

Otherwise it looks correct to me.

regards,
dan carpenter


^ permalink raw reply

* RE: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag support
From: Sherry Sun @ 2026-06-25  7:25 UTC (permalink / raw)
  To: Frank Li (OSS)
  Cc: Sherry Sun (OSS), robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, Frank Li, s.hauer@pengutronix.de,
	kernel@pengutronix.de, festevam@gmail.com, Amitkumar Karwar,
	Neeraj Sanjay Kale, marcel@holtmann.org, luiz.dentz@gmail.com,
	Hongxing Zhu, l.stach@pengutronix.de, lpieralisi@kernel.org,
	kwilczynski@kernel.org, mani@kernel.org, bhelgaas@google.com,
	brgl@kernel.org, imx@lists.linux.dev, linux-pci@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org,
	linux-pm@vger.kernel.org
In-Reply-To: <ajwOvZUlOEQzmjsu@SMW015318>

> Subject: Re: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag support
> 
> On Wed, Jun 24, 2026 at 07:09:26AM +0000, Sherry Sun wrote:
> > > Subject: Re: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag
> > > support
> > >
> > > On Tue, Jun 23, 2026 at 11:07:28AM +0800, Sherry Sun (OSS) wrote:
> > > > From: Sherry Sun <sherry.sun@nxp.com>
> > > >
> > > > Use dw_pcie_rp::skip_pwrctrl_off to avoid powering off devices
> > > > during suspend to preserve wakeup capability of the devices and
> > > > also not to power on the devices in the init path.
> > > > This allows controller power-off to be skipped when some devices(e.g.
> > > > M.2 cards key E without auxiliary power) required to support PCIe
> > > > L2 link state and wake-up mechanisms.
> > > >
> > > > Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> > > > ---
> > > >  drivers/pci/controller/dwc/pci-imx6.c | 36
> > > > +++++++++++++++++----------
> > > >  1 file changed, 23 insertions(+), 13 deletions(-)
> > > >
> > > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> > > > b/drivers/pci/controller/dwc/pci-imx6.c
> > > > index 0fa716d1ed75..ff5a9565dbbf 100644
> > > > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > > > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > > > @@ -1382,16 +1382,20 @@ static int imx_pcie_host_init(struct
> > > > dw_pcie_rp
> > > *pp)
> > > >  		}
> > > >  	}
> > > >
> > > > -	ret = pci_pwrctrl_create_devices(dev);
> > > > -	if (ret) {
> > > > -		dev_err(dev, "failed to create pwrctrl devices\n");
> > > > -		goto err_reg_disable;
> > > > +	if (!pci->suspended) {
> > > > +		ret = pci_pwrctrl_create_devices(dev);
> > >
> > > Is possible move pci_pwrctrl_create_devices() of
> > > pci_pwrctrl_create_devices
> > >
> > > and call it direct at probe() function, like other regulator_get function.
> > >
> >
> > Hi Frank,
> > That makes sense. However, if we move pci_pwrctrl_create_devices () to
> > probe(), we may need to add the following goto err_pwrctrl_destroy
> > path in imx_pcie_probe() to properly handle errors from
> > pci_pwrctrl_power_on_devices(), is that acceptable?
> 
> Can you add a API devm_pci_pwrctrl_create_devices() ?
> 

Hi Frank, we cannot unconditionally destroy the pwrctrl devices
when probing fails by using devm API.
Since we need to check the return value of
pci_pwrctrl_power_on_devices() for example EPROBE_DEFER to decide
whether to destroy the pwrctrl devices to avoid the deferred probe loop.

You can find more related discussion here.
https://lore.kernel.org/all/tutxwjciedqoje5wxvtin4h637auni5zzpvb7rtfg4uticxoux@yfl6xg7oht7t/

Best Regards
Sherry
> 
> >
> > @@ -1960,11 +1949,15 @@ static int imx_pcie_probe(struct
> platform_device *pdev)
> >         if (ret)
> >                 return ret;
> >
> > +       ret = pci_pwrctrl_create_devices(dev);
> > +       if (ret)
> > +               return dev_err_probe(dev, ret, "failed to create
> > + pwrctrl devices\n");
> > +
> >         pci->use_parent_dt_ranges = true;
> >         if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) {
> >                 ret = imx_add_pcie_ep(imx_pcie, pdev);
> >                 if (ret < 0)
> > -                       return ret;
> > +                       goto err_pwrctrl_destroy;
> >
> >                 /*
> >                  * FIXME: Only single Device (EPF) is supported due to
> > the @@ -1979,7 +1972,7 @@ static int imx_pcie_probe(struct
> platform_device *pdev)
> >                 pci->pp.use_atu_msg = true;
> >                 ret = dw_pcie_host_init(&pci->pp);
> >                 if (ret < 0)
> > -                       return ret;
> > +                       goto err_pwrctrl_destroy;
> >
> >                 if (pci_msi_enabled()) {
> >                         u8 offset = dw_pcie_find_capability(pci,
> > PCI_CAP_ID_MSI); @@ -1991,6 +1984,11 @@ static int
> imx_pcie_probe(struct platform_device *pdev)
> >         }
> >
> >         return 0;
> > +
> > +err_pwrctrl_destroy:
> > +       if (ret != -EPROBE_DEFER)
> > +               pci_pwrctrl_destroy_devices(dev);
> > +       return ret;
> >  }
> >
> > Best Regards
> > Sherry
> >
> > >
> > > > +		if (ret) {
> > > > +			dev_err(dev, "failed to create pwrctrl devices\n");
> > > > +			goto err_reg_disable;
> > > > +		}
> > > >  	}
> > > >
> > > > -	ret = pci_pwrctrl_power_on_devices(dev);
> > > > -	if (ret) {
> > > > -		dev_err(dev, "failed to power on pwrctrl devices\n");
> > > > -		goto err_pwrctrl_destroy;
> > > > +	if (!pp->skip_pwrctrl_off) {
> > > > +		ret = pci_pwrctrl_power_on_devices(dev);
> > > > +		if (ret) {
> > > > +			dev_err(dev, "failed to power on pwrctrl devices\n");
> > > > +			goto err_pwrctrl_destroy;
> > > > +		}
> > > >  	}
> > > >
> > > >  	ret = imx_pcie_clk_enable(imx_pcie); @@ -1460,9 +1464,10 @@
> > > static
> > > > int imx_pcie_host_init(struct dw_pcie_rp *pp)
> > > >  err_clk_disable:
> > > >  	imx_pcie_clk_disable(imx_pcie);
> > > >  err_pwrctrl_power_off:
> > > > -	pci_pwrctrl_power_off_devices(dev);
> > > > +	if (!pp->skip_pwrctrl_off)
> > > > +		pci_pwrctrl_power_off_devices(dev);
> > > >  err_pwrctrl_destroy:
> > > > -	if (ret != -EPROBE_DEFER)
> > > > +	if (ret != -EPROBE_DEFER && !pci->suspended)
> > > >  		pci_pwrctrl_destroy_devices(dev);
> > > >  err_reg_disable:
> > > >  	if (imx_pcie->vpcie)
> > > > @@ -1482,7 +1487,8 @@ static void imx_pcie_host_exit(struct
> > > > dw_pcie_rp
> > > *pp)
> > > >  	}
> > > >  	imx_pcie_clk_disable(imx_pcie);
> > > >
> > > > -	pci_pwrctrl_power_off_devices(pci->dev);
> > > > +	if (!pci->pp.skip_pwrctrl_off)
> > > > +		pci_pwrctrl_power_off_devices(pci->dev);
> > > >  	if (imx_pcie->vpcie)
> > > >  		regulator_disable(imx_pcie->vpcie);
> > > >  }
> > > > @@ -1990,12 +1996,16 @@ static int imx_pcie_probe(struct
> > > > platform_device *pdev)  static void imx_pcie_shutdown(struct
> > > > platform_device *pdev)  {
> > > >  	struct imx_pcie *imx_pcie = platform_get_drvdata(pdev);
> > > > +	struct dw_pcie *pci = imx_pcie->pci;
> > > > +	struct dw_pcie_rp *pp = &pci->pp;
> > > >
> > > >  	/* bring down link, so bootloader gets clean state in case of reboot */
> > > >  	imx_pcie_assert_core_reset(imx_pcie);
> > > >  	imx_pcie_assert_perst(imx_pcie, true);
> > > > -	pci_pwrctrl_power_off_devices(&pdev->dev);
> > > > -	pci_pwrctrl_destroy_devices(&pdev->dev);
> > > > +	if (!pp->skip_pwrctrl_off)
> > > > +		pci_pwrctrl_power_off_devices(&pdev->dev);
> > > > +	if (!pci->suspended)
> > > > +		pci_pwrctrl_destroy_devices(&pdev->dev);
> > > >  }
> > > >
> > > >  static const struct imx_pcie_drvdata drvdata[] = {
> > > > --
> > > > 2.50.1
> > > >
> > > >

^ permalink raw reply

* RE: [v2] Bluetooth: virtio_bt: fix cleanup paths
From: bluez.test.bot @ 2026-06-25  2:41 UTC (permalink / raw)
  To: linux-bluetooth, haoxiang_li2024
In-Reply-To: <20260625020159.3446736-1-haoxiang_li2024@163.com>

[-- Attachment #1: Type: text/plain, Size: 1181 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/project/bluetooth/list/?series=1116220

---Test result---

Test Summary:
CheckPatch                    PASS      1.39 seconds
VerifyFixes                   PASS      0.34 seconds
VerifySignedoff               PASS      0.27 seconds
GitLint                       PASS      0.57 seconds
SubjectPrefix                 PASS      0.28 seconds
BuildKernel                   PASS      25.78 seconds
CheckAllWarning               PASS      28.53 seconds
CheckSparse                   PASS      27.60 seconds
BuildKernel32                 PASS      25.17 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      458.20 seconds
IncrementalBuild              PASS      25.61 seconds

Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/350

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re:Re: [PATCH] Bluetooth: virtio_bt: unregister HCI device on open failure
From: haoxiang_li2024 @ 2026-06-25  2:09 UTC (permalink / raw)
  To: Paul Menzel, martin.petersen@oracle.com
  Cc: marcel, luiz.dentz, yangyingliang, mst, error27, linux-bluetooth,
	linux-kernel, stable
In-Reply-To: <17c94df1-3659-4d21-b327-ad52d498ba9c@molgen.mpg.de>


Hi Paul,

At 2026-06-24 17:10:55, "Paul Menzel" <pmenzel@molgen.mpg.de> wrote:
>
>Should you resend, please re-flow for 72/75 characters, so only three 
>lines are used.
>

Thank you for the review!

Following Dan's suggestions, I reworked the patch in v2. I also made the
commit message more compact based on your suggestion. 

>
>PS: gemini/gemini-3.1-pro-preview found an unrelated issue to the patch 
>during review of this patch [1].
>
>> This is a pre-existing issue, but does this error path safely clean up the
>> active virtqueues? 
>> Earlier in virtbt_probe(), virtio_device_ready(vdev) marks the device as
>> active. If virtbt_open_vdev() subsequently fails, the code jumps to the
>> open_failed label and eventually reaches here to call del_vqs(vdev).
>> Deleting virtqueues without calling virtio_reset_device(vdev) first violates
>> the VirtIO API contract for active devices. It could allow the host or
>> hypervisor to access guest memory that has already been freed by del_vqs(),
>> potentially leading to a use-after-free.
>> Should virtio_reset_device(vdev) be called before tearing down the
>> virtqueues in this error path?
>
>No idea, how to best track these things.
>

The issue noted in your PS is now handled by resetting the virtio device
before tearing down the virtqueues in the virtbt_open_vdev() failure path.

Since v2 changes more than the original one-line fix, I did not carry your
Reviewed-by tag. Thanks again!

Thanks,
Haoxiang

^ permalink raw reply

* Re: [PATCH 12/13] Bluetooth: btqca: Fix undetected error HCI status in qca_send_reset()
From: Zijun Hu @ 2026-06-25  2:08 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Marcel Holtmann, Rocky Liao, Bartosz Golaszewski,
	Ben Young Tae Kim, Balakrishna Godavarthi, Matthias Kaehlcke,
	Zijun Hu, linux-bluetooth, linux-kernel, Luiz Augusto von Dentz,
	linux-arm-msm
In-Reply-To: <CABBYNZKx3+08Ajisy7-sHi0TZJNrbgzBzdszcKfiZUiNOdhByA@mail.gmail.com>

On 6/23/2026 11:15 PM, Luiz Augusto von Dentz wrote:
>>         u8 cmd;
>> @@ -990,7 +971,8 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
>>         }
>>
>>         /* Perform HCI reset */
>> -       err = qca_send_reset(hdev);
>> +       bt_dev_dbg(hdev, "QCA HCI_RESET");
>> +       err = __hci_reset_sync(hdev);
> So All the fuss about the reset is just to use it here? Actually the
> distinction between -err and status is rather important. The first
> means the command didn't run (timeout, cancel, etc), or it did run but
> returned a status != 0. If you want to capture both then you must use
> if (err). And yes there are parts of the code that test for < 0, but
> that is either a bug or they are intentionally ignoring the reset
> status as it is probably a non-recoverable error at that point.

  1. Agree: > 0 for HCI status code (controller-side error), < 0 for command not executed
  (timeout, cancel, transport failure). __hci_reset_sync() was introduced in this series and will
  be refactored to follow this convention in v2.
  2. Looking at drivers/bluetooth/, 7 out of 12 HCI reset call sites in the init/setup phase
  silently ignore the controller status — not sure if this is intentional or not.
  3. qca_set_bdaddr() and qca_send_reset() will be fixed to return the HCI status code and let the
  caller decide whether to ignore it.

  Will address all of the above in v2.


^ permalink raw reply

* [PATCH v2] Bluetooth: virtio_bt: fix cleanup paths
From: Haoxiang Li @ 2026-06-25  2:01 UTC (permalink / raw)
  To: marcel, luiz.dentz, yangyingliang, error27, mst
  Cc: linux-bluetooth, linux-kernel, Haoxiang Li, stable

virtbt_probe() registers the HCI device before opening the virtio
Bluetooth device. If virtbt_open_vdev() fails, the error path frees
the HCI device without unregistering it first. The probe error paths
also leak the virtio_bluetooth structure after it has been allocated.

Rework the probe error handling into an unwind ladder so each failure
path releases the resources acquired earlier. Also close the virtio
device before unregistering the HCI device in virtbt_remove(), matching
the cleanup order used by the probe failure path.

Fixes: afd2daa26c7a ("Bluetooth: Add support for virtio transport driver")
Fixes: dc65b4b0f90a ("Bluetooth: virtio_bt: fix device removal")
Cc: stable@vger.kernel.org
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
Changes in v2:
 - Rework virtbt_probe() error paths into an unwind ladder.
 - Free vbt on probe failures.
 - Reset the virtio device and unregister the HCI device before freeing it
   when virtbt_open_vdev() fails.
 - Close the virtio device before unregistering the HCI device in remove().

   Thanks Dan for the suggestions. The blog is very helpful.
---
 drivers/bluetooth/virtio_bt.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 140ab55c9fc5..4ca9b76f6410 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -311,12 +311,12 @@ static int virtbt_probe(struct virtio_device *vdev)
 
 	err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, vqs_info, NULL);
 	if (err)
-		return err;
+		goto err_free_vbt;
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
 		err = -ENOMEM;
-		goto failed;
+		goto err_del_vqs;
 	}
 
 	vbt->hdev = hdev;
@@ -383,23 +383,28 @@ static int virtbt_probe(struct virtio_device *vdev)
 	if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT))
 		hci_set_aosp_capable(hdev);
 
-	if (hci_register_dev(hdev) < 0) {
-		hci_free_dev(hdev);
+	err = hci_register_dev(hdev);
+	if (err < 0) {
 		err = -EBUSY;
-		goto failed;
+		goto err_free_hdev;
 	}
 
 	virtio_device_ready(vdev);
 	err = virtbt_open_vdev(vbt);
 	if (err)
-		goto open_failed;
+		goto err_reset_vdev;
 
 	return 0;
 
-open_failed:
+err_reset_vdev:
+	virtio_reset_device(vdev);
+	hci_unregister_dev(hdev);
+err_free_hdev:
 	hci_free_dev(hdev);
-failed:
+err_del_vqs:
 	vdev->config->del_vqs(vdev);
+err_free_vbt:
+	kfree(vbt);
 	return err;
 }
 
@@ -408,10 +413,10 @@ static void virtbt_remove(struct virtio_device *vdev)
 	struct virtio_bluetooth *vbt = vdev->priv;
 	struct hci_dev *hdev = vbt->hdev;
 
-	hci_unregister_dev(hdev);
 	virtio_reset_device(vdev);
 	virtbt_close_vdev(vbt);
 
+	hci_unregister_dev(hdev);
 	hci_free_dev(hdev);
 	vbt->hdev = NULL;
 
-- 
2.25.1


^ permalink raw reply related

* RE: Bluetooth: sco: Fix a race condition in sco_sock_timeout()
From: bluez.test.bot @ 2026-06-25  0:22 UTC (permalink / raw)
  To: linux-bluetooth, iam
In-Reply-To: <20260624213304.1460149-2-iam@sung-woo.kim>

[-- Attachment #1: Type: text/plain, Size: 3865 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/project/bluetooth/list/?series=1116158

---Test result---

Test Summary:
CheckPatch                    FAIL      1.08 seconds
VerifyFixes                   PASS      0.21 seconds
VerifySignedoff               PASS      0.20 seconds
GitLint                       FAIL      0.45 seconds
SubjectPrefix                 PASS      0.18 seconds
BuildKernel                   PASS      28.74 seconds
CheckAllWarning               PASS      29.24 seconds
CheckSparse                   PASS      28.03 seconds
BuildKernel32                 PASS      26.10 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      491.15 seconds
TestRunner_sco-tester         PASS      31.94 seconds
IncrementalBuild              PASS      26.32 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: sco: Fix a race condition in sco_sock_timeout()
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#109: 
ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline]

total: 0 errors, 1 warnings, 0 checks, 24 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14643876.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
Bluetooth: sco: Fix a race condition in sco_sock_timeout()

15: B3 Line contains hard tab characters (\t): "	      sco_conn_free()"
16: B3 Line contains hard tab characters (\t): "	        disable_delayed_work_sync()"
17: B3 Line contains hard tab characters (\t): "	                           lock(sk) // <-- SAME LOCK"
28: B1 Line exceeds max length (155>80): "ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline]"
29: B1 Line exceeds max length (152>80): "ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_read_lock sect/v6.13-rc4/./include/linux/rcupdate.h:849 [inline]"
30: B1 Line exceeds max length (148>80): "ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4137 [inline]"
31: B1 Line exceeds max length (146>80): "ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: __flush_work+0xd1/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195"
34: B1 Line exceeds max length (128>80): "ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline]"
35: B1 Line exceeds max length (133>80): "ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_sock_close+0x25/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:524"
47: B1 Line exceeds max length (82>80): "       process_scheduled_works+0xa99/0x18f0 sect/v6.13-rc4/kernel/workqueue.c:3310"
63: B1 Line exceeds max length (81>80): "       disable_delayed_work_sync+0xbb/0xf0 sect/v6.13-rc4/kernel/workqueue.c:4514"
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/349

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH] Bluetooth: sco: Fix a race condition in sco_sock_timeout()
From: Sungwoo Kim @ 2026-06-24 22:32 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz
  Cc: Dave Tian, Luiz Augusto von Dentz, linux-bluetooth, linux-kernel
In-Reply-To: <20260624213304.1460149-2-iam@sung-woo.kim>

On Wed, Jun 24, 2026 at 5:36 PM Sungwoo Kim <iam@sung-woo.kim> wrote:
>
> sco_sock_timeout() runs asynchronously and lock_sock(sk). If the socket
> is closing while the timer is running, it holds the same lock
> (lock_sock(sk)) twice, leading to a deadlock.
>
> CPU 0                      CPU 1
> ====================       ======================
> sco_sock_close()
>                            sco_sock_timeout()
> lock_sock(sk) // <-- LOCK
>   __sco_sock_close()
>     sco_chan_del()
>       sco_conn_put()
>               sco_conn_free()
>                 disable_delayed_work_sync()
>                                    lock(sk) // <-- SAME LOCK
>
> Fix this by moving disable_delayed_work_sync() outside of lock_sock(sk),
> ensuring that no lock_sock(sk) is held before sco_sock_timeout().
>
> Lockdep splat:
>
> WARNING: possible circular locking dependency detected
> 6.13.0-rc4 #7 Not tainted
>
> syz-executor292/9514 is trying to acquire lock:
> ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline]
> ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_read_lock sect/v6.13-rc4/./include/linux/rcupdate.h:849 [inline]
> ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4137 [inline]
> ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: __flush_work+0xd1/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195
>
> but task is already holding lock:
> ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline]
> ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_sock_close+0x25/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:524
>
> which lock already depends on the new lock.
>
> the existing dependency chain (in reverse order) is:
>
> -> #1 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}:
>        lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849
>        lock_sock_nested+0x48/0x130 sect/v6.13-rc4/net/core/sock.c:3622
>        lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline]
>        sco_sock_timeout+0xbe/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:158
>        process_one_work sect/v6.13-rc4/kernel/workqueue.c:3229 [inline]
>        process_scheduled_works+0xa99/0x18f0 sect/v6.13-rc4/kernel/workqueue.c:3310
>        worker_thread+0x8a9/0xd80 sect/v6.13-rc4/kernel/workqueue.c:3391
>        kthread+0x2c6/0x360 sect/v6.13-rc4/kernel/kthread.c:389
>        ret_from_fork+0x4e/0x80 sect/v6.13-rc4/arch/x86/kernel/process.c:147
>        ret_from_fork_asm+0x1a/0x30 sect/v6.13-rc4/arch/x86/entry/entry_64.S:244
>
> -> #0 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}:
>        check_prev_add sect/v6.13-rc4/kernel/locking/lockdep.c:3161 [inline]
>        check_prevs_add sect/v6.13-rc4/kernel/locking/lockdep.c:3280 [inline]
>        validate_chain+0x1888/0x5760 sect/v6.13-rc4/kernel/locking/lockdep.c:3904
>        __lock_acquire+0x13b4/0x2120 sect/v6.13-rc4/kernel/locking/lockdep.c:5226
>        lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849
>        touch_work_lockdep_map sect/v6.13-rc4/kernel/workqueue.c:3909 [inline]
>        start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4163 [inline]
>        __flush_work+0x70f/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195
>        __cancel_work_sync sect/v6.13-rc4/kernel/workqueue.c:4351 [inline]
>        disable_delayed_work_sync+0xbb/0xf0 sect/v6.13-rc4/kernel/workqueue.c:4514
>        sco_conn_free sect/v6.13-rc4/net/bluetooth/sco.c:95 [inline]
>        kref_put sect/v6.13-rc4/./include/linux/kref.h:65 [inline]
>        sco_conn_put+0x18f/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:107
>        sco_chan_del+0xe2/0x210 sect/v6.13-rc4/net/bluetooth/sco.c:236
>        sco_sock_close+0x8f/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:526
>        sco_sock_release+0x62/0x2d0 sect/v6.13-rc4/net/bluetooth/sco.c:1300
>        __sock_release+0xe1/0x2d0 sect/v6.13-rc4/net/socket.c:640
>        sock_close+0x1c/0x30 sect/v6.13-rc4/net/socket.c:1408
>        __fput+0x2bd/0xa80 sect/v6.13-rc4/fs/file_table.c:450
>        __fput_sync+0x15e/0x1c0 sect/v6.13-rc4/fs/file_table.c:535
>        __do_sys_close sect/v6.13-rc4/fs/open.c:1554 [inline]
>        __se_sys_close sect/v6.13-rc4/fs/open.c:1539 [inline]
>        __x64_sys_close+0x93/0x120 sect/v6.13-rc4/fs/open.c:1539
>        do_syscall_x64 sect/v6.13-rc4/arch/x86/entry/common.c:52 [inline]
>        do_syscall_64+0xee/0x210 sect/v6.13-rc4/arch/x86/entry/common.c:83
>        entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn")
> Acked-by: Dave Tian <daveti@purdue.edu>
> Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
> ---
>  net/bluetooth/sco.c | 15 ++++++++++++++-
>  1 file changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index fcc597be5bbd..c05f79b7aa31 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -570,10 +570,23 @@ static void __sco_sock_close(struct sock *sk)
>  /* Must be called on unlocked socket. */
>  static void sco_sock_close(struct sock *sk)
>  {
> +       struct sco_conn *conn;
> +
> +       lock_sock(sk);
> +       conn = sco_pi(sk)->conn;
> +       if (conn)
> +               sco_conn_hold(conn);
> +       release_sock(sk);
> +
> +       if (conn)
> +               disable_delayed_work_sync(&conn->timeout_work);
> +
>         lock_sock(sk);
> -       sco_sock_clear_timer(sk);
>         __sco_sock_close(sk);
>         release_sock(sk);
> +
> +       if (conn)
> +               sco_conn_put(conn);
>  }
>
>  static void sco_sock_init(struct sock *sk, struct sock *parent)
> --
> 2.47.3
>

Although Sashiko [1] pointed out some interesting bugs in this patch,
they appear to be pre-existing.
I can send another patch for this.

[1] https://sashiko.dev/#/patchset/20260624213304.1460149-2-iam%40sung-woo.kim

^ permalink raw reply


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