Re: [PATCH 2/4] Bluetooth: qca: add QCC2072 support

From: Yepuri Siddu

Date: Thu Jul 02 2026 - 06:33:09 EST




On 6/1/2026 1:57 PM, Bartosz Golaszewski wrote:
On Fri, 29 May 2026 19:58:22 +0200, Yepuri Siddu
<yepuri.siddu@xxxxxxxxxxxxxxxx> said:
QCC2072 is a BT/WiFi combo SoC that uses different firmware
filenames and requires no external voltage regulators, so add
it as a new SoC type.

The chip supports the wideband speech and valid LE states
capabilities. Its firmware is named using the "orn" prefix and
follows the standard rom-version-based scheme:
- qca/ornbtfw<ver>.tlv
- qca/ornnv<ver>.bin

These firmware files are already present in the linux-firmware
repository.

Signed-off-by: Yepuri Siddu <yepuri.siddu@xxxxxxxxxxxxxxxx>
---
drivers/bluetooth/btqca.c | 9 +++++++++
drivers/bluetooth/btqca.h | 1 +
drivers/bluetooth/hci_qca.c | 24 ++++++++++++++++++++++++
3 files changed, 34 insertions(+)

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index dda76365726f..0ef7546e7c7a 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -843,6 +843,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
snprintf(config.fwname, sizeof(config.fwname),
"qca/hmtbtfw%02x.tlv", rom_ver);
break;
+ case QCA_QCC2072:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/ornbtfw%02x.tlv", rom_ver);
+ break;
default:
snprintf(config.fwname, sizeof(config.fwname),
"qca/rampatch_%08x.bin", soc_ver);
@@ -937,6 +941,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_QCC2072:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/ornnv%02x.bin", rom_ver);
+ break;
default:
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
@@ -999,6 +1007,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCC2072:
/* 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..a175ac31e7b2 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -158,6 +158,7 @@ enum qca_btsoc_type {
QCA_WCN6750,
QCA_WCN6855,
QCA_WCN7850,
+ QCA_QCC2072,
};

#if IS_ENABLED(CONFIG_BT_QCA)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index ed280399bf47..fc67ba0e4984 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1372,6 +1372,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)

/* Give the controller time to process the request */
switch (qca_soc_type(hu)) {
+ case QCA_QCC2072:
case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
@@ -1459,6 +1460,7 @@ static unsigned int qca_get_speed(struct hci_uart *hu,
static int qca_check_speeds(struct hci_uart *hu)
{
switch (qca_soc_type(hu)) {
+ case QCA_QCC2072:
case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
@@ -1510,6 +1512,7 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCC2072:
hci_uart_set_flow_control(hu, true);
break;

@@ -1545,6 +1548,7 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCC2072:
hci_uart_set_flow_control(hu, false);
break;

@@ -1861,6 +1865,7 @@ static int qca_power_on(struct hci_dev *hdev)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCC2072:
ret = qca_regulator_init(hu);
break;

@@ -1957,6 +1962,10 @@ static int qca_setup(struct hci_uart *hu)
soc_name = "wcn7850";
break;

+ case QCA_QCC2072:
+ soc_name = "qcc2072";
+ break;
+
default:
soc_name = "ROME/QCA6390";
}
@@ -1980,6 +1989,7 @@ static int qca_setup(struct hci_uart *hu)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCC2072:
if (qcadev->bdaddr_property_broken)
hci_set_quirk(hdev, HCI_QUIRK_BDADDR_PROPERTY_BROKEN);

@@ -2013,6 +2023,7 @@ static int qca_setup(struct hci_uart *hu)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCC2072:
break;

default:
@@ -2166,6 +2177,12 @@ static const struct qca_device_data qca_soc_data_wcn3998 __maybe_unused = {
.num_vregs = 4,
};

+static const struct qca_device_data qca_soc_data_qcc2072 __maybe_unused = {
+ .soc_type = QCA_QCC2072,
+ .num_vregs = 0,
+ .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
+};
+
static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = {
.soc_type = QCA_WCN6750,
.vregs = (struct qca_vreg []) {
@@ -2268,6 +2285,7 @@ static void qca_power_off(struct hci_uart *hu)

case QCA_WCN6750:
case QCA_WCN6855:
+ case QCA_QCC2072:
gpiod_set_value_cansleep(qcadev->bt_en, 0);
msleep(100);
qca_regulator_disable(qcadev);
@@ -2414,6 +2432,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
qcadev->btsoc_type = QCA_ROME;

switch (qcadev->btsoc_type) {
+ case QCA_QCC2072:
case QCA_QCA6390:
case QCA_WCN3950:
case QCA_WCN3988:
@@ -2434,6 +2453,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
}

switch (qcadev->btsoc_type) {
+ case QCA_QCC2072:
case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
@@ -2484,6 +2504,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
if (!qcadev->bt_en &&
(data->soc_type == QCA_WCN6750 ||
data->soc_type == QCA_WCN6855 ||
+ data->soc_type == QCA_QCC2072 ||

Looking at the bindings: this chip cannot have an enable GPIO, so it probably
should have its own if branch that unconditionally sets power_ctrl_enabled to
false?

QCC2072 is an M.2 E-key chip. As M.2 Power Sequencing changes are being upstreamed, we will reupdate our existing patches to align with M.2 power sequencing approach.
This includes aligning the bindings, DT and also BT driver changes.

Thanks,
Siddu


data->soc_type == QCA_WCN7850))
power_ctrl_enabled = false;

@@ -2492,6 +2513,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
if (IS_ERR(qcadev->sw_ctrl) &&
(data->soc_type == QCA_WCN6750 ||
data->soc_type == QCA_WCN6855 ||
+ data->soc_type == QCA_QCC2072 ||

Same here.

Bart

data->soc_type == QCA_WCN7850)) {
dev_err(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
return PTR_ERR(qcadev->sw_ctrl);
@@ -2570,6 +2592,7 @@ static void qca_serdev_remove(struct serdev_device *serdev)
struct qca_power *power = qcadev->bt_power;

switch (qcadev->btsoc_type) {
+ case QCA_QCC2072:
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
@@ -2779,6 +2802,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = {
{ .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750},
{ .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855},
{ .compatible = "qcom,wcn7850-bt", .data = &qca_soc_data_wcn7850},
+ { .compatible = "qcom,qcc2072-bt", .data = &qca_soc_data_qcc2072},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
--
2.34.1