Index | Thread | Search

From:
Kevin Lo <kevlo@kevlo.org>
Subject:
qwx: add support for QCA2066
To:
tech@openbsd.org
Date:
Thu, 27 Mar 2025 16:06:50 +0800

Download raw body.

Thread
Hi,

The diff below adds support for QCA2066.  It is very similar to QCNFA765,
The most significant difference is that QCA2066 supports 3-antenna
configuration while QCNFA765 does not.  To differentiate them,
subversion numbers are used.

See Linux commit 5dc9d1a55e953d9059ecbdd8fe6ec81e9edd349e

Since qca2066 will encounter the same regdb firmware loading issue as qwz(4),
fix the regdb firmware loading, code taken from qwz.c r1.11 by patrick@.

Tested:
qwx0 at pci2 dev 0 function 0 "Qualcomm QCNFA765" rev 0x01: msi
qwx0: qca2066 hw2.1 fw 0x1101ffff address 54:f2:9f:27:f9:9c

qwx0 at pci2 dev 0 function 0 "Qualcomm QCNFA765" rev 0x01: msi
qwx0: wcn6855 hw2.1 fw 0x11088c35 address 38:d5:7a:27:ea:c3

ok?

Index: sys/dev/ic/qwx.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/qwx.c,v
diff -u -p -u -p -r1.68 qwx.c
--- sys/dev/ic/qwx.c	5 Feb 2025 09:28:01 -0000	1.68
+++ sys/dev/ic/qwx.c	27 Mar 2025 06:58:18 -0000
@@ -4002,6 +4002,97 @@ static const struct ath11k_hw_params ath
 		.smp2p_wow_exit = true,
 #endif
 	},
+	{
+		.name = "qca2066 hw2.1",
+		.hw_rev = ATH11K_HW_QCA2066_HW21,
+		.fw = {
+			.dir = "qca2066-hw2.1",
+			.board_size = 256 * 1024,
+			.cal_offset = 128 * 1024,
+		},
+		.max_radios = 3,
+		.bdf_addr = 0x4B0C0000,
+		.hw_ops = &wcn6855_ops,
+		.ring_mask = &ath11k_hw_ring_mask_qca6390,
+		.internal_sleep_clock = true,
+		.regs = &wcn6855_regs,
+		.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390,
+		.host_ce_config = qwx_host_ce_config_qca6390,
+		.ce_count = QWX_CE_COUNT_QCA6390,
+		.target_ce_config = ath11k_target_ce_config_wlan_qca6390,
+		.target_ce_count = 9,
+		.svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390,
+		.svc_to_ce_map_len = 14,
+		.single_pdev_only = true,
+		.rxdma1_enable = false,
+		.num_rxmda_per_pdev = 2,
+		.rx_mac_buf_ring = true,
+		.vdev_start_delay = true,
+		.htt_peer_map_v2 = false,
+#if notyet
+		.spectral = {
+			.fft_sz = 0,
+			.fft_pad_sz = 0,
+			.summary_pad_sz = 0,
+			.fft_hdr_len = 0,
+			.max_fft_bins = 0,
+			.fragment_160mhz = false,
+		},
+
+		.interface_modes = BIT(NL80211_IFTYPE_STATION) |
+					BIT(NL80211_IFTYPE_AP),
+		.supports_monitor = false,
+		.full_monitor_mode = false,
+#endif
+		.supports_shadow_regs = true,
+		.idle_ps = true,
+		.supports_sta_ps = true,
+		.cold_boot_calib = false,
+		.cbcal_restart_fw = false,
+		.fw_mem_mode = 0,
+		.num_vdevs = 16 + 1,
+		.num_peers = 512,
+		.supports_suspend = true,
+		.hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855),
+		.supports_regdb = true,
+		.fix_l1ss = false,
+		.credit_flow = true,
+		.max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390,
+		.hal_params = &ath11k_hw_hal_params_qca6390,
+#if notyet
+		.supports_dynamic_smps_6ghz = false,
+		.alloc_cacheable_memory = false,
+		.supports_rssi_stats = true,
+#endif
+		.fw_wmi_diag_event = true,
+		.current_cc_support = true,
+		.dbr_debug_support = false,
+		.global_reset = true,
+#ifdef notyet
+		.bios_sar_capa = &ath11k_hw_sar_capa_wcn6855,
+#endif
+		.m3_fw_support = true,
+		.fixed_bdf_addr = false,
+		.fixed_mem_region = false,
+		.static_window_map = false,
+		.hybrid_bus_type = false,
+		.fixed_fw_mem = false,
+#if notyet
+		.support_off_channel_tx = true,
+		.supports_multi_bssid = true,
+
+		.sram_dump = {
+			.start = 0x01400000,
+			.end = 0x0177ffff,
+		},
+
+		.tcl_ring_retry = true,
+#endif
+		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
+#ifdef notyet
+		.smp2p_wow_exit = false,
+#endif
+	},
 };

 const struct ath11k_hw_regs ipq8074_regs = {
@@ -8239,6 +8330,33 @@ qwx_qmi_mem_seg_send(struct qwx_softc *s
 }

 int
+qwx_loadfirmware(struct qwx_softc *sc, int type, const char *filename,
+    u_char **data, size_t *len)
+{
+	char path[PATH_MAX];
+	int ret;
+
+	if (!sc->fw_img[type].data) {
+		ret = snprintf(path, sizeof(path), "%s-%s-%s",
+		    ATH11K_FW_DIR, sc->hw_params.fw.dir, filename);
+		if (ret < 0 || ret >= sizeof(path))
+			return ENOSPC;
+
+		ret = loadfirmware(path, &sc->fw_img[type].data,
+		    &sc->fw_img[type].size);
+		if (ret) {
+			printf("%s: could not read %s (error %d)\n",
+			    sc->sc_dev.dv_xname, path, ret);
+			return ret;
+		}
+	}
+
+	*data = sc->fw_img[type].data;
+	*len = sc->fw_img[type].size;
+	return 0;
+}
+
+int
 qwx_core_check_smbios(struct qwx_softc *sc)
 {
 	return 0; /* TODO */
@@ -8326,9 +8444,15 @@ qwx_qmi_request_device_info(struct qwx_s
 	return -1;
 }

+enum ath11k_bdf_name_type {
+	ATH11K_BDF_NAME_FULL,
+	ATH11K_BDF_NAME_BUS_NAME,
+	ATH11K_BDF_NAME_CHIP_ID
+};
+
 int
 _qwx_core_create_board_name(struct qwx_softc *sc, char *name,
-    size_t name_len, int with_variant, int bus_type_mode)
+    size_t name_len, int with_variant, enum ath11k_bdf_name_type name_type)
 {
 	/* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */
 	char variant[9 + ATH11K_QMI_BDF_EXT_STR_LENGTH] = { 0 };
@@ -8339,9 +8463,8 @@ _qwx_core_create_board_name(struct qwx_s

 	switch (sc->id.bdf_search) {
 	case ATH11K_BDF_SEARCH_BUS_AND_BOARD:
-		if (bus_type_mode)
-			snprintf(name, name_len, "bus=%s", sc->sc_bus_str);
-		else
+		switch (name_type) {
+		case ATH11K_BDF_NAME_FULL:
 			snprintf(name, name_len,
 			    "bus=%s,vendor=%04x,device=%04x,"
 			    "subsystem-vendor=%04x,subsystem-device=%04x,"
@@ -8350,6 +8473,15 @@ _qwx_core_create_board_name(struct qwx_s
 			    sc->id.subsystem_vendor, sc->id.subsystem_device,
 			    sc->qmi_target.chip_id, sc->qmi_target.board_id,
 			    variant);
+			break;
+		case ATH11K_BDF_NAME_BUS_NAME:
+			snprintf(name, name_len, "bus=%s", sc->sc_bus_str);
+			break;
+		case ATH11K_BDF_NAME_CHIP_ID:
+			snprintf(name, name_len, "bus=%s,qmi-chip-id=%d",
+			    sc->sc_bus_str, sc->qmi_target.chip_id);
+			break;
+		}
 		break;
 	default:
 		snprintf(name, name_len,
@@ -8367,21 +8499,32 @@ _qwx_core_create_board_name(struct qwx_s
 int
 qwx_core_create_board_name(struct qwx_softc *sc, char *name, size_t name_len)
 {
-	return _qwx_core_create_board_name(sc, name, name_len, 1, 0);
+	return _qwx_core_create_board_name(sc, name, name_len, 1,
+	    ATH11K_BDF_NAME_FULL);
 }

 int
 qwx_core_create_fallback_board_name(struct qwx_softc *sc, char *name,
     size_t name_len)
 {
-	return _qwx_core_create_board_name(sc, name, name_len, 0, 0);
+	return _qwx_core_create_board_name(sc, name, name_len, 0,
+	    ATH11K_BDF_NAME_FULL);
 }

 int
 qwx_core_create_bus_type_board_name(struct qwx_softc *sc, char *name,
     size_t name_len)
 {
-	return _qwx_core_create_board_name(sc, name, name_len, 0, 1);
+	return _qwx_core_create_board_name(sc, name, name_len, 0,
+	    ATH11K_BDF_NAME_BUS_NAME);
+}
+
+int
+qwx_core_create_chip_id_board_name(struct qwx_softc *sc, char *name,
+    size_t name_len)
+{
+	return _qwx_core_create_board_name(sc, name, name_len, 0,
+	    ATH11K_BDF_NAME_CHIP_ID);
 }

 struct ath11k_fw_ie {
@@ -8574,7 +8717,7 @@ next:

 out:
 	if (!*boardfw || !*boardfw_len) {
-		printf("%s: failed to fetch %s for %s from %s\n",
+		DPRINTF("%s: failed to fetch %s for %s from %s\n",
 		    __func__, qwx_bd_ie_type_str(ie_id_match),
 		    boardname, filename);
 		return ENOENT;
@@ -8584,42 +8727,112 @@ out:
 }

 int
-qwx_core_fetch_bdf(struct qwx_softc *sc, u_char **data, size_t *len,
-    const u_char **boardfw, size_t *boardfw_len, const char *filename)
+qwx_core_fetch_bdf(struct qwx_softc *sc, const u_char **boardfw,
+    size_t *boardfw_len)
 {
-	char path[PATH_MAX];
-	char boardname[200];
+	char boardname[200], fallback_boardname[200], chip_id_boardname[200];
+	u_char *data;
+	size_t len;
 	int ret;

-	ret = snprintf(path, sizeof(path), "%s-%s-%s",
-	    ATH11K_FW_DIR, sc->hw_params.fw.dir, filename);
-	if (ret < 0 || ret >= sizeof(path))
-		return ENOSPC;
+	ret = qwx_loadfirmware(sc, QWX_FW_BOARD, ATH11K_BOARD_API2_FILE,
+	    &data, &len);
+	if (ret) {
+		printf("%s: could not read %s (error %d)\n",
+		    sc->sc_dev.dv_xname, ATH11K_BOARD_API2_FILE, ret);
+		return ret;
+	}

 	ret = qwx_core_create_board_name(sc, boardname, sizeof(boardname));
 	if (ret) {
-		DPRINTF("%s: failed to create board name: %d",
+		printf("%s: ailed to create board name: %d\n",
 		    sc->sc_dev.dv_xname, ret);
 		return ret;
 	}

-	ret = loadfirmware(path, data, len);
+	ret = qwx_core_fetch_board_data_api_n(sc, boardfw, boardfw_len, data,
+	    len, boardname, ATH11K_BD_IE_BOARD, ATH11K_BD_IE_BOARD_NAME,
+	    ATH11K_BD_IE_BOARD_DATA);
+	if (!ret)
+		return 0;
+
+	ret = qwx_core_create_fallback_board_name(sc, fallback_boardname,
+	    sizeof(fallback_boardname));
 	if (ret) {
-		printf("%s: could not read %s (error %d)\n",
-		    sc->sc_dev.dv_xname, path, ret);
+		printf("%s: failed to create fallback board name: %d\n",
+		    sc->sc_dev.dv_xname, ret);
 		return ret;
 	}

-	ret = qwx_core_fetch_board_data_api_n(sc, boardfw, boardfw_len,
-	    *data, *len, boardname, ATH11K_BD_IE_BOARD,
+	ret = qwx_core_fetch_board_data_api_n(sc, boardfw, boardfw_len, data,
+	    len, fallback_boardname, ATH11K_BD_IE_BOARD,
+	    ATH11K_BD_IE_BOARD_NAME, ATH11K_BD_IE_BOARD_DATA);
+	if (!ret)
+		return 0;
+
+	ret = qwx_core_create_chip_id_board_name(sc, chip_id_boardname,
+	    sizeof(chip_id_boardname));
+	if (ret) {
+		printf("%s: failed to create chip id board name: %d\n",
+		    sc->sc_dev.dv_xname, ret);
+		return ret;
+	}
+
+	ret = qwx_core_fetch_board_data_api_n(sc, boardfw, boardfw_len, data,
+	    len, chip_id_boardname, ATH11K_BD_IE_BOARD,
 	    ATH11K_BD_IE_BOARD_NAME, ATH11K_BD_IE_BOARD_DATA);
+	if (!ret)
+		return 0;
+
+	DPRINTF("%s: failed to fetch board data for %s from %s\n",
+	    sc->sc_dev.dv_xname, boardname, path);
+	return ret;
+}
+
+int
+qwx_core_fetch_regdb(struct qwx_softc *sc, const u_char **boardfw,
+    size_t *boardfw_len)
+{
+	char boardname[200], default_boardname[200];
+	u_char *data;
+	size_t len;
+	int ret;
+
+	ret = qwx_loadfirmware(sc, QWX_FW_BOARD, ATH11K_BOARD_API2_FILE,
+	    &data, &len);
+	if (ret)
+		return ret;
+
+	ret = qwx_core_create_board_name(sc, boardname, sizeof(boardname));
 	if (ret) {
-		DPRINTF("%s: failed to fetch board data for %s from %s\n",
-		    sc->sc_dev.dv_xname, boardname, path);
+		DPRINTF("%s: failed to create board name: %d",
+		    sc->sc_dev.dv_xname, ret);
 		return ret;
 	}

-	return 0;
+	ret = qwx_core_fetch_board_data_api_n(sc, boardfw, boardfw_len, data,
+	    len, boardname, ATH11K_BD_IE_REGDB, ATH11K_BD_IE_REGDB_NAME,
+	    ATH11K_BD_IE_REGDB_DATA);
+	if (!ret)
+		return 0;
+
+	ret = qwx_core_create_bus_type_board_name(sc, default_boardname,
+	    sizeof(default_boardname));
+	if (ret) {
+		DPRINTF("%s: failed to create board name: %d",
+		    sc->sc_dev.dv_xname, ret);
+		return ret;
+	}
+
+	ret = qwx_core_fetch_board_data_api_n(sc, boardfw, boardfw_len,
+	    data, len, default_boardname, ATH11K_BD_IE_REGDB,
+	    ATH11K_BD_IE_REGDB_NAME, ATH11K_BD_IE_REGDB_DATA);
+	if (!ret)
+		return 0;
+
+	DPRINTF("%s: failed to fetch regdb data for %s from %s\n",
+	    sc->sc_dev.dv_xname, boardname, path);
+	return ret;
 }

 int
@@ -8747,35 +8960,23 @@ err_free_req:
 int
 qwx_qmi_load_bdf_qmi(struct qwx_softc *sc, int regdb)
 {
-	u_char *data = NULL;
 	const u_char *boardfw;
-	size_t len = 0, boardfw_len;
+	size_t boardfw_len;
 	uint32_t fw_size;
 	int ret = 0, bdf_type;
-#ifdef notyet
-	const uint8_t *tmp;
-	uint32_t file_type;
-#endif
-	int fw_idx = regdb ? QWX_FW_REGDB : QWX_FW_BOARD;

-	if (sc->fw_img[fw_idx].data) {
-		boardfw = sc->fw_img[fw_idx].data;
-		boardfw_len = sc->fw_img[fw_idx].size;
+	if (regdb) {
+		ret = qwx_core_fetch_regdb(sc, &boardfw, &boardfw_len);
 	} else {
-		ret = qwx_core_fetch_bdf(sc, &data, &len,
-		    &boardfw, &boardfw_len,
-		    regdb ? ATH11K_REGDB_FILE : ATH11K_BOARD_API2_FILE);
+		ret = qwx_core_fetch_bdf(sc, &boardfw, &boardfw_len);
 		if (ret)
-			return ret;
-
-		sc->fw_img[fw_idx].data = malloc(boardfw_len, M_DEVBUF,
-		    M_NOWAIT);
-		if (sc->fw_img[fw_idx].data) {
-			memcpy(sc->fw_img[fw_idx].data, boardfw, boardfw_len);
-			sc->fw_img[fw_idx].size = boardfw_len;
-		}
+			printf("%s: qmi failed to fetch board file: %d\n",
+			    sc->sc_dev.dv_xname, ret);
 	}

+	if (ret)
+		goto out;
+
 	if (regdb)
 		bdf_type = ATH11K_QMI_BDF_TYPE_REGDB;
 	else if (boardfw_len >= QWX_SELFMAG &&
@@ -8846,7 +9047,6 @@ out_qmi_cal:
 		release_firmware(fw_entry);
 #endif
 out:
-	free(data, M_DEVBUF, len);
 	if (ret == 0)
 		DPRINTF("%s: BDF download sequence completed\n", __func__);

@@ -8890,28 +9090,11 @@ qwx_qmi_m3_load(struct qwx_softc *sc)
 {
 	u_char *data;
 	size_t len;
-	char path[PATH_MAX];
 	int ret;

-	if (sc->fw_img[QWX_FW_M3].data) {
-		data = sc->fw_img[QWX_FW_M3].data;
-		len = sc->fw_img[QWX_FW_M3].size;
-	} else {
-		ret = snprintf(path, sizeof(path), "%s-%s-%s",
-		    ATH11K_FW_DIR, sc->hw_params.fw.dir, ATH11K_M3_FILE);
-		if (ret < 0 || ret >= sizeof(path))
-			return ENOSPC;
-
-		ret = loadfirmware(path, &data, &len);
-		if (ret) {
-			printf("%s: could not read %s (error %d)\n",
-			    sc->sc_dev.dv_xname, path, ret);
-			return ret;
-		}
-
-		sc->fw_img[QWX_FW_M3].data = data;
-		sc->fw_img[QWX_FW_M3].size = len;
-	}
+	ret = qwx_loadfirmware(sc, QWX_FW_M3, ATH11K_M3_FILE, &data, &len);
+	if (ret)
+		return ret;

 	if (sc->m3_mem == NULL || QWX_DMA_LEN(sc->m3_mem) < len) {
 		if (sc->m3_mem)
Index: sys/dev/ic/qwxreg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/qwxreg.h,v
diff -u -p -u -p -r1.8 qwxreg.h
--- sys/dev/ic/qwxreg.h	1 Sep 2024 03:08:56 -0000	1.8
+++ sys/dev/ic/qwxreg.h	27 Mar 2025 06:58:19 -0000
@@ -56,6 +56,8 @@ enum ath11k_hw_rev {
 	ATH11K_HW_WCN6855_HW20,
 	ATH11K_HW_WCN6855_HW21,
 	ATH11K_HW_WCN6750_HW10,
+	ATH11K_HW_IPQ5018_HW10,
+	ATH11K_HW_QCA2066_HW21,
 };

 enum ath11k_firmware_mode {
Index: sys/dev/pci/if_qwx_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_qwx_pci.c,v
diff -u -p -u -p -r1.23 if_qwx_pci.c
--- sys/dev/pci/if_qwx_pci.c	4 Oct 2024 07:46:33 -0000	1.23
+++ sys/dev/pci/if_qwx_pci.c	27 Mar 2025 06:58:20 -0000
@@ -124,6 +124,8 @@
 #define TCSR_SOC_HW_VERSION_MAJOR_MASK	GENMASK(11, 8)
 #define TCSR_SOC_HW_VERSION_MINOR_MASK	GENMASK(7, 0)

+#define TCSR_SOC_HW_SUB_VER		0x1910010
+
 /*
  * pci.h
  */
@@ -601,6 +603,17 @@ const struct qwx_msi_config qwx_msi_conf
 		},
 		.hw_rev = ATH11K_HW_WCN6750_HW10,
 	},
+	{
+		.total_vectors = 32,
+		.total_users = 4,
+		.users = (struct qwx_msi_user[]) {
+			{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
+			{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
+			{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
+			{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
+		},
+		.hw_rev = ATH11K_HW_QCA2066_HW21,
+	},
 };

 int
@@ -742,7 +755,7 @@ qwx_pci_attach(struct device *parent, st
 	struct qwx_softc *sc = &psc->sc_sc;
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ifnet *ifp = &ic->ic_if;
-	uint32_t soc_hw_version_major, soc_hw_version_minor;
+	uint32_t soc_hw_version_major, soc_hw_version_minor, sub_version;
 	struct pci_attach_args *pa = aux;
 	pci_intr_handle_t ih;
 	pcireg_t memtype, reg;
@@ -918,7 +931,18 @@ qwx_pci_attach(struct device *parent, st
 				break;
 			case 0x10:
 			case 0x11:
-				sc->sc_hw_rev = ATH11K_HW_WCN6855_HW21;
+				sub_version =
+				    qwx_pcic_read32(sc, TCSR_SOC_HW_SUB_VER);
+				switch (sub_version) {
+				case 0x1019a0e1:
+				case 0x1019b0e1:
+				case 0x1019c0e1:
+				case 0x1019d0e1:
+					sc->sc_hw_rev = ATH11K_HW_QCA2066_HW21;
+					break;
+				default:
+					sc->sc_hw_rev = ATH11K_HW_WCN6855_HW21;
+				}
 				break;
 			default:
 				goto unsupported_wcn6855_soc;