diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index f06c873fce0e8..9056576bdf7bc 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -191,14 +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 +qcs6490-rb3gen2-industrial-mezzanine-bt-uart-dtbs := qcs6490-rb3gen2.dtb qcs6490-rb3gen2-industrial-mezzanine.dtbo qcs6490-rb3gen2-industrial-mezzanine-bt-uart.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-industrial-mezzanine-bt-uart.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-bt-uart.dtso b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-bt-uart.dtso new file mode 100644 index 0000000000000..591af9dfc64e1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-bt-uart.dtso @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + regulator-boot-on; + }; + + m2-e-connector { + compatible = "pcie-m2-e-connector"; + vpcie3v3-supply = <&vreg_wcn_3p3>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + m2_e_pcie_ep: endpoint@0 { + reg = <0>; + remote-endpoint = <&pcie0_m2_e_ep>; + }; + }; + + port@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + m2_e_uart_ep: endpoint@0 { + reg = <0>; + remote-endpoint = <&uart4_m2_e_ep>; + }; + }; + }; + }; +}; + +&qup_uart4_cts { + bias-bus-hold; +}; + +&qup_uart4_rts { + drive-strength = <2>; + bias-disable; +}; + +&qup_uart4_rx { + bias-pull-up; +}; + +&qup_uart4_tx { + drive-strength = <2>; + bias-disable; +}; + +&tlmm { + qup_uart4_sleep_cts: qup-uart4-sleep-cts-state { + pins = "gpio16"; + function = "gpio"; + bias-bus-hold; + }; + + qup_uart4_sleep_rts: qup-uart4-sleep-rts-state { + pins = "gpio17"; + function = "gpio"; + bias-pull-down; + }; + + qup_uart4_sleep_tx: qup-uart4-sleep-tx-state { + pins = "gpio18"; + function = "gpio"; + bias-pull-up; + }; + + qup_uart4_sleep_rx: qup-uart4-sleep-rx-state { + pins = "gpio19"; + function = "gpio"; + bias-pull-up; + }; +}; + +&pcie0_port { + pcie@0,0 { + pcie@2,0 { + port { + pcie0_m2_e_ep: endpoint { + remote-endpoint = <&m2_e_pcie_ep>; + }; + }; + }; + }; +}; + +&uart4 { + 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>; + + status = "okay"; + + port { + uart4_m2_e_ep: endpoint { + remote-endpoint = <&m2_e_uart_ep>; + }; + }; +}; + +&uart7 { + status = "disabled"; +}; + +&wcn6750_pmu { + status = "disabled"; +}; 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 deleted file mode 100644 index 905a40f4b938f..0000000000000 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-m2-cologne.dtso +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -/* - * Copyright (c) 2026, 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-bus-hold; -}; - -&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>; - - bluetooth: bluetooth { - compatible = "qcom,qcc2072-bt"; - max-speed = <3200000>; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts index ef4055f3b364a..6d591c6c7956e 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts @@ -349,7 +349,7 @@ }; }; - wcn6750-pmu { + wcn6750_pmu: wcn6750-pmu { compatible = "qcom,wcn6750-pmu"; pinctrl-0 = <&bt_en>; pinctrl-names = "default"; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index d713ace388067..b017b80218f2b 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -2333,6 +2333,7 @@ status = "disabled"; pcie0_port: pcie@0 { + compatible = "pciclass,0604"; device_type = "pci"; reg = <0x0 0x0 0x0 0x0 0x0>; bus-range = <0x01 0xff>; diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 95394586a84b6..1a9d030517d3f 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -567,6 +567,54 @@ static int qca_inject_cmd_complete_event(struct hci_dev *hdev) return hci_recv_frame(hdev, skb); } +static void qca_combine_nvm_calib(struct hci_dev *hdev, u8 **data, + int *size, char *calib_name, + size_t max_size) +{ + const struct firmware *calib_fw; + struct tlv_type_hdr *outer_hdr; + size_t inner_len, combined_size; + u8 *combined_data; + int err; + + err = request_firmware(&calib_fw, calib_name, &hdev->dev); + if (err) { + if (qca_get_alt_nvm_file(calib_name, max_size)) + err = request_firmware(&calib_fw, calib_name, &hdev->dev); + + if (err) { + bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", + calib_name, err); + return; + } + } + + bt_dev_info(hdev, "QCA Downloading %s", calib_name); + + inner_len = *size + calib_fw->size; + combined_size = sizeof(*outer_hdr) + inner_len; + combined_data = vmalloc(combined_size); + if (!combined_data) { + bt_dev_warn(hdev, + "QCA Failed to allocate memory for file: %s", + calib_name); + release_firmware(calib_fw); + return; + } + + outer_hdr = (struct tlv_type_hdr *)combined_data; + /* high 24 bits = payload length, low 8 bits = type */ + outer_hdr->type_len = cpu_to_le32((inner_len << 8) | 4); + memcpy(combined_data + sizeof(*outer_hdr), *data, *size); + memcpy(combined_data + sizeof(*outer_hdr) + *size, + calib_fw->data, calib_fw->size); + release_firmware(calib_fw); + + vfree(*data); + *data = combined_data; + *size = combined_size; +} + static int qca_download_firmware(struct hci_dev *hdev, struct qca_fw_config *config, enum qca_btsoc_type soc_type, @@ -612,52 +660,10 @@ static int qca_download_firmware(struct hci_dev *hdev, memcpy(data, fw->data, size); release_firmware(fw); - /* For QCC2072, combine the NVM (type 2) with the calibration file - * into a single TLV of outer type 4. - */ - if (soc_type == QCA_QCC2072 && config->type == TLV_TYPE_NVM) { - const struct firmware *calib_fw = NULL; - char calib_name[32]; - u8 *combined_data = NULL; - size_t inner_len, combined_size; - struct tlv_type_hdr *outer_hdr; - int err; - - snprintf(calib_name, sizeof(calib_name), - "qca/ornbcscal%02x.bin", rom_ver); - err = request_firmware(&calib_fw, calib_name, &hdev->dev); - if (err) { - bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", - calib_name, err); - goto skip_combination; - } - - bt_dev_info(hdev, "QCA Downloading %s", calib_name); - - inner_len = size + calib_fw->size; - combined_size = sizeof(*outer_hdr) + inner_len; - combined_data = vmalloc(combined_size); - if (!combined_data) { - bt_dev_warn(hdev, - "QCA Failed to allocate memory for file: %s", - calib_name); - release_firmware(calib_fw); - goto skip_combination; - } - - outer_hdr = (struct tlv_type_hdr *)combined_data; - /* high 24 bits = payload length, low 8 bits = type */ - outer_hdr->type_len = cpu_to_le32((inner_len << 8) | 4); - memcpy(combined_data + sizeof(*outer_hdr), data, size); - memcpy(combined_data + sizeof(*outer_hdr) + size, - calib_fw->data, calib_fw->size); - release_firmware(calib_fw); - vfree(data); - data = combined_data; - size = combined_size; -skip_combination: - ; - } + if (soc_type == QCA_QCC2072 && config->type == TLV_TYPE_NVM) + qca_combine_nvm_calib(hdev, &data, &size, + config->calib_name, + sizeof(config->calib_name)); ret = qca_tlv_check_data(hdev, config, data, size, soc_type); if (ret) @@ -927,7 +933,8 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, /* Give the controller some time to get ready to receive the NVM */ msleep(10); - if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850) + if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850 || + soc_type == QCA_QCC2072) qca_read_fw_board_id(hdev, &boardid); /* Download NVM configuration */ @@ -988,8 +995,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, "hmtnv", soc_type, ver, rom_ver, boardid); break; case QCA_QCC2072: - snprintf(config.fwname, sizeof(config.fwname), - "qca/ornnv%02x.bin", rom_ver); + qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), + "ornnv", soc_type, ver, + rom_ver, boardid); break; default: snprintf(config.fwname, sizeof(config.fwname), @@ -997,6 +1005,12 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, } } + if (soc_type == QCA_QCC2072) + qca_get_nvm_name_by_board(config.calib_name, + sizeof(config.calib_name), + "ornbcscal", soc_type, ver, + rom_ver, boardid); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); if (err < 0 && !firmware_name && soc_type == QCA_WCN6855) { qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index a175ac31e7b23..425133096eda5 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -94,6 +94,7 @@ enum qca_tlv_type { struct qca_fw_config { u8 type; char fwname[64]; + char calib_name[64]; uint8_t user_baud_rate; enum qca_tlv_dnld_mode dnld_mode; enum qca_tlv_dnld_mode dnld_type;