diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index 6353a336f382e..197248d6f7b8a 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -28,6 +28,7 @@ properties: - qcom,wcn6750-bt - qcom,wcn6855-bt - qcom,wcn7850-bt + - qcom,qcc2072-bt enable-gpios: maxItems: 1 diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 4c830c54f955e..3918b995f8d1a 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -191,12 +191,14 @@ dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-el2.dtb qcs6490-rb3gen2-vision-mezzanine-dtbs := qcs6490-rb3gen2.dtb qcs6490-rb3gen2-vision-mezzanine.dtbo qcs6490-rb3gen2-industrial-mezzanine-dtbs := qcs6490-rb3gen2.dtb qcs6490-rb3gen2-industrial-mezzanine.dtbo +qcs6490-rb3gen2-industrial-mezzanine-m2-cologne-dtbs := qcs6490-rb3gen2.dtb qcs6490-rb3gen2-industrial-mezzanine.dtbo qcs6490-rb3gen2-industrial-mezzanine-m2-cologne.dtbo dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-industrial-mezzanine.dtb qcs6490-rb3gen2-industrial-mezzanine-el2-dtbs := qcs6490-rb3gen2-industrial-mezzanine.dtb kodiak-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-industrial-mezzanine-el2.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-vision-mezzanine.dtb +dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-industrial-mezzanine-m2-cologne.dtb qcs6490-rb3gen2-vision-mezzanine-el2-dtbs := qcs6490-rb3gen2-vision-mezzanine.dtb kodiak-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-vision-mezzanine-el2.dtb diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-m2-cologne.dtso b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-m2-cologne.dtso new file mode 100644 index 0000000000000..7f9fe97f7b375 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-m2-cologne.dtso @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +*/ + +/dts-v1/; +/plugin/; +#include + +&tlmm { + qup_uart4_sleep_cts: qup-uart4-sleep-cts-state { + pins = "gpio16"; + function = "gpio"; + /* + * Configure a bias-bus-hold on CTS to lower power + * usage when Bluetooth is turned off. Bus hold will + * maintain a low power state regardless of whether + * the Bluetooth module drives the pin in either + * direction or leaves the pin fully unpowered. + */ + bias-bus-hold; + }; + + qup_uart4_sleep_rts: qup-uart4-sleep-rts-state { + pins = "gpio17"; + function = "gpio"; + /* + * Configure pull-down on RTS. As RTS is active low + * signal, pull it low to indicate the BT SoC that it + * can wakeup the system anytime from suspend state by + * pulling RX low (by sending wakeup bytes). + */ + bias-pull-down; + }; + + qup_uart4_sleep_rx: qup-uart4-sleep-rx-state { + pins = "gpio19"; + function = "gpio"; + /* + * Configure a pull-up on RX. This is needed to avoid + * garbage data when the TX pin of the Bluetooth module + * is floating which may cause spurious wakeups. + */ + bias-pull-up; + }; + + qup_uart4_sleep_tx: qup-uart4-sleep-tx-state { + pins = "gpio18"; + function = "gpio"; + /* + * Configure pull-up on TX when it isn't actively driven + * to prevent BT SoC from receiving garbage during sleep. + */ + bias-pull-up; + }; + + sw_ctrl: sw-ctrl-state { + pins = "gpio86"; + function = "gpio"; + bias-pull-down; + }; +}; +&qup_uart4_cts { + /* + * Configure a bias-bus-hold on CTS to lower power + * usage when Bluetooth is turned off. Bus hold will + * maintain a low power state regardless of whether + * the Bluetooth module drives the pin in either + * direction or leaves the pin fully unpowered. + */ + bias-pull-up; +}; + +&qup_uart4_rts { + /* We'll drive RTS, so no pull */ + drive-strength = <2>; + bias-disable; +}; + +&qup_uart4_rx { + /* + * Configure a pull-up on RX. This is needed to avoid + * garbage data when the TX pin of the Bluetooth module is + * in tri-state (module powered off or not driving the + * signal yet). + */ + bias-pull-up; +}; + +&qup_uart4_tx { + /* We'll drive TX, so no pull */ + drive-strength = <2>; + bias-disable; +}; + +&uart4 { + status = "okay"; + /delete-property/ interrupts; + interrupts-extended = <&intc GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>, + <&tlmm 19 IRQ_TYPE_EDGE_FALLING>; + pinctrl-names = "default", "sleep"; + pinctrl-1 = <&qup_uart4_sleep_cts>, <&qup_uart4_sleep_rts>, + <&qup_uart4_sleep_tx>, <&qup_uart4_sleep_rx>; + + bluetooth1: bluetooth1 { + compatible = "qcom,qcc2072-bt"; + pinctrl-names = "default"; + max-speed = <3200000>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso index c367ded70ec7c..cb653db7ba5cf 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso @@ -306,3 +306,7 @@ &wifi { status = "disabled"; }; + +&uart7 { +status = "disabled"; +}; diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 3b06269201934..956be6007cb51 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -609,9 +609,71 @@ static int qca_download_firmware(struct hci_dev *hdev, return -ENOMEM; } + /* Copy firmware data into buffer */ memcpy(data, fw->data, size); release_firmware(fw); + /* For QCA_QCC2072, combine the default NVM (type 2) with the + * calibration file clnbcscal10.bin into a single TLV of outer type 4. + */ + if (soc_type == QCA_QCC2072 && config->type == TLV_TYPE_NVM) { + const struct firmware *calib_fw = NULL; + int ret_calib; + u8 *combined_data = NULL; + size_t combined_size; + struct tlv_type_hdr *outer_hdr; + size_t inner_len; + + /* Load calibration firmware */ + ret_calib = request_firmware(&calib_fw, "qca/clnbcscal10.bin", + &hdev->dev); + if (ret_calib) { + bt_dev_err(hdev, + "Failed to load calibration file clnbcscal10.bin (%d)", + ret_calib); + /* Proceed with original NVM if calibration not found */ + goto skip_combination; + } + // TODO: remove this after testing JRT + else + { + bt_dev_info(hdev, "QCA Downloading clnbcscal10.bin success"); + } + + /* Total length of inner payload (original NVM + calibration) */ + inner_len = size + calib_fw->size; + + /* Allocate combined buffer with outer TLV header */ + combined_size = sizeof(*outer_hdr) + inner_len; + combined_data = vmalloc(combined_size); + if (!combined_data) { + bt_dev_err(hdev, + "Failed to allocate memory for combined NVM"); + release_firmware(calib_fw); + goto skip_combination; + } + + outer_hdr = (struct tlv_type_hdr *)combined_data; + /* Set type_len: high 24 bits = length, low 8 bits = type 4 */ + outer_hdr->type_len = cpu_to_le32((inner_len << 8) | 4); + + /* Copy original NVM (including its own TLV header) */ + memcpy(combined_data + sizeof(*outer_hdr), data, size); + /* Append calibration TLV (already a TLV) */ + memcpy(combined_data + sizeof(*outer_hdr) + size, + calib_fw->data, calib_fw->size); + + /* Replace original data with combined data */ + vfree(data); + data = combined_data; + size = combined_size; + + release_firmware(calib_fw); +skip_combination: + ; + } + + /* Continue with TLV parsing on (possibly) combined data */ ret = qca_tlv_check_data(hdev, config, data, size, soc_type); if (ret) goto out; @@ -843,6 +905,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); @@ -936,6 +1002,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); @@ -998,6 +1068,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 8f3c1b1c77b3d..a175ac31e7b23 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 a76c6e42005b3..25a72d704b29a 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1370,6 +1370,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: @@ -1457,6 +1458,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: @@ -1508,6 +1510,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; @@ -1543,6 +1546,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; @@ -1859,6 +1863,7 @@ static int qca_power_on(struct hci_dev *hdev) case QCA_WCN6855: case QCA_WCN7850: case QCA_QCA6390: + case QCA_QCC2072: ret = qca_regulator_init(hu); break; @@ -1955,6 +1960,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"; } @@ -2164,6 +2173,12 @@ static const struct qca_device_data qca_soc_data_qca6390 __maybe_unused = { .num_vregs = 0, }; +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 []) { @@ -2256,6 +2271,7 @@ static void qca_power_shutdown(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); @@ -2402,6 +2418,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) qcadev->btsoc_type = QCA_ROME; switch (qcadev->btsoc_type) { + case QCA_QCC2072: case QCA_WCN3950: case QCA_WCN3988: case QCA_WCN3990: @@ -2422,6 +2439,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) } switch (qcadev->btsoc_type) { + case QCA_QCC2072: case QCA_WCN6855: case QCA_WCN7850: case QCA_WCN6750: @@ -2470,7 +2488,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) "failed to acquire BT_EN gpio\n"); if (!qcadev->bt_en && - (data->soc_type == QCA_WCN6750 || + (data->soc_type == QCA_WCN6750 || data->soc_type == QCA_QCC2072 || data->soc_type == QCA_WCN6855 || data->soc_type == QCA_WCN7850)) power_ctrl_enabled = false; @@ -2480,6 +2498,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 || data->soc_type == QCA_WCN7850)) { dev_err(&serdev->dev, "failed to acquire SW_CTRL gpio\n"); return PTR_ERR(qcadev->sw_ctrl); @@ -2558,6 +2577,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: @@ -2768,6 +2788,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);