Linux bluetooth development
 help / color / mirror / Atom feed
From: Bjorn Andersson <andersson@kernel.org>
To: george.moussalem@outlook.com
Cc: Jens Axboe <axboe@kernel.dk>, Ulf Hansson <ulfh@kernel.org>,
	 Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	 Conor Dooley <conor+dt@kernel.org>,
	Johannes Berg <johannes@sipsolutions.net>,
	 Jeff Johnson <jjohnson@kernel.org>,
	Bartosz Golaszewski <brgl@kernel.org>,
	 Marcel Holtmann <marcel@holtmann.org>,
	Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
	 Balakrishna Godavarthi <quic_bgodavar@quicinc.com>,
	Rocky Liao <quic_rjliao@quicinc.com>,
	 Saravana Kannan <saravanak@kernel.org>,
	Andrew Lunn <andrew@lunn.ch>,
	 Heiner Kallweit <hkallweit1@gmail.com>,
	Russell King <linux@armlinux.org.uk>,
	 "David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	 Jakub Kicinski <kuba@kernel.org>,
	Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
	 Konrad Dybcio <konradybcio@kernel.org>,
	Mathieu Poirier <mathieu.poirier@linaro.org>,
	 Philipp Zabel <p.zabel@pengutronix.de>,
	linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
	 linux-mmc@vger.kernel.org, devicetree@vger.kernel.org,
	linux-wireless@vger.kernel.org,  ath10k@lists.infradead.org,
	linux-arm-msm@vger.kernel.org, linux-bluetooth@vger.kernel.org,
	 netdev@vger.kernel.org, linux-remoteproc@vger.kernel.org
Subject: Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
Date: Sat, 27 Jun 2026 22:07:05 -0500	[thread overview]
Message-ID: <akCOYIIhyl738PC6@baldur> (raw)
In-Reply-To: <20260625-ipq5018-bluetooth-v1-2-d999be0e04f7@outlook.com>

On Thu, Jun 25, 2026 at 06:10:06PM +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.
> 

The remoteproc framework deals with life cycle management of
coprocessors, but you don't want that - you want the BT driver to own
the life cycle.

Further, the fact that you split this in "BT" and "remoteproc", results
in you having two representations in DeviceTree and in the device model
for the same hardware.

I know we have examples of this in the kernel already, but they are all
racy...

Please see if you can embed the firmware loading, authentication and
PAS calls directly into the BT driver - to have a single entity managing
the life cycle of your M0 processor.

Regards,
Bjorn

> 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
> 
> 

  parent reply	other threads:[~2026-06-28  3:07 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
2026-06-25 14:10 ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL George Moussalem via B4 Relay
2026-06-25 15:47   ` Add support for IPQ5018 Bluetooth bluez.test.bot
2026-06-26 10:47   ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL Krzysztof Kozlowski
2026-06-26 10:51     ` George Moussalem
2026-06-26 11:16       ` Krzysztof Kozlowski
2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
2026-06-25 14:18   ` Philipp Zabel
2026-06-25 14:24     ` George Moussalem
2026-06-26 11:20   ` Konrad Dybcio
2026-06-26 11:32     ` George Moussalem
2026-06-28  3:08     ` Bjorn Andersson
2026-06-28  3:07   ` Bjorn Andersson [this message]
2026-06-25 14:10 ` [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
2026-06-25 14:10 ` [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
2026-06-26 10:53   ` Krzysztof Kozlowski
2026-06-26 11:20     ` George Moussalem
2026-06-26 11:30       ` Konrad Dybcio
2026-06-25 14:10 ` [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
2026-06-25 14:10 ` [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=akCOYIIhyl738PC6@baldur \
    --to=andersson@kernel.org \
    --cc=andrew@lunn.ch \
    --cc=ath10k@lists.infradead.org \
    --cc=axboe@kernel.dk \
    --cc=brgl@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=george.moussalem@outlook.com \
    --cc=hkallweit1@gmail.com \
    --cc=horms@kernel.org \
    --cc=jjohnson@kernel.org \
    --cc=johannes@sipsolutions.net \
    --cc=konradybcio@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=linux-remoteproc@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=luiz.dentz@gmail.com \
    --cc=marcel@holtmann.org \
    --cc=mathieu.poirier@linaro.org \
    --cc=netdev@vger.kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=pabeni@redhat.com \
    --cc=quic_bgodavar@quicinc.com \
    --cc=quic_rjliao@quicinc.com \
    --cc=robh@kernel.org \
    --cc=saravanak@kernel.org \
    --cc=ulfh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox