Re: [PATCH RFC 3/5] Bluetooth: hci_qca: Support BD address from an nvmem cell

From: Loic Poulain

Date: Mon Oct 26 2026 - 07:40:09 EST


Hi Abel,

On Wed, Oct 14, 2026 at 3:05 PM Abel Vesa <abel.vesa@xxxxxxxxxxxxxxxx> wrote:
>
> Read an optional Bluetooth device address from an nvmem cell named
> "bd-address" (as already supported by hci_ll), and program it into the
> controller during setup. This lets QCA controllers source their BD_ADDR
> from firmware-provisioned storage such as a UEFI variable exposed via
> nvmem. An all-zero value is ignored, so unprovisioned storage leaves
> the controller default address untouched.

I proposed a similar solution here, at bluetooth core level:
https://lore.kernel.org/all/20261015-block-as-nvmem-v11-5-f890ac74f87d@xxxxxxxxxxxxxxxx/

Regards,
Loic


>
> Assisted-by: LLM
> Signed-off-by: Abel Vesa <abel.vesa@xxxxxxxxxxxxxxxx>
> ---
> drivers/bluetooth/hci_qca.c | 48 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index 7089e9b639b2..b704f4c3e1a4 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> @@ -25,6 +25,7 @@
> #include <linux/gpio/consumer.h>
> #include <linux/module.h>
> #include <linux/of.h>
> +#include <linux/nvmem-consumer.h>
> #include <linux/acpi.h>
> #include <linux/platform_device.h>
> #include <linux/pwrseq/consumer.h>
> @@ -230,6 +231,8 @@ struct qca_serdev {
> bool bdaddr_property_broken;
> bool support_hfp_hw_offload;
> const char *firmware_name[2];
> + bdaddr_t bdaddr;
> + bool bdaddr_valid;
> };
>
> static int qca_regulator_enable(struct qca_serdev *qcadev);
> @@ -2079,6 +2082,16 @@ static int qca_setup(struct hci_uart *hu)
> else
> hu->hdev->set_bdaddr = qca_set_bdaddr;
>
> + /* Program a BD address sourced from an nvmem cell, if provided. */
> + if (qcadev && qcadev->bdaddr_valid) {
> + int berr = hu->hdev->set_bdaddr(hdev, &qcadev->bdaddr);
> +
> + if (berr)
> + bt_dev_warn(hdev, "Failed to set nvmem bd-address: %d", berr);
> + else
> + bt_dev_info(hdev, "Set bd-address %pMR from nvmem", &qcadev->bdaddr);
> + }
> +
> if (qcadev && qcadev->support_hfp_hw_offload)
> qca_configure_hfp_offload(hdev);
>
> @@ -2420,6 +2433,41 @@ static int qca_serdev_probe(struct serdev_device *serdev)
> qcadev->bdaddr_property_broken = device_property_read_bool(&serdev->dev,
> "qcom,local-bd-address-broken");
>
> + /* Optional BD address from an nvmem cell (e.g. a UEFI variable). */
> + {
> + struct nvmem_cell *cell;
> +
> + cell = nvmem_cell_get(&serdev->dev, "bd-address");
> + if (IS_ERR(cell)) {
> + err = PTR_ERR(cell);
> + if (err == -EPROBE_DEFER)
> + return err;
> + /* -ENOENT/-ENOSYS: no cell configured, ignore. */
> + } else {
> + size_t clen;
> + void *cdata = nvmem_cell_read(cell, &clen);
> +
> + nvmem_cell_put(cell);
> + if (!IS_ERR(cdata)) {
> + /*
> + * Ignore an all-zero address: the backing
> + * storage has not been provisioned.
> + */
> + if (clen == sizeof(bdaddr_t) &&
> + bacmp(cdata, BDADDR_ANY)) {
> + /*
> + * Per the device tree bindings the nvmem
> + * value is MSB first, while bdaddr_t is
> + * LSB first.
> + */
> + baswap(&qcadev->bdaddr, cdata);
> + qcadev->bdaddr_valid = true;
> + }
> + kfree(cdata);
> + }
> + }
> + }
> +
> if (data)
> qcadev->btsoc_type = data->soc_type;
> else
>
> --
> 2.54.0
>
>