summary refs log tree commit diff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bluetooth/btqca.c77
-rw-r--r--drivers/bluetooth/btqca.h3
-rw-r--r--drivers/bluetooth/hci_qca.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c32
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c4
-rw-r--r--drivers/gpu/drm/amd/include/kgd_pp_interface.h4
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c18
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h2
-rw-r--r--drivers/gpu/drm/bridge/analogix/Kconfig6
-rw-r--r--drivers/gpu/drm/bridge/analogix/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7580.c623
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7580_regs.h1033
-rw-r--r--drivers/gpu/drm/drm_panel_orientation_quirks.c7
-rw-r--r--drivers/net/wireless/ath/ath11k/Makefile6
-rw-r--r--drivers/net/wireless/ath/ath11k/ahb.c3
-rw-r--r--drivers/net/wireless/ath/ath11k/ce.c6
-rw-r--r--drivers/net/wireless/ath/ath11k/core.c187
-rw-r--r--drivers/net/wireless/ath/ath11k/core.h75
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump.c159
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump.h48
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump_fw.c105
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump_fw.h30
-rw-r--r--drivers/net/wireless/ath/ath11k/debug.h2
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs.c79
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.c12
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.c45
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.c9
-rw-r--r--drivers/net/wireless/ath/ath11k/hif.h8
-rw-r--r--drivers/net/wireless/ath/ath11k/hw.c13
-rw-r--r--drivers/net/wireless/ath/ath11k/hw.h9
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c1035
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.h7
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.c21
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.h1
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c216
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.h66
-rw-r--r--drivers/net/wireless/ath/ath11k/pcic.c36
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.c68
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.h1
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.c224
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.h7
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode.c323
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode.h20
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode_i.h19
-rw-r--r--drivers/net/wireless/ath/ath11k/unitest.c96
-rw-r--r--drivers/net/wireless/ath/ath11k/unitest.h44
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c896
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.h562
-rw-r--r--drivers/net/wireless/ath/ath11k/wow.c3
-rw-r--r--drivers/usb/core/hcd-pci.c24
-rw-r--r--drivers/usb/host/ehci-pci.c3
-rw-r--r--drivers/usb/host/ohci-pci.c8
-rw-r--r--drivers/usb/host/uhci-pci.c7
-rw-r--r--drivers/usb/host/xhci-histb.c2
-rw-r--r--drivers/usb/host/xhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-plat.c4
-rw-r--r--drivers/usb/host/xhci-tegra.c2
-rw-r--r--drivers/usb/host/xhci.c5
-rw-r--r--drivers/usb/host/xhci.h2
65 files changed, 5829 insertions, 513 deletions
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index c9064d34d830..9dcad5af2491 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -205,6 +205,48 @@ static int qca_send_reset(struct hci_dev *hdev)
 	return 0;
 }
 
+static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
+{
+	u8 cmd;
+	struct sk_buff *skb;
+	struct edl_event_hdr *edl;
+	int err = 0;
+	int bid_len;
+
+	bt_dev_dbg(hdev, "QCA read board ID");
+
+	cmd = EDL_GET_BID_REQ_CMD;
+	skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
+				&cmd, 0, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(hdev, "Reading QCA board ID failed (%d)", err);
+		return err;
+	}
+
+	edl = skb_pull_data(skb, sizeof(*edl));
+	if (!edl) {
+		bt_dev_err(hdev, "QCA read board ID with no header");
+		err = -EILSEQ;
+		goto out;
+	}
+
+	if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
+	    edl->rtype != EDL_GET_BID_REQ_CMD) {
+		bt_dev_err(hdev, "QCA Wrong packet: %d %d", edl->cresp, edl->rtype);
+		err = -EIO;
+		goto out;
+	}
+
+	bid_len = edl->data[0];
+	*bid = (edl->data[1] << 8) + edl->data[2];
+	bt_dev_info(hdev, "%s: bid len = %x, bid = %x", __func__, bid_len, *bid);
+
+out:
+	kfree_skb(skb);
+	return err;
+}
+
 int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -574,6 +616,30 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
 }
 EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
 
+static void qca_generate_nvm_name(struct hci_dev *hdev, char *fwname,
+		   size_t max_size, struct qca_btsoc_version ver, u16 bid)
+{
+	u8 rom_ver = 0;
+	u32 soc_ver;
+	const char *variant;
+
+	soc_ver = get_soc_ver(ver.soc_id, ver.rom_ver);
+	rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
+
+	if ((ver.soc_id & 0x0000ff00) == QCA_HSP_GF_SOC_ID)  /* hsp gf chip */
+		variant = "g";
+	else
+		variant = "";
+
+	if (bid == 0x0)
+		snprintf(fwname, max_size, "qca/hpnv%02x%s.bin", rom_ver, variant);
+	else
+		snprintf(fwname, max_size, "qca/hpnv%02x%s.%x",
+				rom_ver, variant, bid);
+
+	bt_dev_info(hdev, "%s: nvm name is %s", __func__, fwname);
+}
+
 int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 		   enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
 		   const char *firmware_name)
@@ -582,6 +648,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	int err;
 	u8 rom_ver = 0;
 	u32 soc_ver;
+	u16 boardid = 0;
 
 	bt_dev_dbg(hdev, "QCA setup on UART");
 
@@ -604,6 +671,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	if (qca_is_wcn399x(soc_type)) {
 		snprintf(config.fwname, sizeof(config.fwname),
 			 "qca/crbtfw%02x.tlv", rom_ver);
+	} else if (soc_type == QCA_QCA2066) {
+		snprintf(config.fwname, sizeof(config.fwname),
+			 "qca/hpbtfw%02x.tlv", rom_ver);
 	} else if (soc_type == QCA_QCA6390) {
 		snprintf(config.fwname, sizeof(config.fwname),
 			 "qca/htbtfw%02x.tlv", rom_ver);
@@ -628,6 +698,9 @@ 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)
+		qca_read_fw_board_id(hdev, &boardid);
+
 	/* Download NVM configuration */
 	config.type = TLV_TYPE_NVM;
 	if (firmware_name)
@@ -641,7 +714,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 			snprintf(config.fwname, sizeof(config.fwname),
 				 "qca/crnv%02x.bin", rom_ver);
 		}
-	}
+	} else if (soc_type == QCA_QCA2066)
+		qca_generate_nvm_name(hdev, config.fwname, sizeof(config.fwname),
+				ver, boardid);
 	else if (soc_type == QCA_QCA6390)
 		snprintf(config.fwname, sizeof(config.fwname),
 			 "qca/htnv%02x.bin", rom_ver);
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 61e9a50e66ae..cb9a58b30b5e 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -13,6 +13,7 @@
 #define EDL_PATCH_TLV_REQ_CMD		(0x1E)
 #define EDL_GET_BUILD_INFO_CMD		(0x20)
 #define EDL_NVM_ACCESS_SET_REQ_CMD	(0x01)
+#define EDL_GET_BID_REQ_CMD		(0x23)
 #define EDL_PATCH_CONFIG_CMD		(0x28)
 #define MAX_SIZE_PER_TLV_SEGMENT	(243)
 #define QCA_PRE_SHUTDOWN_CMD		(0xFC08)
@@ -48,6 +49,7 @@
 
 #define QCA_FW_BUILD_VER_LEN		255
 
+#define QCA_HSP_GF_SOC_ID		0x1200
 
 enum qca_baudrate {
 	QCA_BAUDRATE_115200 	= 0,
@@ -145,6 +147,7 @@ enum qca_btsoc_type {
 	QCA_WCN3990,
 	QCA_WCN3998,
 	QCA_WCN3991,
+	QCA_QCA2066,
 	QCA_QCA6390,
 	QCA_WCN6750,
 };
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 45dffd2cbc71..e7361dfe858a 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1721,7 +1721,7 @@ static int qca_setup(struct hci_uart *hu)
 
 	bt_dev_info(hdev, "setting up %s",
 		qca_is_wcn399x(soc_type) ? "wcn399x" :
-		(soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390");
+		(soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390/QCA2066");
 
 	qca->memdump_state = QCA_MEMDUMP_IDLE;
 
@@ -1770,7 +1770,8 @@ retry:
 		qca_debugfs_init(hdev);
 		hu->hdev->hw_error = qca_hw_error;
 		hu->hdev->cmd_timeout = qca_cmd_timeout;
-		hu->hdev->wakeup = qca_wakeup;
+		if (device_can_wakeup(hu->serdev->ctrl->dev.parent))
+			hu->hdev->wakeup = qca_wakeup;
 	} else if (ret == -ENOENT) {
 		/* No patch/nvm-config found, run with original fw/config */
 		set_bit(QCA_ROM_FW, &qca->flags);
@@ -1863,6 +1864,11 @@ static const struct qca_device_data qca_soc_data_qca6390 = {
 	.num_vregs = 0,
 };
 
+static const struct qca_device_data qca_soc_data_qca2066 = {
+	.soc_type = QCA_QCA2066,
+	.num_vregs = 0,
+};
+
 static const struct qca_device_data qca_soc_data_wcn6750 = {
 	.soc_type = QCA_WCN6750,
 	.vregs = (struct qca_vreg []) {
@@ -2332,6 +2338,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = {
 	{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
 	{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
 	{ .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750},
+	{ .compatible = "qcom,qca2066-bt", .data = &qca_soc_data_qca2066},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
@@ -2343,6 +2350,7 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
 	{ "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
 	{ "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
 	{ "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+	{ "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 },
 	{ },
 };
 MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index afe39421d10c..df59a6919d87 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -1104,6 +1104,8 @@ struct amdgpu_device {
 	bool                            dc_enabled;
 	/* Mask of active clusters */
 	uint32_t			aid_mask;
+
+	bool				csib_initialized;
 };
 
 static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 1ecc6b095a87..b2def958aad2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4410,6 +4410,10 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon)
 	if (amdgpu_sriov_vf(adev))
 		amdgpu_virt_release_full_gpu(adev, false);
 
+	r = amdgpu_dpm_set_rlc_state(adev, false);
+	if (r)
+		dev_err(adev->dev, "Failed to notify RLC to be OFF.\n");
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
index 7429b20257a6..72085a3ef53c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
@@ -147,7 +147,7 @@ void amdgpu_ring_commit(struct amdgpu_ring *ring)
 	count %= ring->funcs->align_mask + 1;
 	ring->funcs->insert_nop(ring, count);
 
-	mb();
+	smp_wmb();
 	amdgpu_ring_set_wptr(ring);
 
 	if (ring->funcs->end_use)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
index f7ad883a70fa..8256f80d468d 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
@@ -3492,6 +3492,7 @@ static uint64_t gfx_v10_0_get_gpu_clock_counter(struct amdgpu_device *adev);
 static void gfx_v10_0_select_se_sh(struct amdgpu_device *adev, u32 se_num,
 				   u32 sh_num, u32 instance, int xcc_id);
 static u32 gfx_v10_0_get_wgp_active_bitmap_per_sh(struct amdgpu_device *adev);
+static int gfx_v10_0_wait_for_idle(void *handle);
 
 static int gfx_v10_0_rlc_backdoor_autoload_buffer_init(struct amdgpu_device *adev);
 static void gfx_v10_0_rlc_backdoor_autoload_buffer_fini(struct amdgpu_device *adev);
@@ -5931,7 +5932,7 @@ static int gfx_v10_0_cp_gfx_load_microcode(struct amdgpu_device *adev)
 	return 0;
 }
 
-static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev)
+static int gfx_v10_csib_submit(struct amdgpu_device *adev)
 {
 	struct amdgpu_ring *ring;
 	const struct cs_section_def *sect = NULL;
@@ -5939,13 +5940,6 @@ static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev)
 	int r, i;
 	int ctx_reg_offset;
 
-	/* init the CP */
-	WREG32_SOC15(GC, 0, mmCP_MAX_CONTEXT,
-		     adev->gfx.config.max_hw_contexts - 1);
-	WREG32_SOC15(GC, 0, mmCP_DEVICE_ID, 1);
-
-	gfx_v10_0_cp_gfx_enable(adev, true);
-
 	ring = &adev->gfx.gfx_ring[0];
 	r = amdgpu_ring_alloc(ring, gfx_v10_0_get_csb_size(adev) + 4);
 	if (r) {
@@ -6008,6 +6002,25 @@ static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev)
 
 		amdgpu_ring_commit(ring);
 	}
+
+	gfx_v10_0_wait_for_idle(adev);
+	adev->csib_initialized = true;
+
+	return 0;
+};
+
+static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev)
+{
+	/* init the CP */
+	WREG32_SOC15(GC, 0, mmCP_MAX_CONTEXT,
+		     adev->gfx.config.max_hw_contexts - 1);
+	WREG32_SOC15(GC, 0, mmCP_DEVICE_ID, 1);
+
+	gfx_v10_0_cp_gfx_enable(adev, true);
+
+	if (!adev->csib_initialized)
+		gfx_v10_csib_submit(adev);
+
 	return 0;
 }
 
@@ -7167,8 +7180,11 @@ static int gfx_v10_0_hw_fini(void *handle)
 	return 0;
 }
 
+static int gfx_v10_0_set_powergating_state(void *handle,
+					  enum amd_powergating_state state);
 static int gfx_v10_0_suspend(void *handle)
 {
+	gfx_v10_0_set_powergating_state(handle, AMD_CG_STATE_UNGATE);
 	return gfx_v10_0_hw_fini(handle);
 }
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 28e6fa8d7860..c1aeeb927a0b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -147,7 +147,7 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
 #define PSP_FOOTER_BYTES 0x100
 
 /* Maximum backlight level. */
-#define AMDGPU_MAX_BL_LEVEL 0xFFFF
+#define AMDGPU_MAX_BL_LEVEL 0xFFF
 
 /**
  * DOC: overview
@@ -7349,7 +7349,8 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
 				drm_add_modes_noedid(connector, 1920, 1080);
 	} else {
 		amdgpu_dm_connector_ddc_get_modes(connector, edid);
-		amdgpu_dm_connector_add_common_modes(encoder, connector);
+		if (connector->connector_type != DRM_MODE_CONNECTOR_eDP)
+			amdgpu_dm_connector_add_common_modes(encoder, connector);
 		amdgpu_dm_connector_add_freesync_modes(connector, edid);
 	}
 	amdgpu_dm_fbc_init(connector);
diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
index e5f4587aa40c..55bb9fa9eb62 100644
--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
@@ -963,7 +963,9 @@ void dce110_edp_backlight_control(
 		return;
 	}
 
-	if (link->panel_cntl) {
+	if (link->panel_cntl && !(link->dpcd_sink_ext_caps.bits.oled ||
+		link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 ||
+		link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) {
 		bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl);
 
 		if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) {
diff --git a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
index 81fd50ee97c3..fdbe3d42cd7b 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
@@ -75,6 +75,7 @@ static const struct hw_sequencer_funcs dcn301_funcs = {
 	.get_hw_state = dcn10_get_hw_state,
 	.clear_status_bits = dcn10_clear_status_bits,
 	.wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect,
+	.edp_backlight_control = dce110_edp_backlight_control,
 	.edp_power_control = dce110_edp_power_control,
 	.edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready,
 	.set_cursor_position = dcn10_set_cursor_position,
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
index 2039a345f23a..e4626f2072f0 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
@@ -266,8 +266,8 @@ bool set_default_brightness_aux(struct dc_link *link)
 	if (link && link->dpcd_sink_ext_caps.bits.oled == 1) {
 		if (!read_default_bl_aux(link, &default_backlight))
 			default_backlight = 150000;
-		// if < 5 nits or > 5000, it might be wrong readback
-		if (default_backlight < 5000 || default_backlight > 5000000)
+		// if < 1 nits or > 5000, it might be wrong readback
+		if (default_backlight < 1000 || default_backlight > 5000000)
 			default_backlight = 150000; //
 
 		return edp_set_backlight_level_nits(link, true,
diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 9f542f6e19ed..ec9e740aa913 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -420,6 +420,10 @@ struct amd_pm_funcs {
 				   struct dpm_clocks *clock_table);
 	int (*get_smu_prv_buf_details)(void *handle, void **addr, size_t *size);
 	void (*pm_compute_clocks)(void *handle);
+	/**
+	 * @system_features_control: Enable/disable all SMU features.
+	 */
+	int (*system_features_control)(void *handle, bool en);
 };
 
 struct metrics_table_header {
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index 078aaaa53162..5c36fc114e91 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -180,6 +180,24 @@ int amdgpu_dpm_set_mp1_state(struct amdgpu_device *adev,
 	return ret;
 }
 
+int amdgpu_dpm_set_rlc_state(struct amdgpu_device *adev, bool en)
+{
+	int ret = 0;
+	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+
+	if (pp_funcs && pp_funcs->system_features_control) {
+		mutex_lock(&adev->pm.mutex);
+
+		ret = pp_funcs->system_features_control(
+				adev->powerplay.pp_handle,
+				en);
+
+		mutex_unlock(&adev->pm.mutex);
+	}
+
+	return ret;
+}
+
 bool amdgpu_dpm_is_baco_supported(struct amdgpu_device *adev)
 {
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 42172b00be66..a96406c4109f 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -401,6 +401,8 @@ int amdgpu_dpm_mode1_reset(struct amdgpu_device *adev);
 int amdgpu_dpm_set_mp1_state(struct amdgpu_device *adev,
 			     enum pp_mp1_state mp1_state);
 
+int amdgpu_dpm_set_rlc_state(struct amdgpu_device *adev, bool en);
+
 int amdgpu_dpm_set_gfx_power_up_by_imu(struct amdgpu_device *adev);
 
 int amdgpu_dpm_baco_exit(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig
index 173dada218ec..2ad49e625c77 100644
--- a/drivers/gpu/drm/bridge/analogix/Kconfig
+++ b/drivers/gpu/drm/bridge/analogix/Kconfig
@@ -13,6 +13,12 @@ config DRM_ANALOGIX_ANX6345
 	  ANX6345 transforms the LVTTL RGB output of an
 	  application processor to eDP or DisplayPort.
 
+config DRM_ANALOGIX_ANX7580
+	tristate "Analogix ANX7580 Decoder"
+	depends on I2C
+	help
+	  Support for the Analogix ANX7580 to MIPI DSI bridge.
+
 config DRM_ANALOGIX_ANX78XX
 	tristate "Analogix ANX78XX bridge"
 	select DRM_ANALOGIX_DP
diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile
index 44da392bb9f9..f7f39671845a 100644
--- a/drivers/gpu/drm/bridge/analogix/Makefile
+++ b/drivers/gpu/drm/bridge/analogix/Makefile
@@ -3,4 +3,5 @@ analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o analogix-i2c-dptx.o
 obj-$(CONFIG_DRM_ANALOGIX_ANX6345) += analogix-anx6345.o
 obj-$(CONFIG_DRM_ANALOGIX_ANX7625) += anx7625.o
 obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
+obj-$(CONFIG_DRM_ANALOGIX_ANX7580) += anx7580.o
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o
diff --git a/drivers/gpu/drm/bridge/analogix/anx7580.c b/drivers/gpu/drm/bridge/analogix/anx7580.c
new file mode 100644
index 000000000000..2852f8671f59
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/anx7580.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2023, Valve Software. All rights reserved.
+ *
+ */
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "anx7580_regs.h"
+
+#define LOG_TAG "ANX"
+
+typedef struct {
+	struct i2c_client *client;
+	u8 panel_read_data[512];
+	int panel_read_reg_addr, panel_read_num_bytes;
+	unsigned int read_sid, read_offset, read_len;
+	uint16_t dp_vtotal;
+	struct delayed_work work;
+} anx7580_t;
+
+/* i2c functions ported from analogix chicago android driver */
+
+static int anx7580_i2c_write_byte( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 data )
+{
+	int ret = 0;
+
+	if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) {
+		pr_err( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr );
+		return -EINVAL;
+	}
+
+	anx->client->addr = ( ANX7580_SLAVE_ID_ADDR >> 1 );
+	ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) );
+	if ( ret < 0 ) {
+		pr_err("%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr);
+	}else{
+		anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 );
+		ret = i2c_smbus_write_byte_data( anx->client, (u8)(offset_addr&0x00FF), data );
+		if ( ret < 0 ) {
+			pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr );
+		}
+	}
+	return ret;
+}
+
+static int anx7580_i2c_write_block( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 length, u8 *p_data )
+{
+	int ret = 0;
+
+	if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) {
+		pr_info( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr );
+		return -EINVAL;
+	}
+
+	anx->client->addr = ( ANX7580_SLAVE_ID_ADDR >> 1 );
+	ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) );
+	if (ret < 0) {
+		pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr );
+	}else{
+		anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 );
+		ret = i2c_smbus_write_i2c_block_data( anx->client, (u8)(offset_addr&0x00FF), length, p_data );
+		if (ret < 0) {
+			pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr );
+		}
+	}
+	return ret;
+}
+
+static int anx7580_i2c_write_byte4( anx7580_t * anx, u8 slave_id, u16 offset_addr, u32 data )
+{
+	int ret = 0;
+	u8	buf[4],i;
+	for ( i=0; i<4; i++ ) {
+		buf[i] = (u8)(data&0x000000FF);
+		data = data >> 8;
+	}
+
+	ret = anx7580_i2c_write_block( anx, slave_id, offset_addr, 4, &buf[0] );
+
+	return ret;
+}
+
+static int anx7580_i2c_read_byte( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 *p_data )
+{
+	int ret = 0;
+
+	if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) {
+		pr_info( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr );
+		return -EINVAL;
+	}
+
+	anx->client->addr = ( ANX7580_SLAVE_ID_ADDR >> 1 );
+	ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) );
+	if (ret < 0) {
+		pr_err("%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr);
+	}else{
+		anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 );
+		ret = i2c_smbus_read_byte_data( anx->client, (u8)(offset_addr&0x00FF) );
+		if (ret < 0) {
+			pr_err(" %s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr );
+			return ret;
+		}
+		*p_data = (u8)ret;
+	}
+	return ret;
+}
+
+static int anx7580_i2c_read_block( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 length, u8 *p_data )
+{
+	int ret = 0;
+
+	if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) {
+		pr_err( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr );
+		return -EINVAL;
+	}
+
+	anx->client->addr = (ANX7580_SLAVE_ID_ADDR >> 1);
+	ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) );
+	if (ret < 0) {
+		pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr );
+	} else {
+		anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 );
+		ret = i2c_smbus_read_i2c_block_data( anx->client, (u8)(offset_addr&0x00FF), length, p_data );
+		if (ret < 0) {
+			pr_err("%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr);
+		}
+	}
+	return ret;
+}
+
+static int anx7580_i2c_read_byte4( anx7580_t * anx, u8 slave_id, u16 offset_addr, u32 *p_data )
+{
+	int ret = 0;
+	u8	buf[4],i;
+	
+	ret = anx7580_i2c_read_block( anx, slave_id, offset_addr, 4, &buf[0] );
+
+	if ( ret>=0 ) {
+		*p_data = 0;
+		for ( i=0; i<3; i++ ){
+			*p_data += (u32)(buf[3-i]);
+			*p_data = *p_data<<8;
+		}
+		*p_data += (u32)(buf[3-i]);
+	}
+	
+	return ret;
+}
+
+static void anx7580_panel_dcs_cmd( anx7580_t * anx, uint8_t cmd )
+{
+	uint32_t reg_val = DCS_WRITE_NO_PARAM | cmd << 8;
+	anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, reg_val );
+}
+
+static void anx7580_panel_write_immediate( anx7580_t * anx, uint8_t reg, uint8_t val )
+{
+	uint32_t reg_val = DCS_WRITE_ONE_PARAM | reg << 8 | val << 16;
+	anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, reg_val );
+}
+
+static void anx7580_panel_write_long( anx7580_t * anx, void * _buf, uint8_t len )
+{
+    const uint8_t *buf = _buf;
+    for ( uint32_t v = 0, l = len; l; ) {
+      int i;
+
+      // swap byte order in string to meet anx mipi port interface requirements
+      for ( i = 0; i < 4 && l; i++, l-- ) {
+        v = ( v >> 8 ) | ( *( buf++ ) << 24 );
+      }
+      anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_PLD_DATA, v >> ( 8 * ( 4 - i ) ) );
+    }
+    anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, ( ( len ) << 8 ) | DCS_WRITE_LONG );
+}
+
+//-----------------------------------------------------------------------------
+// Reset mipi output block
+// Must do this after panel read to clear buffer offset
+static int anx7580_mipi_reset( anx7580_t * anx )
+{
+  anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, PWR_UP, 0 );
+  return anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, PWR_UP, 1 );
+};
+
+//Minimum time between DCS transfers.
+//Right now, we are going to indiscriminately wait, since the methodology for
+//timing out and verifiying transaction seems to be fraught with peril.
+//
+//This seems to be pretty reliable in my tests.
+#define DCS_INTERWRITE_DELAY_MS 34   
+
+static uint32_t anx7580_dcs_read( anx7580_t * anx, uint8_t reg, uint8_t len, uint8_t *pay )
+{
+	uint32_t data, bytes_copied = 0, returned_bytes = 0;
+	uint16_t dcs_timeout_msec = 0;
+	uint32_t fifo_status;
+
+	// request len number of bytes from reg
+	anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, ( len << 8 ) | DCS_SET_RETURN_SIZE );
+	anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, ( uint32_t )DCS_READ | ( reg << 8 ) );
+
+	do
+	{
+		anx7580_i2c_read_byte4( anx, SLAVEID_MIPI_PORT0, CMD_PKT_STATUS, &fifo_status );
+		msleep(1);
+		dcs_timeout_msec++;
+	}
+	while (( fifo_status & 0x10 ) && ( dcs_timeout_msec < DCS_INTERWRITE_DELAY_MS ) );
+
+	if ( dcs_timeout_msec >= DCS_INTERWRITE_DELAY_MS ) {
+		pr_err( "anx7580: MIPI Read timeout\n" );
+		return 0;
+	}
+
+	// get data from anx7580 fifo
+	do
+	{
+		anx7580_i2c_read_byte4(anx, SLAVEID_MIPI_PORT0, GEN_PLD_DATA, &data );
+		for ( uint8_t i = 0; i < 4 && ( i + returned_bytes ) < len; i++ )
+		{
+			pay[bytes_copied++] = ( data >> ( i * 8 ) ) & 0xFF;
+		}
+		returned_bytes += 4;
+		anx7580_i2c_read_byte4( anx, SLAVEID_MIPI_PORT0, CMD_PKT_STATUS, &fifo_status );
+	} while ( !( fifo_status & 0x10 ) );
+
+	// must reset mipi buffers after read to correct offset
+	anx7580_mipi_reset(anx);
+
+	return bytes_copied;
+}
+
+static void anx7580_read_chip_id(anx7580_t * anx)
+{
+	u8 reg_temp;
+	u16 reg_int;
+	
+	anx7580_i2c_read_byte( anx,SLAVEID_SPI, CHIP_ID_HIGH, &reg_temp );
+	reg_int = ((((u16)reg_temp)<<8)&0xFF00);
+	anx7580_i2c_read_byte( anx,SLAVEID_SPI, CHIP_ID_LOW, &reg_temp );
+	reg_int |= (((u16)reg_temp)&0x00FF);
+	pr_info( "anx7580: Chip ID = %04X\n", reg_int );
+}
+
+static u8 anx7580_check_ocm_status(anx7580_t * anx)
+{
+	u8 reg_temp;
+	// Check OCM status
+	anx7580_i2c_read_byte( anx, SLAVEID_SERDES,SERDES_POWER_CONTROL, &reg_temp );
+	if ( OCM_LOAD_DONE == ( OCM_LOAD_DONE & reg_temp ) ) {
+		return 1;
+    }
+    return 0;
+}
+
+static void anx7580_get_dp_vtotal(anx7580_t * anx, uint16_t * dp_vtotal)
+{
+	uint8_t high, low;
+	anx7580_i2c_read_byte( anx, SLAVEID_MAIN_LINK, ADDR_VTOTAL_DBG, &high );
+	anx7580_i2c_read_byte( anx, SLAVEID_MAIN_LINK, ADDR_VTOTAL_DBG+4, &low );
+	*dp_vtotal = high << 8 | low;
+}
+
+// sysfs interface 
+static ssize_t bmode_store(struct device *device,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	int result;
+	unsigned int mode;
+	anx7580_t *anx = dev_get_drvdata(device);
+	if ( !anx ) {
+		return 0;
+	}
+
+    result = sscanf( buf, "%d", &mode );
+	if( result == 1 ) {
+		if ( mode == 1 ){
+			anx7580_panel_write_immediate( anx, 0x53, 0xE0 ); // high brightness mode
+		} else if (mode == 2){
+			anx7580_panel_write_immediate( anx, 0x53, 0x28 ); // normal mode, update brightness within 32 frame
+		} else if (mode == 3){
+			anx7580_panel_write_immediate( anx, 0x53, 0x20 ); // normal mode, update brightness within 32 frame
+		}
+	}
+	return count;
+}
+
+static ssize_t bmode_show(struct device *device,
+			   struct device_attribute *attr, char *buf)
+{
+	uint8_t brightness_mode_reg, count, bytes_copied = 0;
+	anx7580_t *anx = dev_get_drvdata( device );
+	// TODO return current brightness mode
+	bytes_copied = anx7580_dcs_read( anx, 0x53, 1, &brightness_mode_reg );
+	if ( brightness_mode_reg == 0xE0 ) {
+		count = sprintf( buf, "High Brightness\n");
+	} else if ( brightness_mode_reg == 0x28 ) {
+		count = sprintf( buf, "Normal Brightness 32 frames\n" );
+	} else if ( brightness_mode_reg == 0x20 ) {
+		count = sprintf( buf, "Normal Brightness 1 frames\n" );
+	} else {
+		count = sprintf( buf, "Unknown Brightness mode %x\n", brightness_mode_reg );
+	}
+	return count;
+}
+
+static ssize_t brightness_store(struct device *device,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	unsigned int result;
+	unsigned int brightness;
+	uint8_t cmd[3];
+	anx7580_t *anx = dev_get_drvdata( device );
+
+	result = sscanf( buf, "%d", &brightness );
+
+    if ( result == 1 ) {
+		// limit brightness to 16 bits
+		brightness &= 0xFFFF;
+		cmd[0] = 0x51;
+		cmd[1] = ( brightness & 0xFF00 ) >> 8;
+		cmd[2] = brightness & 0xFF;
+
+		anx7580_panel_write_long( anx, cmd, 3 );
+	}
+	return count;
+}
+
+static ssize_t brightness_show( struct device *device, struct device_attribute *attr, char *buf )
+{
+	uint8_t brightness_reg[2];
+	uint8_t count, bytes_copied = 0;
+	anx7580_t *anx = dev_get_drvdata( device );
+	// TODO return current brightness mode
+	bytes_copied = anx7580_dcs_read( anx, 0x51, 2, brightness_reg );
+	if (bytes_copied) {
+		count = sprintf( buf, "Brightness %d\n", (int)(brightness_reg[0] << 8 | brightness_reg[1]) );
+	} else {
+		count = sprintf( buf, "Brightness Unknwn\n" );
+	}
+	return count;
+}
+
+static ssize_t panel_read_show( struct device *device, struct device_attribute *attr, char *buf )
+{
+	int ret;
+	ssize_t len = 0;
+	unsigned int i;
+	anx7580_t *anx = dev_get_drvdata( device );
+	ret = anx7580_dcs_read( anx, anx->panel_read_reg_addr, anx->panel_read_num_bytes, anx->panel_read_data );
+	if ( ret < 0 ) {
+		pr_err( "anx7580: failed to read panel reg %u\n", anx->panel_read_reg_addr );
+		return ret;
+	} else {
+		for (i = 0; i < anx->panel_read_num_bytes; i++) {
+				ret = scnprintf( buf + len, PAGE_SIZE - len, "%x ",
+								anx->panel_read_data[i] );
+				if ( ret < 0 )
+						return ret;
+				len += ret;
+		}
+		buf[len - 1] = '\n';
+		return len;
+	}
+}
+
+static ssize_t panel_read_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count )
+{
+	int ret;
+	anx7580_t *anx = dev_get_drvdata( device );
+	ret = sscanf( buf, "%2x %d", &anx->panel_read_reg_addr, &anx->panel_read_num_bytes );
+	if(ret != 2) {
+		pr_err( "anx7580: panel read got wrong number of input parameters from sscanf" );
+		return -EINVAL;
+	}
+	return count;
+}
+
+static ssize_t panel_write_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count )
+{
+	int ret;
+	int addr, len, i, v;
+	char char_data[256];
+	u8 payload[256];
+	anx7580_t *anx = dev_get_drvdata(device);
+	// get the base parameters
+	ret = sscanf( buf, "%2x %d %s", &addr, &len, char_data );
+	if(ret != 3) {
+		pr_err( "anx7580: panel write got wrong number of input parameters (%d) from sscanf", ret );
+		return -EINVAL;
+	}
+	// TODO: for now we limit writes to a total of 256 bytes
+	if(len >256) {
+		pr_err( "anx7580: panel write exceed max length %d\n", len );
+		return -EINVAL;
+	}
+	payload[0] = addr;
+	// get the data payload
+	for ( i=0; i<len; i++ ) {
+	    if ( sscanf( char_data + i * 2, "%2x", &v ) != 1 ) break;
+        payload[i+1] = (unsigned char)v;
+	}
+
+    if ( len == 0 )
+	    anx7580_panel_dcs_cmd( anx, payload[0] );
+	if ( len == 1 )
+		anx7580_panel_write_immediate( anx, payload[0], payload[1] );
+	else
+		anx7580_panel_write_long( anx, payload, len );
+
+	pr_info( "anx7580: wrote %s to reg %x\n", char_data, addr );
+
+	return count;
+}
+
+static ssize_t panel_write_show( struct device *device, struct device_attribute *attr, char *buf )
+{
+	return 0;
+}
+
+static ssize_t anx7580_read_reg_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count )
+{
+    int ret;
+	anx7580_t *anx = dev_get_drvdata(device);
+	ret = sscanf( buf, "%x %x %u", &anx->read_sid, &anx->read_offset, &anx->read_len);
+	if ( ret != 3 ) {
+		pr_err( "anx7580: invalid reg read params\n" );
+		return -EINVAL;
+	}
+	return count;
+}
+
+static ssize_t anx7580_read_reg_show( struct device *device, struct device_attribute *attr, char *buf )
+{
+	ssize_t len = 0;
+	unsigned int i, ret;
+	uint8_t data[256];
+	anx7580_t *anx = dev_get_drvdata(device);
+	anx7580_i2c_read_block(anx, anx->read_sid, anx->read_offset, anx->read_len, data);
+	for (i = 0; i < anx->read_len; i++) {
+		ret = scnprintf( buf + len, PAGE_SIZE - len, "%x ", data[i] );
+		if ( ret < 0 )
+				return ret;
+		len += ret;
+	}
+	buf[len - 1] = '\n';
+	return len;
+}
+
+static ssize_t anx7580_write_reg_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count )
+{
+    int ret;
+	unsigned int write_byte_sid, write_byte_offset, write_byte_data;
+	anx7580_t *anx = dev_get_drvdata(device);
+	ret = sscanf( buf, "%x %x %x", &write_byte_sid, &write_byte_offset, &write_byte_data);
+	if ( ret != 3 ) {
+		pr_err( "anx7580: invalid write byte params\n" );
+		return -EINVAL;
+	}
+	anx7580_i2c_write_byte(anx, write_byte_sid, write_byte_offset, write_byte_data);
+	return count;
+}
+
+static ssize_t anx7580_write_reg_show( struct device *device, struct device_attribute *attr, char *buf )
+{
+	return 0;
+}
+
+static ssize_t anx7580_write_reg4_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count )
+{
+    int ret, i, v;
+	unsigned int sid, offset;
+	char char_data[4*2];
+	unsigned int reg_value = 0;
+	anx7580_t *anx = dev_get_drvdata(device);
+
+	// get the base parameters
+	ret = sscanf( buf, "%x %x %s", &sid, &offset, char_data );
+	if(ret != 3) {
+		pr_err( "anx7580: panel write got wrong number of input parameters (%d) from sscanf", ret );
+		return -EINVAL;
+	}
+
+	// get the data payload
+	for ( i=0; i<4; i++ ) {
+	    if ( sscanf( char_data + i * 2, "%2x", &v ) != 1 ) break;
+        reg_value = ( reg_value << (8) ) | (unsigned char)v;
+	}
+	// pr_info("anx7580: write4 %x %x = %x\n", sid, offset, reg_value);
+	anx7580_i2c_write_byte4(anx, sid, offset, reg_value);
+	return count;
+}
+
+static ssize_t anx7580_write_reg4_show( struct device *device, struct device_attribute *attr, char *buf )
+{
+	return 0;
+}
+
+static struct device_attribute anx7580_attrs[] = {
+	__ATTR(bmode, 0664, bmode_show, bmode_store),
+	__ATTR(brightness, 0664, brightness_show, brightness_store),
+	__ATTR(panel_read, 0664, panel_read_show, panel_read_store),
+	__ATTR(panel_write, 0664, panel_write_show, panel_write_store),
+	__ATTR(anx7580_read_reg, 0664, anx7580_read_reg_show, anx7580_read_reg_store),
+	__ATTR(anx7580_write_reg, 0664, anx7580_write_reg_show, anx7580_write_reg_store),
+	__ATTR(anx7580_write_reg4, 0664, anx7580_write_reg4_show, anx7580_write_reg4_store),
+};
+
+static int anx7580_probe(struct i2c_client *client,
+			      const struct i2c_device_id *id)
+{
+	anx7580_t *anx;
+	unsigned int i;
+	int ret;
+
+	if ( !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK) ) {
+		pr_err("%s: i2c bus does not support the anx7580\n", __func__);
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	anx = kzalloc(sizeof(anx7580_t), GFP_KERNEL);
+	if ( !anx ) {
+		pr_err("%s: failed to allocate driver data\n", __func__);
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	dev_set_drvdata( &client->dev, anx );
+    anx->client = client;
+
+    // anx7580 is powered on by bios, so we assume ocm wait period is complete
+    if ( anx7580_check_ocm_status(anx) )
+    {
+        // OCM load done
+        // Read Chip ID
+        anx7580_read_chip_id(anx);
+		// store current dp vtotal for future mode changes
+		anx7580_get_dp_vtotal(anx, &anx->dp_vtotal);
+		pr_info("anx7580: initial dp vtotal %d\n", anx->dp_vtotal);
+    }
+
+	for ( i=0; i < ARRAY_SIZE(anx7580_attrs); i++ )
+	{
+		if ( device_create_file(&client->dev, &anx7580_attrs[i]) )
+			pr_err( "anx7580: could not create sysfs index %d\n", i );
+	}
+
+exit:
+    return 0;
+}
+
+static void anx7580_remove(struct i2c_client *client)
+{
+    anx7580_t *data;
+	unsigned int i;
+	data = i2c_get_clientdata(client);
+	for (i=0; i < ARRAY_SIZE(anx7580_attrs); i++)
+	{
+		device_remove_file(&client->dev, &anx7580_attrs[i]);
+	}
+    kfree(data);
+}
+
+static int anx7580_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int anx7580_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(anx7580_pm_ops, anx7580_runtime_suspend,
+				 anx7580_runtime_resume, NULL);
+
+static const struct acpi_device_id anx7580_acpi_match[] = {
+        { "ANX7580A", 0 },
+        { }
+};
+MODULE_DEVICE_TABLE(acpi, anx7580_acpi_match);
+
+static const struct i2c_device_id anx7580_i2c_ids[] = {
+	{ "anx7580", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, anx7580_i2c_ids);
+
+static const struct of_device_id anx7580_of_ids[] = {
+	{ .compatible = "analogix,anx7580", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, anx7580_of_ids);
+
+static struct i2c_driver anx7580_i2c_driver = {
+        .driver = {
+                .name   = "anx7580",
+                .pm     = pm_ptr(&anx7580_pm_ops),
+                .of_match_table = anx7580_of_ids,
+                .acpi_match_table = anx7580_acpi_match,
+        },
+        .probe          = anx7580_probe,
+        .remove         = anx7580_remove,
+        .id_table       = anx7580_i2c_ids,
+};
+module_i2c_driver(anx7580_i2c_driver);
+
+MODULE_AUTHOR("Keith Mikoleit <keithm@valvesoftware.com>");
+MODULE_DESCRIPTION("Analogix ANX7580 DP to MIPI Bridge Interface");
+MODULE_LICENSE("GPL");
\ No newline at end of file
diff --git a/drivers/gpu/drm/bridge/analogix/anx7580_regs.h b/drivers/gpu/drm/bridge/analogix/anx7580_regs.h
new file mode 100644
index 000000000000..9b51239f90fe
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/anx7580_regs.h
@@ -0,0 +1,1033 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2023, Valve Software. All rights reserved.
+ *
+ */
+
+#ifndef __ANX7580_H__
+#define __ANX7580_H__
+
+/*
+// From ACPI Tables
+l   ANX1 (_HID:ANX7580A) for “Select Slave ID”, 7-bit slave address is 0x47
+l   ANX2 (_HID:ANX7580D) for “Select Offset”, 7-bit slave address is 0x43
+l   GPIO AGPIO90 for interrupt pin ANX_APU_INT#
+*/
+
+// combine the two endpoints into a single driver by manually modifying the i2c address
+#define ANX7580_SLAVE_ID_ADDR	0x8E
+#define ANX7580_OFFSET_ADDR		0x86
+
+/*********  ANX7580 Register  **********/
+
+#define _BIT0	0x01
+#define _BIT1	0x02
+#define _BIT2	0x04
+#define _BIT3	0x08
+#define _BIT4	0x10
+#define _BIT5	0x20
+#define _BIT6	0x40
+#define _BIT7	0x80
+
+// Register Slave ID definition
+#define SLAVEID_SPI			0x01
+#define SLAVEID_PPS			0x02
+#define SLAVEID_EDID		0x03
+#define SLAVEID_MIPI_CTRL	0x08
+#define SLAVEID_SERDES		0x0A
+#define SLAVEID_DP_TOP		0xA0
+#define SLAVEID_DPCD		0x10
+#define SLAVEID_MAIN_LINK	0x20
+#define SLAVEID_DP_IP		0x30
+#define SLAVEID_AUDIO		0x50
+#define SLAVEID_VIDEO		0x60
+#define SLAVEID_PLL			0x70
+#define SLAVEID_MIPI_PORT0	0xC0
+#define SLAVEID_MIPI_PORT1	0xD0
+#define SLAVEID_MIPI_PORT2	0xE0
+#define SLAVEID_MIPI_PORT3	0xF0
+
+//*************************************************
+//SLAVEID_SPI 0x0100 register offset_addr definition
+//*************************************************
+#define R_RAM_CS			0x0000
+#define R_RAM_ADDR_H		0x0001
+#define R_RAM_ADDR_L		0x0002
+#define R_RAM_LEN_H			0x0003
+#define R_RAM_LEN_L			0x0004
+#define R_RAM_CTRL			0x0005
+// bit definition
+#define FLASH_DONE			_BIT7
+#define BOOT_LOAD_DONE		_BIT6
+#define CRC_OK				_BIT5
+#define LOAD_DONE			_BIT4
+#define DECRYPT_EN			_BIT1
+#define LOAD_START			_BIT0
+
+#define R_FLASH_ADDR_H		0x000F
+#define R_FLASH_ADDR_L		0x0010
+#define R_FLASH_ADDR_0		0x0011
+#define R_FLASH_LEN_H		0x0031
+#define R_FLASH_LEN_L		0x0032
+#define R_FLASH_RW_CTRL		0x0033
+// bit definition
+#define READ_DELAY_SELECT		_BIT7
+#define GENERAL_INSTRUCTION_EN	_BIT6
+#define FLASH_ERASE_EN 			_BIT5
+#define RDID_READ_EN			_BIT4
+#define REMS_READ_EN			_BIT3
+#define WRITE_STATUS_EN			_BIT2
+#define FLASH_READ 				_BIT1
+#define FLASH_WRITE				_BIT0
+
+#define R_FLASH_STATUS_0	0x0034
+#define R_FLASH_STATUS_2	0x0036
+// data definition
+// Flash operation commands - refer to Table 2 in GD25D10B/05B datasheet
+#define  WRITE_ENABLE      0x06
+#define  WRITE_DISABLE     0x04
+#define  DEEP_POWER_DOWN   0xB9
+#define  DEEP_PD_REL       0xAB  /* Release from Deep Power-Down */
+#define  CHIP_ERASE_A      0xC7
+#define  CHIP_ERASE_B      0x60
+
+#define R_FLASH_STATUS_3	0x0037
+// data definition
+#define  SECTOR_ERASE		0x20
+#define  BLOCK_ERASE_32K	0x52
+#define  BLOCK_ERASE_64K	0xD8
+
+#define R_FLASH_STATUS_4	0x0038
+// bit definition
+/* Status Register Protect bit, operates in conjunction with the Write Protect (WP#) signal */
+/* The SRP bit and WP signal set the device to the Hardware Protected mode. */
+/* When the SRP = 1, and WP# signal is Low, the non-volatile bits of the Status Register (SRP, BP2, BP1, BP0)
+   become read-only and the Write Status Register (WRSR) instruction is not executed. */
+/* The default value of SRP bit is 0. */
+#define  SRP0   _BIT7
+
+/* Block Protect bits */
+/* These bits are non-volatile. They define the size of the area to be software protected against Program and Erase commands. */
+/* These bits are written with the Write Status Register (WRSR) command. */
+/* When the (BP4, BP3, BP2, BP1, BP0) bits are set to 1, the relevant memory area becomes protected against Page Program (PP),
+   Sector Erase (SE), and Block Erase (BE) commands. Refer to Table 1.0 in GD25D10B/05B datasheet for details. */
+/* The (BP4, BP3, BP2, BP1, BP0) bits can be written provided that the Hardware Protected mode has not been set. */
+#define  BP4   _BIT6
+#define  BP3   _BIT5
+#define  BP2   _BIT4
+#define  BP1   _BIT3
+#define  BP0   _BIT2
+
+/* Write Enable Latch bit, indicates the status of the internal Write Enable Latch. */
+/* When WEL bit is 1, the internal Write Enable Latch is set. */
+/* When WEL bit is 0, the internal Write Enable Latch is reset, and Write Status Register, Program or
+   Erase commands are NOT accepted. */
+/* The default value of WEL bit is 0. */
+#define  WEL   _BIT1
+
+/* Write In Progress bit, indicates whether the memory is busy in program/erase/write status register progress. */
+/* When WIP bit is 1, it means the device is busy in program/erase/write status register progress. */
+/* When WIP bit is 0, it means the device is not in program/erase/write status register progress. */
+/* The default value of WIP bit is 0. */
+#define  WIP   _BIT0
+
+#define R_FLASH_CTRL_0		0x003F
+// bit definition
+#define	AUX_DET				_BIT5
+
+#define R_DSC_CTRL_0		0x0040
+// bit definition
+#define READ_STATUS_EN		_BIT7
+#define DSC_EN				_BIT0
+
+
+#define	GPIO_CTRL_1			0x0048	
+#define	GPIO_CTRL_2			0x0049
+// bit definition
+#define HDCP14_ST			_BIT6
+#define R_AUX_DET_MASK		_BIT5
+#define TRAN_ORDER			_BIT4
+// data definition
+#define PANEL_INFO_MASK		0x1F
+
+#define GPIO_STATUS_1		0x004B
+// bit definition
+#define FLASH_WP			_BIT0
+
+#define FLASH_READ_D0		0x0060
+// data definition
+#define FLASH_READ_MAX_LENGTH	0x20
+#define FLASH_WRITE_MAX_LENGTH	0x20
+
+#define R_SYS_CTRL_1		0x0081
+#define REG_BAUD_DIV_RATIO	0x0087
+#define REG_OCM_INTR_END_COUNTER	0x0088
+#define OCM_DEBUG_CTRL		0x0089
+// bit definition
+#define OCM_INT_GATE		_BIT7
+#define OCM_RESET			_BIT6
+
+#define INT_NOTIFY_MCU0		0x0090
+// bit definition 
+#define AUX_CABLE_OUT		_BIT0
+#define VIDEO_STABLE		_BIT1
+#define VIDEO_RE_CALCULATE	_BIT2
+#define AUX_CABLE_IN		_BIT3
+#define VIDEO_INPUT_EMPTY	_BIT4
+#define AUDIO_MN_RST		_BIT5
+#define AUDIO_PLL_RST		_BIT7
+
+#define INT_NOTIFY_MCU1		0x0091
+// bit definition 
+#define CHIP_STANDBY_MODE		_BIT0
+#define CHIP_NORMAL_MODE		_BIT1
+#define DP_PHY_CTS_START		_BIT2
+#define DP_PHY_CTS_STOP			_BIT3
+#define DP_LINK_TRAINING_FAIL	_BIT4
+
+#define MISC_NOTIFY_OCM1	0x0092
+// bit definition
+#define TURN_ON_OCM_MAILBOX			_BIT1
+
+#define VIDEO_STABLE_DELAY_L	0x0093
+#define VIDEO_STABLE_DELAY_H	0x0094
+#define VIDEO_EMPTY_DELAY		0x0095
+
+#define CHIP_ID_HIGH		0x0096
+#define CHIP_ID_LOW			0x0097
+#define OCM_VERSION_MAJOR	0x0098
+#define OCM_BUILD_NUM		0x0099
+#define SECURE_OCM_VERSION	0x009B
+#define HDCP_LOAD_STATUS	0x009C
+// bit definition
+#define OCM_FW_CRC32			_BIT7
+#define HDCP_22_FW_LOAD_DONE	_BIT5
+#define HDCP_22_KEY_LOAD_DONE	_BIT4
+#define HDCP_14_KEY_LOAD_DONE	_BIT3
+#define HDCP_22_FW_CRC32 		_BIT2
+#define HDCP_22_KEY_CRC32		_BIT1
+#define HDCP_14_KEY_CRC32		_BIT0
+
+#define SW_PANEL_FRAME_RATE	0x009D
+
+#define MISC_NOTIFY_OCM0	0x009E
+// bit definition
+#define RESET_DP_PHY_WHEN_VIDEO_MUTE	_BIT1
+#define ENABLE_EMPTY_RESET_DP_PHY		_BIT2
+#define ENABLE_DP_LS_CHECK				_BIT3
+#define ENABLE_STANDBY_MODE				_BIT4
+#define AUD_MCLK_ALWAYS_ON				_BIT5
+#define PANEL_INFO_SET_DONE				_BIT6
+#define MCU_LOAD_DONE					_BIT7
+
+#define M_VALUE_MULTIPLY	0x009F 
+#define SW_H_ACTIVE_L		0x00A0
+#define SW_H_ACTIVE_H		0x00A1
+// data definition
+#define SW_H_ACTIVE_H_BITS	0x3F
+
+#define SW_HFP_L			0x00A2
+#define SW_HFP_H			0x00A3
+// data definition
+#define SW_HFP_H_BITS	0x0F
+
+#define SW_HSYNC_L			0x00A4
+#define SW_HSYNC_H			0x00A5
+// data definition
+#define SW_HSYNC_H_BITS	0x0F
+
+#define SW_HBP_L			0x00A6
+#define SW_HBP_H			0x00A7
+// data definition
+#define SW_HBP_H_BITS	0x0F
+
+#define SW_V_ACTIVE_L		0x00A8
+#define SW_V_ACTIVE_H		0x00A9
+// data definition
+#define SW_V_ACTIVE_H_BITS	0x3F
+
+#define SW_VFP_L			0x00AA
+#define SW_VFP_H			0x00AB
+// data definition
+#define SW_VFP_H_BITS	0x0F
+
+#define SW_VSYNC_L			0x00AC
+#define SW_VSYNC_H			0x00AD
+// data definition
+#define SW_VSYNC_H_BITS	0x0F
+
+#define SW_VBP_L			0x00AE
+#define SW_VBP_H			0x00AF
+// data definition
+#define SW_VBP_H_BITS	0x0F
+
+#define SW_PANEL_INFO_0		0x00B0
+// data definition
+#define REG_PANEL_COUNT_SHIFT		6
+#define REG_MIPI_TOTAL_PORT_SHIFT	4
+#define REG_MIPI_LANE_COUNT_SHIFT	2
+#define REG_PANEL_VIDEO_MODE_SHIFT	0
+// data definition
+#define REG_PANEL_COUNT			0xC0
+#define REG_MIPI_TOTAL_PORT		0x30
+#define REG_MIPI_LANE_COUNT		0x0C
+#define REG_PANEL_VIDEO_MODE	0x03
+
+#define SW_PANEL_INFO_1		0x00B1
+// data definition
+#define REG_PANEL_TRANS_MODE_SHIFT	2
+// data definition
+#define REG_PANEL_TRANS_MODE		0x0C
+// bit definition 
+#define REG_PANEL_ORDER			_BIT0
+#define REG_PANEL_DSC_MODE		_BIT1
+#define VIDEO_BIST_MODE			_BIT5
+#define SET_DPHY_TIMING			_BIT6
+
+#define SW_AUD_MAUD_SVAL_7_0	0xB2
+#define SW_AUD_MAUD_SVAL_15_8	0xB3
+#define SW_AUD_MAUD_SVAL_23_16	0xB4
+#define SW_AUD_NAUD_SVAL_7_0	0xB5
+#define SW_AUD_NAUD_SVAL_15_8	0xB6
+#define SW_AUD_NAUD_SVAL_23_16	0xB7
+
+#define TEST_PATTERN_CTRL	0x00ED
+// data definition
+	#define TEST_PATTERN_EN		0x80
+	#define DISPLAY_BIST_EN		0x08
+	#define TEST_PATTERN_MODE1	0x10
+	#define TEST_PATTERN_MODE2	0x20
+	#define TEST_PATTERN_MODE3	0x30
+
+#define H_BLANK_L			0x00EF
+#define H_BLANK_H			0x00F0
+
+#define REG_BYTE_CLK_EN		0x00F1
+#define MIPI_CLK_EN			0x00F2
+#define CONFIG_X_ACCESS_FIFO	0x00F6 // config_x(i2c_master) read data out
+#define CONFIG_X_CTRL_0			0x00F7 // config_x(i2c_master) device address
+#define CONFIG_X_CTRL_1			0x00F8 // config_x(i2c_master) offset
+#define CONFIG_X_CTRL_2			0x00F9
+// bit definition
+#define CONFIG_X_GLH_SEL		_BIT7
+#define CONFIG_X_CMD			_BIT6|_BIT5|_BIT4
+#define I2C_BYTE_READ			0x00
+#define I2C_BYTE_WRITE			0x10
+#define I2C_RESET				0x40
+#define CONFIG_X_SPEED			_BIT3|_BIT2
+#define CONFIG_X_NO_STOP		_BIT1
+#define CONFIG_X_NO_ACK			_BIT0
+
+#define CONFIG_X_CTRL_3			0x00FA // config_x(i2c_master) access number low byte
+#define CONFIG_X_CTRL_4			0x00FB
+// bit definition
+#define CONFIG_X_DDC_STATE			_BIT7|_BIT6|_BIT5|_BIT4|_BIT3
+#define CONFIG_X_MODE				_BIT2
+#define CONFIG_X_ACCESS_NUM_HIGH	_BIT1|_BIT0
+
+#define CONFIG_X_CTRL_5			0x00FC
+#define CONFIG_X_CTRL_6			0x00FD
+#define OCM_DEBUG				0x00FE
+#define R_VERSION			0x00FF
+
+//*************************************************
+//SLAVEID_PPS 0x0200 register offset_addr definition
+//*************************************************
+#define	REG_ADDR_ACTIVE_LINE_CFG_L	0x0014
+#define	REG_ADDR_ACTIVE_LINE_CFG_H	0x0015
+// data definition
+#define REG_V_ACTIVE_H_BITS	0x3F
+
+#define	REG_ADDR_V_SYNC_CFG			0x0017
+#define REG_ADDR_TOTAL_PIXEL_CFG_L	0x0019
+#define REG_ADDR_TOTAL_PIXEL_CFG_H	0x001A
+// data definition
+#define REG_H_TOTAL_H_BITS	0x3F
+
+#define	REG_ADDR_ACTIVE_PIXEL_CFG_L	0x001B
+#define REG_ADDR_ACTIVE_PIXEL_CFG_H	0x001C
+// data definition
+#define REG_H_ACTIVE_H_BITS	0x3F
+
+#define REG_ADDR_H_F_PORCH_CFG_L	0x001D
+#define REG_ADDR_H_F_PORCH_CFG_H	0x001E
+// data definition
+#define REG_HFP_H_BITS	0x0F
+
+#define REG_ADDR_H_SYNC_CFG_L		0x001F
+#define REG_ADDR_H_SYNC_CFG_H		0x0020
+// data definition
+#define REG_HSYNC_H_BITS	0x0F
+
+#define REG_ADDR_H_B_PORCH_CFG_L	0x0021
+#define	REG_ADDR_H_B_PORCH_CFG_H	0x0022
+// data definition
+#define REG_HBP_H_BITS	0x0F
+
+#define REG_ADDR_V_F_PORCH_CFG_L	0x0023
+#define	REG_ADDR_V_F_PORCH_CFG_H	0x0024
+// data definition
+#define REG_VFP_H_BITS	0x0F
+
+#define REG_ADDR_V_B_PORCH_CFG_L	0x0025
+#define	REG_ADDR_V_B_PORCH_CFG_H	0x0026
+// data definition
+#define REG_VBP_H_BITS	0x0F
+
+#define DSC_DEBUG_2			0x007C
+// bit definitions
+#define FIFO_OF_ERR_BUF00	_BIT7
+#define FIFO_OF_ERR_BUF01	_BIT6
+#define FIFO_OF_ERR_BUF10	_BIT5
+#define FIFO_OF_ERR_BUF11	_BIT4
+#define FIFO_UF_ERR_BUF00	_BIT3
+#define FIFO_UF_ERR_BUF01	_BIT2
+#define FIFO_UF_ERR_BUF10	_BIT1
+#define FIFO_UF_ERR_BUF11	_BIT0
+
+#define PPS_REG_0			0x0080
+#define PPS_REG_96			0x00e0
+#define PPS_REG_97			0x00e1
+#define PPS_REG_98			0x00e2
+#define PPS_REG_99			0x00e3
+#define PPS_REG_100			0x00e4
+#define PPS_REG_101			0x00e5
+#define PPS_REG_102			0x00e6
+#define PPS_REG_103			0x00e7
+#define PPS_REG_104			0x00e8
+#define PPS_REG_105			0x00e9
+#define PPS_REG_106			0x00ea
+#define PPS_REG_107			0x00eb
+#define PPS_REG_108			0x00ec
+#define PPS_REG_109			0x00ed
+#define PPS_REG_110			0x00ee
+#define PPS_REG_111			0x00ef
+
+#define R_MISC_DEBUG				0x00FB
+#define PRE_FILLER_BUF0_15_8		0x00FC
+#define PRE_FILLER_BUF0_7_0			0x00FD
+#define PRE_FILLER_BUF1_15_8		0x00FE
+#define PRE_FILLER_BUF1_7_0			0x00FF
+
+//*************************************************
+//EDID 0x0300 buffer register offset_addr definition
+//*************************************************
+#define EDID_EXTENSION_BUF		0x0080
+
+// Other EDID information
+#define EDID_LENGTH	128
+#define EDID_EXTENSION_LENGTH	128
+// EDID position
+#define EDID_MANUFACTURER_ID_H	0x08
+#define EDID_MANUFACTURER_ID_L	0x09
+#define EDID_PRODUCT_ID_L	0x0A
+#define EDID_PRODUCT_ID_H	0x0B
+#define EDID_SERIAL_NUM_L	0x0C
+#define EDID_SERIAL_NUM_H	0x0E
+#define EDID_WEEK			0x10
+#define EDID_YEAR			0x11
+
+#define EDID_DB1_PIXEL_CLOCK_L	0x36
+#define EDID_DB1_PIXEL_CLOCK_H	0x37
+
+#define EDID_DB1_BASE		0x36
+#define EDID_DB2_BASE		0x48
+#define EDID_DB3_BASE		0x5A
+#define EDID_DB4_BASE		0x6C
+
+#define EDID_DB_MAX			4
+#define EDID_DB_SIZE		18
+#define EDID_DB_DUMMY		0x10
+
+#define EDID_PIXEL_CLK_L	0
+#define EDID_PIXEL_CLK_H	1
+#define EDID_HACTIVE_L		2
+#define EDID_HBP_L			3
+#define EDID_HACT_HBP_H		4
+#define EDID_VACTIVE_L		5
+#define EDID_VBP_L			6
+#define EDID_VACT_VBP_H		7
+#define	EDID_HFP_L			8
+#define	EDID_HSYNC_L		9
+#define	EDID_VFP_VSYNC_L	10
+#define EDID_HFP_HSYNC_VFP_VSYNC_H	11
+#define EDID_H_DISPLAY_SIZE		12
+#define EDID_FEATURES_BITMAP	17
+
+#define EDID_EXTERN_PANEL_DATA	0x49
+#define EDID_VFP_MAX_VALUE		63
+
+//*************************************************
+//SLAVEID_MIPI_CTRL 0x0800 register offset_addr definition
+//*************************************************
+#define R_MIPI_TX_PORT_PD		0x0000
+// bit definition
+#define MIPI_PORT_3_PD		_BIT7
+#define MIPI_PORT_2_PD		_BIT6
+#define MIPI_PORT_1_PD		_BIT5
+#define MIPI_PORT_0_PD		_BIT4
+
+#define R_MIP_TX_PHY_TIMER_0	0x0001
+#define R_MIP_TX_PHY_TIMER_1	0x0002
+#define R_MIP_TX_PHY_TIMER_2	0x0003
+#define R_MIP_TX_PHY_TIMER_3	0x0004
+#define R_MIP_TX_PHY_TIMER_4	0x0005
+#define R_MIP_TX_PHY_TIMER_5	0x0006
+#define R_MIP_TX_PHY_TIMER_6	0x0007
+#define R_MIP_TX_PHY_TIMER_7	0x0008
+#define R_MIP_TX_PHY_TIMER_8	0x0009
+#define R_MIP_TX_PHY_TIMER_9	0x000A
+#define R_MIP_TX_PHY_TIMER_10	0x000B
+#define R_MIP_TX_PHY_TIMER_11	0x000C
+#define R_MIP_TX_PHY_TIMER_12	0x000D
+#define R_MIP_TX_PHY_TIMER_13	0x000E
+#define R_MIP_TX_TLPX_COUNTER	0x0018
+#define R_MIP_TX_SELECT			0x0019
+#define R_MIP_TX_STATE			0x0022
+// bit definition
+#define MIPI_TX_STABLE_STATE	_BIT0
+
+#define R_MIP_TX_INT			0x0023
+// bit definition
+#define MIPI_PORT_3_INT			_BIT5
+#define MIPI_PORT_2_INT			_BIT4
+#define MIPI_PORT_1_INT			_BIT3
+#define MIPI_PORT_0_INT			_BIT2
+#define MIPI_STABLE_INT			_BIT1
+#define MIPI_JITTER_INT			_BIT0
+
+#define R_MIP_TX_INT_MASK		0x0024
+// bit definition
+#define	MIPI_STABLE_INT_MASK	_BIT1
+#define MIPI_JITTER_INT_MASK	_BIT0
+
+#define R_MIP_TX_INT_CLR		0x0025
+// bit definition
+#define MIPI_STABLE_INT_CLR		_BIT1
+#define MIPI_JITTER_INT_CLR		_BIT0
+
+#define R_MIP_TX_PHY_TIMER_14	0x002A
+#define R_MIP_TX_PHY_TIMER_15	0x002B
+#define R_MIP_TX_PHY_TIMER_16	0x002C
+#define R_MIP_TX_PHY_TIMER_17	0x002D
+#define R_MIP_TX_PHY_TIMER_18	0x002E
+#define R_MIP_TX_PHY_TIMER_19	0x002F
+#define R_MIP_TX_PHY_TIMER_20	0x0030
+#define R_MIP_TX_PHY_TIMER_21	0x0031
+#define R_MIPI_PHY				0x003A
+
+//*************************************************
+//SLAVEID_SERDES 0x0A00 register offset_addr definition
+//*************************************************
+#define SERDES_REG_2				0x0002
+#define SERDES_REG_3				0x0003
+// bit definition
+#define REG_BYPASS_SIGNAL_DET		_BIT5
+#define REG_BYPASS_STATE			_BIT4
+
+#define SERDES_REG_7				0x0007
+#define SERDES_REG_9				0x0009
+// bit definition
+#define REG_LANE_OK_SW				_BIT4
+#define SWAP_AUX_R					_BIT5
+#define SWAP_AUX_T					_BIT6
+
+#define DPPLL_REG2					0x0022
+// bit definition
+#define EXT_INTR_OUTPUT				_BIT2
+
+#define SERDES_REG_38				0x0026
+// data definition
+#define ALL_SET_LANE0				0x00
+#define ALL_SET_LANE1				0x55
+#define ALL_SET_LANE2				0xAA
+#define ALL_SET_LANE3				0xFF
+// data definition
+#define PHY_SINK_TEST_LANE_SEL_0	0x00
+#define PHY_SINK_TEST_LANE_SEL_1	0x10
+#define PHY_SINK_TEST_LANE_SEL_2	0x20
+#define PHY_SINK_TEST_LANE_SEL_3	0x30
+
+#define SERDES_SET_1_RX_REG1		0x002B
+#define SERDES_SET_2_RX_REG2		0x002C
+#define SERDES_SET_5_RX_REG5		0x002F
+
+#define SERDES_SET_7				0x0031
+#define SERDES_SET_8_RX_REG8		0x0032
+#define SERDES_SET_9_RX_REG9		0x0033
+#define SERDES_SET_13_RX_REG13		0x0037
+#define SERDES_SET_14_RX_REG14		0x0038
+#define SERDES_SET_15_RX_REG15		0x0039
+#define SERDES_POWER_CONTROL		0x003C
+// bit definition
+#define OCM_LOAD_DONE				_BIT4
+#define PD_V10_AUX_PHY				_BIT3
+#define PD_V10_DPRX					_BIT2
+#define PD_V10_APLL					_BIT1
+#define PD_V10_MIPI					_BIT0
+
+
+#define REG0_0_RX_REG0				0x003E
+#define REG0_1_RX_REG0				0x003F
+#define REG0_2_RX_REG0				0x0040
+#define REG0_3_RX_REG0				0x0041
+#define REG7_0_RX_REG7				0x0042
+#define REG7_1_RX_REG7				0x0043
+#define REG7_2_RX_REG7				0x0044
+#define REG7_3_RX_REG7				0x0045
+#define REG16_0_RX_REG16			0x0046
+#define REG16_1_RX_REG16			0x0047
+#define REG16_2_RX_REG16			0x0048
+#define REG16_3_RX_REG16			0x0049
+#define REG_CR						0x004A
+
+//*************************************************
+//SLAVEID_DP_TOP 0xA000 register offset_addr definition
+//*************************************************
+#define ADDR_PWD_SEL			0x0000
+#define HDCP1_PWD_SW_EN			_BIT4
+
+#define ADDR_PWD_CTRL_0			0x0004
+// bit definition
+#define PWD_AUX_CH				_BIT4
+
+#define ADDR_PWD_CTRL_1			0x0008
+// bit definition
+#define PWD_HDCP2				_BIT5
+#define PWD_HDCP1				_BIT4
+#define	PWD_AUDPLL				_BIT3
+#define PWD_AUD					_BIT2
+
+#define ADDR_RST_SEL_0			0x0010
+// bit definition
+#define HDCP1DEC_AUTO_RST		_BIT5
+#define HDCP1AUTH_AUTO_RST		_BIT4
+#define AUD_AUTO_RST			_BIT3
+
+#define ADDR_RST_SEL_1			0x0014
+// bit definition
+#define MAIN_VID_CHANGE_RST_EN	_BIT4
+#define ALIGN_STATUS_RST_EN		_BIT3
+#define LANE_DEC_RST_EN			_BIT2
+#define AUDIOPLL_AUTO_RST		_BIT1
+#define VIDEOPLL_AUTO_RST		_BIT0
+
+
+#define ADDR_RST_CTRL_0			0x0018
+// bit definition
+#define RST_SW_RESET			_BIT1
+#define RST_LOGIC_RESET			_BIT0
+
+#define ADDR_RST_CTRL_1			0x001C
+// bit definition
+#define RST_SERDESCTRL_RESET	_BIT7
+#define RST_HDCP2_RESET			_BIT6
+#define RST_HDCP1DEC_RESET		_BIT5
+#define RST_HDCP1AUTH_RESET		_BIT4
+#define RST_VID_RESET			_BIT3
+#define RST_MAINLINK_RESET		_BIT2
+#define RST_RXTOP_RESET			_BIT1
+#define RST_AUX_CH_RESET		_BIT0
+
+#define ADDR_RST_CTRL_2			0x0020
+// bit definition
+#define RST_VIDEOPLLPRE_RESET	_BIT4
+#define RST_VIDEOPLL_RESET		_BIT3
+#define RST_AUDIOPLL_RESET		_BIT2
+#define RST_AUDIOMNADJ_RESET	_BIT1
+#define RST_AUDIO_RESET			_BIT0
+
+#define ADDR_MISC_CTRL			0x0030
+#define ADDR_POWER_STATUS		0x0040
+// bit definition
+#define AUX_STATUS				_BIT2
+#define MAIN_LINK_STATUS		_BIT1
+#define VIDEO_STATUS			_BIT0
+
+#define ADDR_SW_INTR_CTRL		0x0044
+// bit definition
+#define PLL_INTR				_BIT7
+#define PLL_INTR_MASK			_BIT6
+#define SOFT_INTR				_BIT0
+
+#define ADDR_INTR				0x0048
+#define ADDR_INTR_MASK			0x004c
+#define MAIN_LINK_INTR		_BIT0
+#define DP_IP_INTR			_BIT1
+#define DPCD_INTR			_BIT2
+#define AUDIO_INTR			_BIT3
+#define VIDEO_INTR			_BIT4
+#define SERDES_INTR			_BIT5		
+#define HDCP1_INTR			_BIT6
+#define VIDEO_ON_INT		_BIT7
+
+//*************************************************
+//SLAVEID_DPCD 0x1000 register offset_addr definition
+//*************************************************
+#define DPCD_REV				0x0000
+#define MAX_LINK_RATE			0x0001
+#define MAX_LANE_COUNT			0x0002
+#define MAX_DOWNSPREAD			0x0003
+#define DP_PWR_VOLTAGE_CAP		0x0004
+#define DOWN_STREAM_PORT_PRESENT	0x0005
+#define MAIN_LINK_CHANNEL_CODING	0x0006
+#define DOWN_STREAM_PORT_COUNT		0x0007
+#define RECEIVE_PORT0_CAP_0			0x0008
+#define RECEIVE_PORT0_CAP_1			0x0009
+#define RECEIVE_PORT1_CAP_0			0x000A
+#define eDP_CONFIGURATION_CAP		0x000D
+#define TRAINING_AUX_RD_INTERVAL	0x000E
+
+#define LINK_BW_SET				0x0100
+// data definition
+#define DPCD_BW_1P62G	0x06
+#define DPCD_BW_2P7G	0x0A
+#define DPCD_BW_5P4G	0x14
+#define DPCD_BW_6P75G	0x1E
+
+#define LANE_COUNT_SET			0x0101
+#define TRAINING_PATTERN_SET	0x0102
+// data definition
+#define TRAINING_PATTERN_SELECT	0x03
+#define LT_NOT_IN_PROGRESS		0x00
+#define LT_PATTERN_SEQUENCE1	0x01
+#define LT_PATTERN_SEQUENCE2	0x02
+
+#define SINK_COUNT					0x0200
+#define LANE_ALIGN_STATUS_UPDATED	0x0204
+// bit definition
+#define INTERLANE_ALIGN_DONE		_BIT0
+
+#define TEST_SINK_MISC				0x0246
+// data definition
+#define TEST_CRC_COUNT				0x0F
+// bit definition
+#define TEST_CRC_SUPPORTED			_BIT5
+
+#define TEST_SINK					0x0270
+// bit definition
+#define TEST_SINK_START			_BIT0
+#define PHY_SINK_TEST_LANE_SEL	0x30
+#define	PHY_SINK_TEST_LANE_EN	_BIT7
+#define PHY_SINK_TEST_LANE_SEL_POS	4
+
+#define DPCD_SINK_IEEE_OUI_FIRST		0x0400
+#define DPCD_SINK_IEEE_OUI_SECOND		0x0401
+#define DPCD_SINK_IEEE_OUI_THIRD		0x0402
+
+#define ADDR_BW_CHANGE			0x04F1
+#define DPCD_BW_CHANDE  	_BIT6
+
+#define AUX_FLOATING_ZONE		0x04F2
+#define FLOATING_DPCD_0_INTR_0	_BIT0
+
+#define DPCD_INTR0				0x04F4
+#define DPCD_INTR1				0x04F5
+#define DPCD_INTR2				0x04F6
+#define DPCD_INTR3				0x04F7
+
+#define DPCD_BRANCH_IEEE_OUI_FIRST		0x0500
+#define DPCD_BRANCH_IEEE_OUI_SECOND		0x0501
+#define DPCD_BRANCH_IEEE_OUI_THIRD		0x0502
+
+#define DPCD_POWEWR_STATE		0x0600
+// data difinition
+#define POWER_STATE_MASK		0x07
+#define POWER_STATE_NORMAL		0x01
+#define POWER_STATE_SLEEP		0x02
+#define POWER_STATE_STANDBY		0x05
+
+//*************************************************
+//SLAVEID_MAIN_LINK 0x2000 register offset_addr definition
+//*************************************************
+#define ADDR_HSTART_DBG					0x0048
+#define ADDR_HSW_DBG					0x0050
+#define ADDR_HTOTAL_DBG					0x0058
+#define ADDR_HWIDTH15_8_DBG				0x0060
+#define ADDR_HWIDTH7_0_DBG				0x0064
+#define ADDR_VHEIGHT15_8_DBG			0x0068
+#define ADDR_VHEIGHT7_0_DBG				0x006C
+#define ADDR_VSW_DBG					0x0078
+#define ADDR_VTOTAL_DBG					0x0080
+#define ADDR_MAIN_LINK_CTRL				0x009C
+// bit definition
+#define FORCE_MN_READY				_BIT7
+#define FORCE_M_N					_BIT6
+#define FIELD_INV_EN				_BIT5
+#define MAIN_WRITE_EN				_BIT4
+#define REFILL_ON_FIFO_ERROR		_BIT3
+#define RESET_ON_FIFO_ERROR			_BIT2
+#define OVERFLOW_COMPENSATE_EN		_BIT1
+#define UNDERFLOW_COMPENSATE_EN		_BIT0
+
+#define ADDR_M_FORCE_VALUE_3			0x00A0
+#define ADDR_M_FORCE_VALUE_2			0x00A4
+#define ADDR_M_FORCE_VALUE_1			0x00A8
+#define ADDR_N_FORCE_VALUE_3			0x00AC
+#define ADDR_N_FORCE_VALUE_2			0x00B0
+#define ADDR_N_FORCE_VALUE_1			0x00B4
+#define ADDR_VIDEO_ACTIVE_MASK			0x00D4
+// bit defintion
+#define VIDEO_UNMUTE_JUDGE_MASK			_BIT2
+#define MSA_JUDGE_MASK					_BIT1
+#define VIDEO_M_READY_MASK				_BIT0
+
+#define ADDR_VIDEO_FIFO_PRE_FILLER_L	0x00E8
+#define ADDR_VIDEO_FIFO_PRE_FILLER_H	0x00EC
+#define ADDR_AVI_INFOR					0x0100
+#define ADDR_LINK_LAYER_STATE_2			0x0284
+// bit definition
+#define MAIN_DATA_OUT_EN				_BIT4
+
+#define ADDR_VID_M_RPT_23_16			0x0290
+#define ADDR_VID_M_RPT_15_8				0x0294
+#define ADDR_VID_M_RPT_7_0				0x0298
+#define ADDR_NVID_23_16					0x029C
+#define ADDR_NVID_15_8					0x02A0
+#define ADDR_NVID_7_0					0x02A4
+#define ADDR_AUD_PACK_STATUS			0x02B4
+// data definition
+#define AUDIO_CHANNEL_COUNT				0x07
+
+#define ADDR_MAIN_LINK_INTR0			0x02C0
+#define ADDR_MAIN_LINK_INTR1			0x02C4
+#define ADDR_MAIN_LINK_INTR2			0x02C8
+// bit definition
+#define BE_CNT_ERR_IN_FRAME_INTR		_BIT1
+#define BS_CNT_ERR_IN_FRAME_INTR		_BIT0
+
+#define ADDR_MAIN_LINK_INTR0_MASK		0x02D0
+// bit definition
+#define M_N_AUD_IND						_BIT6
+#define VSC_PACKAGE_CHANGE				_BIT5
+#define SPD_INFO_CHANGE					_BIT4
+#define AVI_INFO_CHANGE					_BIT3
+#define MPEG_INFO_CHANGE				_BIT2
+#define OTHER_INFO_RECEIVED_INT			_BIT1
+#define AUDIO_INFO_CHANGE				_BIT0
+
+#define ADDR_MAIN_LINK_INTR1_MASK		0x02D4
+// bit definition
+#define MSA_UPDATE_INTR					_BIT6
+#define AUDIO_CH_COUNT_CHANGE_INT		_BIT5
+#define AUDIO_M_N_CHANGE_INT			_BIT4
+#define MAIN_LOST_FLAG					_BIT3
+#define VOTING_ERROR					_BIT2
+#define MAIN_FIFO_OVFL					_BIT1
+#define MAIN_FIFO_UNFL					_BIT0
+
+#define ADDR_MAIN_LINK_INTR2_MASK		0x02D8
+// bit defintion
+#define BE_CNT_ERR_IN_LINE_INTR			_BIT3
+#define BS_CNT_ERR_IN_LINE_INTR			_BIT2
+#define BE_CNT_ERR_IN_FRAME_INTR		_BIT1
+#define BS_CNT_ERR_IN_FRAME_INTR		_BIT0
+
+#define ADDR_MAIN_LINK_INTR0_AEC		0x02E0
+#define ADDR_MAIN_LINK_INTR1_AEC		0x02E4
+#define ADDR_MAIN_LINK_INTR2_AEC		0x02E8
+#define ADDR_MAIN_LINK_STATUS_0			0x02F0
+// bit definition
+#define VIDEO_MN_READY_INT				_BIT1
+#define VIDEO_UNMUTE_INT				_BIT0
+
+
+//*************************************************
+//SLAVEID_DP_IP 0x3000 register offset_addr definition
+//*************************************************
+#define ADDR_SYSTEM_CTRL_0			0x0028
+// bit definition
+#define SYNC_STATUS_SEL			_BIT5
+#define ALIGN_LATCH_LOW			_BIT4
+#define FORCE_HPD_VALUE			_BIT3
+#define FORCE_HPD_EN			_BIT2
+#define POL_MAKE_SEL			_BIT1
+#define CHK_DU_EN				_BIT0
+
+#define ADDR_SYSTEM_CTRL_1			0x002C
+#define ADDR_SYSTEM_STATUS_1		0x0038
+// bit definition
+#define AUX_CMD_RCV			_BIT1
+#define AUX_CMD_REPLY		_BIT0
+
+#define ADDR_RCD_PN_CONVERTE			0x0064
+// bit definition
+#define BYPASS_RC_PAT_CHK			_BIT5
+
+#define ADDR_HDCP2_CTRL				0x0140
+// bit definition
+#define HDCP2_FW_EN		_BIT5
+#define HDCP_VERSION	_BIT6
+
+#define ADDR_HDCP2_DEBUG_2				0x014C
+#define ADDR_HDCP2_STATUS				0x0154
+// bit definition
+#define ENCRYPT_STATUS					_BIT1
+
+#define ADDR_FLOATING_DPCD_ZONE_0_L		0x0180
+#define ADDR_FLOATING_DPCD_ZONE_0_H		0x0184
+#define ADDR_FLOATING_DPCD_ZONE_1_L		0x0188
+#define ADDR_FLOATING_DPCD_ZONE_1_H		0x018C
+#define ADDR_FLOATING_DPCD_ZONE_2_L		0x0190
+#define ADDR_FLOATING_DPCD_ZONE_2_H		0x0194
+#define ADDR_FLOATING_DPCD_ZONE_3_L		0x0198
+#define ADDR_FLOATING_DPCD_ZONE_3_H		0x019C
+
+#define ADDR_HPD_ACTIVE_WAIT_TIMER	0x0270
+
+#define ADDR_AUX_CH_STATUS			0x030C
+// bit definition
+#define AUX_ADO_INTR_LAT		_BIT3
+#define AUX_TIMEOUT_LAT			_BIT2
+#define AUX_MII_ERR_LAT			_BIT1
+#define AUX_LEN_ERR_LAT			_BIT0
+
+#define ADDR_DPIP_INTR				0x0320
+#define ADDR_DPIP_INTR_MASK			0x0330
+// bit definition
+#define ALIGN_STATUS_UNLOCK_INTR	_BIT3
+#define LINK_TRAINING_FAIL_INTR		_BIT2
+#define AUX_CH_ERR_INTR				_BIT1
+#define VID_FORMAT_CHANGE			_BIT0
+
+#define ADDR_DPIP_INTR_AEC			0x0340
+#define ADDR_AUX_ADDR_L				0x0368
+// bit definition
+#define AUX_ADDR_ERROR				0x40
+
+//*************************************************
+//SLAVEID_AUDIO 0x5000 register offset_addr definition
+//*************************************************
+#define ADDR_AUD_MCTRL		0x000C
+// bit definition
+#define AUD_SOFTMUTE_EN				_BIT7
+#define HARD_MUTE_EN				_BIT3
+#define AUD_MUTEDONE_INT_ON_UNMUTE	_BIT2
+#define AUD_UNMUTE_ON_DE			_BIT1
+#define AUD_MUTE					_BIT0
+
+#define ADDR_AUD_INTR		0x0010
+#define ADDR_AUD_INTR_MASK	0x0014
+// bit definition
+#define AUD_CH_STATUS_CHANGE_INT	_BIT7
+#define VBID_AUD_MUTE_INTR			_BIT6
+#define AUD_FIFO_UND_INT			_BIT5
+#define AUD_FIFO_OVER_INT			_BIT4
+#define AUD_LINKERR_INT				_BIT3
+#define AUD_DECERR_INT				_BIT2
+#define AUD_SPDIFERR_INT			_BIT1
+#define AUD_MUTEDONE_INT			_BIT0
+
+#define ADDR_AUD_AAC		0x0044
+// bit definition
+#define AAC_EN				_BIT0
+
+#define ADDR_AUD_INTR_AEC	0x0048
+#define ADDR_AUD_ACR_CTRL_1	0x0050
+// bit definition
+#define MAUD_SEL			_BIT1
+#define NAUD_SEL			_BIT0
+
+#define ADDR_NAUD_SVAL_7_0		0x0054
+#define ADDR_NAUD_SVAL_15_8		0x0058
+#define ADDR_NAUD_SVAL_23_16	0x005C
+#define ADDR_MAUD_SVAL_7_0		0x0084
+#define ADDR_MAUD_SVAL_15_8		0x0088
+#define ADDR_MAUD_SVAL_23_16	0x008C
+
+#define ADDR_DBG_AUD_SAMPLE_CNT_7_0		0x00C8
+#define ADDR_DBG_AUD_SAMPLE_CNT_15_8	0x00CC
+#define	ADDR_AUD_CTRL1		0x00DC
+#define	ADDR_AUD_CTRL2		0x00E0
+#define ADDR_AUD_AFC_CTRL	0x0100
+// bit definition
+#define RE_COUNT_EN			_BIT2
+#define STEP_SCALE			_BIT1
+#define AFC_FUNC_EN			_BIT0
+
+#define	ADDR_AUD_CTRL4		0x0248	
+#define	ADDR_DBG_AUD_CS_3	0x0270
+// bit definition
+#define AUD_SAMPLE_RATE		0x0F
+
+//*************************************************
+//SLAVEID_VIDEO 0x6000 register offset_addr definition
+//*************************************************
+#define ADDR_ACT_PIX_LOW	0x0010
+#define ADDR_ACT_PIX_HIGH	0x0014
+#define ADDR_ACT_LINE_LOW	0x0018
+#define ADDR_ACT_LINE_HIGH	0x001C
+#define ADDR_VID_STABLE_DET	0x0040
+// bit definition
+#define VID_STRM_STABLE_STATUS	_BIT2
+
+#define ADDR_VID_INT		0x00C0
+#define ADDR_VID_INT_MASK	0x00C4
+// bit definition
+#define VSYNC_DET_INT			_BIT5
+#define V_RES_CHANGED			_BIT4
+#define H_RES_CHANGED			_BIT3
+#define SYNC_POL_CHANGED		_BIT2
+#define VID_TYPE_CHANGED		_BIT1
+#define VID_STRM_STABLE_INT		_BIT0
+
+//*************************************************
+//SLAVEID_PLL 0x7000 register offset_addr definition
+//*************************************************
+#define ADDR_VPLL_ANALOG_4	0x0010
+#define ADDR_VPLL_CTRL_0	0x0014
+#define ADDR_VPLL_CTRL_2	0x001C
+#define ADDR_VPLL_CTRL_3	0x0020
+#define ADDR_PLL_INTR		0x0090
+#define ADDR_PLL_INTR_MASK	0x0094
+// bit definition
+#define VPLL_UNLOCK_INT		_BIT3
+#define APLL_UNLOCK_INT		_BIT2
+#define APLL_INTP_N_CHG		_BIT1
+#define APLL_INTP_FS_CHG	_BIT0
+
+#define ADDR_PLL_INTR_AEC	0x0098
+
+//*************************************************
+//MIPI TX 0xC000 register offset_addr definition
+//*************************************************
+#define PWR_UP				0x0004
+#define CLKMGR_CFG			0x0008
+#define DPI_COLOR_CODING	0x0010
+#define DPI_CFG_POL			0x0014
+#define PCKHDL_CFG			0x002C
+#define GEN_VCID			0x0030
+#define MODE_CFG			0x0034
+#define VID_MODE_CFG		0x0038
+#define VID_PKT_SIZE		0x003C
+#define VID_HSA_TIME		0x0048
+#define VID_HBP_TIME		0x004C
+#define VID_HLINE_TIME		0x0050
+#define VID_VSA_LINES		0x0054
+#define VID_VBP_LINES		0x0058
+#define VID_VFP_LINES		0x005C
+#define VID_VACTIVE_LINES	0x0060
+#define CMD_MODE_CFG		0x0068
+#define LPCLK_CTRL			0x0094
+#define PHY_TMR_LPCLK_CFG	0x0098
+#define PHY_TMR_CFG			0x009C
+#define PHY_RSTZ			0x00A0
+#define PHY_IF_CFG			0x00A4
+#define INT_MSK0			0x00C4
+#define INT_MSK1			0x00C8
+#define GEN_HDR				0x006C
+#define GEN_PLD_DATA		0x0070
+#define CMD_PKT_STATUS		0x0074
+// bit definition
+#define gen_pld_w_empty		_BIT2
+
+#define DSC_PARAMETER		0x00F0
+
+// MIPI DCS packet types
+#define DCS_WRITE_NO_PARAM         0x05
+#define DCS_WRITE_ONE_PARAM        0x15
+#define DCS_WRITE_LONG             0x39
+#define DCS_SET_RETURN_SIZE        0x37
+#define DCS_READ                   0x06
+
+#endif  /* __ANX7580_H__ */
\ No newline at end of file
diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c
index 0cb646cb04ee..014101e92b5a 100644
--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c
+++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c
@@ -395,6 +395,13 @@ static const struct dmi_system_id orientation_data[] = {
 		  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
 		},
 		.driver_data = (void *)&lcd800x1280_rightside_up,
+	}, {	/* Valve Steam Deck */
+		.matches = {
+		  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"),
+		  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+		  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
+		},
+		.driver_data = (void *)&lcd800x1280_rightside_up,
 	}, {	/* VIOS LTH17 */
 		.matches = {
 		  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile
index cc47e0114595..6bbaa96dc526 100644
--- a/drivers/net/wireless/ath/ath11k/Makefile
+++ b/drivers/net/wireless/ath/ath11k/Makefile
@@ -17,7 +17,9 @@ ath11k-y += core.o \
 	    peer.o \
 	    dbring.o \
 	    hw.o \
-	    pcic.o
+	    pcic.o \
+            wow.o \
+            unitest.o
 
 ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
 ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
@@ -30,7 +32,7 @@ obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o
 ath11k_ahb-y += ahb.o
 
 obj-$(CONFIG_ATH11K_PCI) += ath11k_pci.o
-ath11k_pci-y += mhi.o pci.o
+ath11k_pci-y += mhi.o pci.o coredump_fw.o coredump.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 76f275ca53e9..fa460d3fe5e2 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -1140,6 +1140,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
 	ab->hif.ops = hif_ops;
 	ab->pdev = pdev;
 	ab->hw_rev = hw_rev;
+	ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
 	platform_set_drvdata(pdev, ab);
 
 	ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c
index f2da95fd4253..6abcacbcb672 100644
--- a/drivers/net/wireless/ath/ath11k/ce.c
+++ b/drivers/net/wireless/ath/ath11k/ce.c
@@ -627,9 +627,9 @@ ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz)
 	 * coherent DMA are unsupported
 	 */
 	ce_ring->base_addr_owner_space_unaligned =
-		dma_alloc_coherent(ab->dev,
-				   nentries * desc_sz + CE_DESC_RING_ALIGN,
-				   &base_addr, GFP_KERNEL);
+		ath11k_core_dma_alloc_coherent(ab->dev,
+						nentries * desc_sz + CE_DESC_RING_ALIGN,
+						&base_addr, GFP_KERNEL | GFP_DMA32);
 	if (!ce_ring->base_addr_owner_space_unaligned) {
 		kfree(ce_ring);
 		return ERR_PTR(-ENOMEM);
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 893fefadbba9..7e4dafdafb80 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -9,6 +9,7 @@
 #include <linux/remoteproc.h>
 #include <linux/firmware.h>
 #include <linux/of.h>
+#include <linux/dma-direct.h>
 
 #include "core.h"
 #include "dp_tx.h"
@@ -16,6 +17,7 @@
 #include "debug.h"
 #include "hif.h"
 #include "wow.h"
+#include "wmi.h"
 
 unsigned int ath11k_debug_mask;
 EXPORT_SYMBOL(ath11k_debug_mask);
@@ -32,6 +34,10 @@ module_param_named(frame_mode, ath11k_frame_mode, uint, 0644);
 MODULE_PARM_DESC(frame_mode,
 		 "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)");
 
+unsigned int ath11k_ftm_mode;
+module_param_named(ftm_mode, ath11k_ftm_mode, uint, 0444);
+MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
+
 static const struct ath11k_hw_params ath11k_hw_params[] = {
 	{
 		.hw_rev = ATH11K_HW_IPQ8074,
@@ -115,6 +121,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = true,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
+		.coex_isolation = false,
 	},
 	{
 		.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -195,6 +202,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = true,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
+		.coex_isolation = false,
+		.support_fw_mac_sequence = false,
 	},
 	{
 		.name = "qca6390 hw2.0",
@@ -277,6 +286,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = true,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
+		.coex_isolation = false,
+		.support_fw_mac_sequence = true,
 	},
 	{
 		.name = "qcn9074 hw1.0",
@@ -356,6 +367,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = true,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
+		.coex_isolation = false,
+		.support_fw_mac_sequence = false,
 	},
 	{
 		.name = "wcn6855 hw2.0",
@@ -402,6 +415,77 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.idle_ps = true,
 		.supports_sta_ps = true,
 		.cold_boot_calib = 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,
+		.supports_dynamic_smps_6ghz = false,
+		.alloc_cacheable_memory = false,
+		.supports_rssi_stats = true,
+		.fw_wmi_diag_event = true,
+		.current_cc_support = true,
+		.dbr_debug_support = false,
+		.coex_isolation = false,
+                .global_reset = true,
+		.bios_sar_capa = &ath11k_hw_sar_capa_wcn6855,
+		.m3_fw_support = true,
+		.fixed_bdf_addr = false,
+		.fixed_mem_region = false,
+		.static_window_map = false,
+		.hybrid_bus_type = false,
+		.fixed_fw_mem = false,
+		.support_off_channel_tx = true,
+	},
+	{
+		.name = "qca206x hw2.1",
+		.hw_rev = ATH11K_HW_QCA206X_HW21,
+		.fw = {
+			.dir = "QCA206X/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 = ath11k_host_ce_config_qca6390,
+		.ce_count = 9,
+		.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,
+
+		.spectral = {
+			.fft_sz = 0,
+			.fft_pad_sz = 0,
+			.summary_pad_sz = 0,
+			.fft_hdr_len = 0,
+			.max_fft_bins = 0,
+		},
+
+		.interface_modes = BIT(NL80211_IFTYPE_STATION) |
+					BIT(NL80211_IFTYPE_AP),
+		.supports_monitor = false,
+		.full_monitor_mode = false,
+		.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,
@@ -438,6 +522,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = true,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
+		.coex_isolation = true,
+		.support_fw_mac_sequence = true,
 	},
 	{
 		.name = "wcn6855 hw2.1",
@@ -479,6 +565,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.interface_modes = BIT(NL80211_IFTYPE_STATION) |
 					BIT(NL80211_IFTYPE_AP),
 		.supports_monitor = false,
+		.full_monitor_mode = false,
 		.supports_shadow_regs = true,
 		.idle_ps = true,
 		.supports_sta_ps = true,
@@ -519,6 +606,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = true,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
+		.coex_isolation = false,
+		.support_fw_mac_sequence = true,
 	},
 	{
 		.name = "wcn6750 hw1.0",
@@ -597,9 +686,45 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tcl_ring_retry = false,
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750,
 		.smp2p_wow_exit = true,
+		.coex_isolation = false,
+		.support_fw_mac_sequence = true,
 	},
 };
 
+static const struct dma_map_ops *ath11k_core_get_dma_ops(struct device *dev)
+{
+	if (dev->dma_ops)
+		return dev->dma_ops;
+	return NULL;
+}
+
+void *ath11k_core_dma_alloc_coherent(struct device *dev, size_t size,
+				     dma_addr_t *dma_handle, gfp_t flag)
+{
+	unsigned long attrs = (flag & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0;
+	const struct dma_map_ops *ops = ath11k_core_get_dma_ops(dev);
+	void *cpu_addr;
+
+	WARN_ON_ONCE(!dev->coherent_dma_mask);
+
+	/*
+	 * DMA allocations can never be turned back into a page pointer, so
+	 * requesting compound pages doesn't make sense (and can't even be
+	 * supported at all by various backends).
+	 */
+	if (WARN_ON_ONCE(flag & __GFP_COMP))
+		return NULL;
+
+	if (!ops)
+		cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
+	else if (ops->alloc)
+		cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
+	else
+		return NULL;
+
+	return cpu_addr;
+}
+
 static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab)
 {
 	WARN_ON(!ab->hw_params.single_pdev_only);
@@ -1290,6 +1415,11 @@ static int ath11k_core_soc_create(struct ath11k_base *ab)
 {
 	int ret;
 
+	if (ath11k_ftm_mode) {
+		ab->fw_mode = ATH11K_FIRMWARE_MODE_FTM;
+		ath11k_info(ab, "Booting in ftm mode\n");
+	}
+
 	ret = ath11k_qmi_init_service(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to initialize qmi :%d\n", ret);
@@ -1384,6 +1514,30 @@ static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
 	ath11k_debugfs_pdev_destroy(ab);
 }
 
+static int ath11k_core_config_coex_isolation(struct ath11k_base *ab)
+{
+       struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
+       struct wmi_coex_config_params param;
+
+       memset(&param, 0, sizeof(struct wmi_coex_config_params));
+       param.config_type = WMI_COEX_CONFIG_ANTENNA_ISOLATION;
+       param.config_arg1 = WMI_COEX_ISOLATION_ARG1_DEFAUT;
+
+       return ath11k_wmi_send_coex_config(ar, &param);
+}
+
+static int ath11k_core_config_btc_mode(struct ath11k_base *ab)
+{
+	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
+	struct wmi_coex_config_params param;
+
+	memset(&param, 0, sizeof(struct wmi_coex_config_params));
+	param.config_type = WMI_COEX_CONFIG_BTC_MODE;
+	param.config_arg1 = WMI_COEX_BTC_MODE_ARG1_DEFAULT;
+
+	return ath11k_wmi_send_coex_config(ar, &param);
+}
+
 static int ath11k_core_start(struct ath11k_base *ab)
 {
 	int ret;
@@ -1481,6 +1635,22 @@ static int ath11k_core_start(struct ath11k_base *ab)
 		goto err_reo_cleanup;
 	}
 
+	if (ab->hw_params.coex_isolation) {
+		ret = ath11k_core_config_coex_isolation(ab);
+		if (ret) {
+			ath11k_err(ab, "failed to set coex isolation: %d\n",
+					ret);
+			goto err_reo_cleanup;
+		}
+	}
+
+	ret = ath11k_core_config_btc_mode(ab);
+	if (ret) {
+		ath11k_err(ab, "failed to set btc mode: %d\n",
+				ret);
+		goto err_reo_cleanup;
+	}
+
 	return 0;
 
 err_reo_cleanup:
@@ -1516,7 +1686,7 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab)
 {
 	int ret;
 
-	ret = ath11k_core_start_firmware(ab, ATH11K_FIRMWARE_MODE_NORMAL);
+	ret = ath11k_core_start_firmware(ab, ab->fw_mode);
 	if (ret) {
 		ath11k_err(ab, "failed to start firmware: %d\n", ret);
 		return ret;
@@ -1630,6 +1800,7 @@ void ath11k_core_halt(struct ath11k *ar)
 	ath11k_mac_scan_finish(ar);
 	ath11k_mac_peer_cleanup_all(ar);
 	cancel_delayed_work_sync(&ar->scan.timeout);
+	cancel_work_sync(&ar->channel_update_work);
 	cancel_work_sync(&ar->regd_update_work);
 	cancel_work_sync(&ab->update_11d_work);
 
@@ -1668,7 +1839,7 @@ static void ath11k_update_11d(struct work_struct *work)
 	}
 }
 
-static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab)
+void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab)
 {
 	struct ath11k *ar;
 	struct ath11k_pdev *pdev;
@@ -1681,7 +1852,8 @@ static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab)
 	for (i = 0; i < ab->num_radios; i++) {
 		pdev = &ab->pdevs[i];
 		ar = pdev->ar;
-		if (!ar || ar->state == ATH11K_STATE_OFF)
+		if (!ar || ar->state == ATH11K_STATE_OFF ||
+		    ar->state == ATH11K_STATE_TM)
 			continue;
 
 		ieee80211_stop_queues(ar->hw);
@@ -1746,7 +1918,11 @@ static void ath11k_core_post_reconfigure_recovery(struct ath11k_base *ab)
 			ath11k_warn(ab,
 				    "device is wedged, will not restart radio %d\n", i);
 			break;
+		case ATH11K_STATE_TM:
+			ath11k_warn(ab, "fw mode reset done radio %d\n", i);
+			break;
 		}
+
 		mutex_unlock(&ar->conf_mutex);
 	}
 	complete(&ab->driver_recovery);
@@ -1757,9 +1933,6 @@ static void ath11k_core_restart(struct work_struct *work)
 	struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work);
 	int ret;
 
-	if (!ab->is_reset)
-		ath11k_core_pre_reconfigure_recovery(ab);
-
 	ret = ath11k_core_reconfigure_on_crash(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to reconfigure driver on crash recovery\n");
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index bd06536f82a6..0fa7eec855d3 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef ATH11K_CORE_H
@@ -142,6 +142,7 @@ enum ath11k_hw_rev {
 	ATH11K_HW_WCN6855_HW20,
 	ATH11K_HW_WCN6855_HW21,
 	ATH11K_HW_WCN6750_HW10,
+	ATH11K_HW_QCA206X_HW21,
 };
 
 enum ath11k_firmware_mode {
@@ -269,6 +270,7 @@ enum ath11k_dev_flags {
 	ATH11K_FLAG_FIXED_MEM_RGN,
 	ATH11K_FLAG_DEVICE_INIT_DONE,
 	ATH11K_FLAG_MULTI_MSI_VECTORS,
+	ATH11K_FLAG_FTM_SEGMENTED,
 };
 
 enum ath11k_monitor_flags {
@@ -301,6 +303,43 @@ struct ath11k_rekey_data {
 	bool enable_offload;
 };
 
+/**
+ * struct chan_power_info - TPE containing power info per channel chunk
+ * @chan_cfreq: channel center freq (MHz)
+ * e.g.
+ * channel 37/20 MHz,  it is 6135
+ * channel 37/40 MHz,  it is 6125
+ * channel 37/80 MHz,  it is 6145
+ * channel 37/160 MHz, it is 6185
+ * @tx_power: transmit power (dBm)
+ */
+struct chan_power_info {
+	u16 chan_cfreq;
+	s8 tx_power;
+};
+
+/**
+ * struct reg_tpc_power_info - regulatory TPC power info
+ * @is_psd_power: is PSD power or not
+ * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD
+ * @power_type_6g: type of power (SP/LPI/VLP)
+ * @num_pwr_levels: number of power levels
+ * @reg_max: Array of maximum TX power (dBm) per PSD value
+ * @ap_constraint_power: AP constraint power (dBm)
+ * @tpe: TPE values processed from TPE IE
+ * @chan_power_info: power info to send to firmware
+ */
+struct ath11k_reg_tpc_power_info {
+	bool is_psd_power;
+	u8 eirp_power;
+	enum wmi_reg_6ghz_ap_type power_type_6g;
+	u8 num_pwr_levels;
+	u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL];
+	u8 ap_constraint_power;
+	s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL];
+	struct chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL];
+};
+
 struct ath11k_vif {
 	u32 vdev_id;
 	enum wmi_vdev_type vdev_type;
@@ -358,6 +397,7 @@ struct ath11k_vif {
 #ifdef CONFIG_ATH11K_DEBUGFS
 	struct dentry *debugfs_twt;
 #endif /* CONFIG_ATH11K_DEBUGFS */
+	struct ath11k_reg_tpc_power_info reg_tpc_info;
 };
 
 struct ath11k_vif_iter {
@@ -521,6 +561,7 @@ enum ath11k_state {
 	ATH11K_STATE_RESTARTING,
 	ATH11K_STATE_RESTARTED,
 	ATH11K_STATE_WEDGED,
+	ATH11K_STATE_TM,
 	/* Add other states as required */
 };
 
@@ -531,6 +572,12 @@ enum ath11k_state {
 
 #define ATH11K_INVALID_RSSI_EMPTY -128
 
+struct ath11k_ftm_event_obj {
+	u32 data_pos;
+	u32 expected_seq;
+	u8 *eventdata;
+};
+
 struct ath11k_fw_stats {
 	struct dentry *debugfs_fwstats;
 	u32 pdev_id;
@@ -685,6 +732,10 @@ struct ath11k {
 	struct completion bss_survey_done;
 
 	struct work_struct regd_update_work;
+	struct work_struct channel_update_work;
+	struct list_head channel_update_queue;
+	/* protects channel_update_queue data */
+	spinlock_t channel_update_lock;
 
 	struct work_struct wmi_mgmt_tx_work;
 	struct sk_buff_head wmi_mgmt_tx_queue;
@@ -700,6 +751,8 @@ struct ath11k {
 	u32 last_ppdu_id;
 	u32 cached_ppdu_id;
 	int monitor_vdev_id;
+	struct completion fw_mode_reset;
+	u8 ftm_msgref;
 #ifdef CONFIG_ATH11K_DEBUGFS
 	struct ath11k_debug debug;
 #endif
@@ -723,6 +776,7 @@ struct ath11k {
 	/* protected by conf_mutex */
 	bool ps_state_enable;
 	bool ps_timekeeper_enable;
+	s8 max_allowed_tx_power;
 };
 
 struct ath11k_band_cap {
@@ -826,9 +880,20 @@ struct ath11k_msi_config {
 	u16 hw_rev;
 };
 
+struct fw_remote_mem {
+	size_t size;
+	void *vaddr;
+};
+
+struct fw_remote_crash_data {
+	u8 *remote_buf;
+	size_t remote_buf_len;
+};
+
 /* Master structure to hold the hw data which may be used in core module */
 struct ath11k_base {
 	enum ath11k_hw_rev hw_rev;
+	enum ath11k_firmware_mode fw_mode;
 	struct platform_device *pdev;
 	struct device *dev;
 	struct ath11k_qmi qmi;
@@ -907,6 +972,7 @@ struct ath11k_base {
 	 * This may or may not be used during the runtime
 	 */
 	struct ieee80211_regdomain *new_regd[MAX_RADIOS];
+	struct cur_regulatory_info *reg_info_store;
 
 	/* Current DFS Regulatory */
 	enum ath11k_dfs_region dfs_region;
@@ -938,6 +1004,7 @@ struct ath11k_base {
 		u32 fw_crash_counter;
 	} stats;
 	u32 pktlog_defs_checksum;
+	struct ath11k_ftm_event_obj ftm_event_obj;
 
 	struct ath11k_dbring_cap *db_caps;
 	u32 num_db_cap;
@@ -968,6 +1035,9 @@ struct ath11k_base {
 		const struct ath11k_pci_ops *ops;
 	} pci;
 
+	struct fw_remote_mem remote_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
+	struct fw_remote_crash_data remote_crash_data;
+
 	/* must be last */
 	u8 drv_priv[] __aligned(sizeof(void *));
 };
@@ -1158,6 +1228,7 @@ int ath11k_core_check_smbios(struct ath11k_base *ab);
 void ath11k_core_halt(struct ath11k *ar);
 int ath11k_core_resume(struct ath11k_base *ab);
 int ath11k_core_suspend(struct ath11k_base *ab);
+void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab);
 
 const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab,
 						    const char *filename);
@@ -1222,4 +1293,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus)
 	return "unknown";
 }
 
+void *ath11k_core_dma_alloc_coherent(struct device *dev, size_t size,
+				     dma_addr_t *dma_handle, gfp_t flag);
 #endif /* _CORE_H_ */
diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c
new file mode 100644
index 000000000000..32d37dda570c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/coredump.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/devcoredump.h>
+#include "coredump.h"
+#include "pci.h"
+#include "debug.h"
+
+static struct ath11k_dump_file_data *
+ath11k_coredump_build(struct ath11k_mhi_fw_crash_data *crash_data,
+		      struct fw_remote_crash_data *remote_crash_data,
+		      struct register_crash_data *reg_crash_data)
+{
+	struct ath11k_dump_file_data *dump_data;
+	struct ath11k_tlv_dump_data *dump_tlv;
+	size_t hdr_len = sizeof(*dump_data);
+	size_t len, sofar = 0;
+	unsigned char *buf;
+	struct timespec64 timestamp;
+
+	len = hdr_len;
+
+	len += sizeof(*dump_tlv) + crash_data->paging_dump_buf_len;
+	len += sizeof(*dump_tlv) + crash_data->ramdump_buf_len;
+	len += sizeof(*dump_tlv) + remote_crash_data->remote_buf_len;
+	len += sizeof(*dump_tlv) + reg_crash_data->reg_buf_len;
+	len += sizeof(*dump_tlv) + reg_crash_data->reg_rddm_buf_len;
+	sofar += hdr_len;
+
+	buf = vzalloc(len);
+	if (!buf)
+		return NULL;
+
+	dump_data = (struct ath11k_dump_file_data *)(buf);
+	strlcpy(dump_data->df_magic, "ATH11K-FW-DUMP",
+		sizeof(dump_data->df_magic));
+	dump_data->len = cpu_to_le32(len);
+	dump_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_VERSION);
+	guid_gen(&dump_data->guid);
+	ktime_get_real_ts64(&timestamp);
+	dump_data->tv_sec = cpu_to_le64(timestamp.tv_sec);
+	dump_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec);
+
+	/* Gather FW paging dump */
+	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
+	dump_tlv->type = cpu_to_le32(ATH11K_FW_CRASH_PAGING_DATA);
+	dump_tlv->tlv_len = cpu_to_le32(crash_data->paging_dump_buf_len);
+	memcpy(dump_tlv->tlv_data, crash_data->paging_dump_buf,
+	       crash_data->paging_dump_buf_len);
+	sofar += sizeof(*dump_tlv) + crash_data->paging_dump_buf_len;
+
+	/* Gather RDDM dump */
+	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
+	dump_tlv->type = cpu_to_le32(ATH11K_FW_CRASH_RDDM_DATA);
+	dump_tlv->tlv_len = cpu_to_le32(crash_data->ramdump_buf_len);
+	memcpy(dump_tlv->tlv_data, crash_data->ramdump_buf,
+	       crash_data->ramdump_buf_len);
+	sofar += sizeof(*dump_tlv) + crash_data->ramdump_buf_len;
+
+	/* gather remote memory */
+	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
+	dump_tlv->type = cpu_to_le32(ATH11K_FW_REMOTE_MEM_DATA);
+	dump_tlv->tlv_len = cpu_to_le32(remote_crash_data->remote_buf_len);
+	memcpy(dump_tlv->tlv_data, remote_crash_data->remote_buf,
+	       remote_crash_data->remote_buf_len);
+	sofar += sizeof(*dump_tlv) + remote_crash_data->remote_buf_len;
+
+	/* gather register memory */
+	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
+	dump_tlv->type = cpu_to_le32(ATH11K_FW_REGISTER_DATA);
+	dump_tlv->tlv_len = cpu_to_le32(reg_crash_data->reg_buf_len);
+	memcpy(dump_tlv->tlv_data, reg_crash_data->reg_buf,
+	       reg_crash_data->reg_buf_len);
+	sofar += sizeof(*dump_tlv) + reg_crash_data->reg_buf_len;
+
+	/* gather register for rddm fail */
+	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
+	dump_tlv->type = cpu_to_le32(ATH11K_FW_REGISTER_RDDM_DATA);
+	dump_tlv->tlv_len = cpu_to_le32(reg_crash_data->reg_rddm_buf_len);
+	memcpy(dump_tlv->tlv_data, reg_crash_data->reg_rddm_buf,
+	       reg_crash_data->reg_rddm_buf_len);
+	sofar += sizeof(*dump_tlv) + reg_crash_data->reg_rddm_buf_len;
+
+	return dump_data;
+}
+
+static int ath11k_coredump_submit(struct ath11k_pci *ab_pci)
+{
+	struct ath11k_dump_file_data *dump;
+
+	dump = ath11k_coredump_build(&ab_pci->mhi_fw_crash_data,
+				     &ab_pci->ab->remote_crash_data,
+				     &ab_pci->reg_data);
+	if (!dump)
+		return -ENODATA;
+
+	dev_coredumpv(ab_pci->mhi_ctrl->cntrl_dev, dump,
+		      le32_to_cpu(dump->len), GFP_KERNEL);
+
+	return 0;
+}
+
+static void ath11k_coredump_buf_release(struct ath11k_pci *ab_pci)
+{
+	struct fw_remote_crash_data *remote = &ab_pci->ab->remote_crash_data;
+	struct ath11k_mhi_fw_crash_data *mhi = &ab_pci->mhi_fw_crash_data;
+	struct register_crash_data *reg = &ab_pci->reg_data;
+
+	if (remote->remote_buf) {
+		vfree(remote->remote_buf);
+		remote->remote_buf = NULL;
+	}
+
+	if (mhi->ramdump_buf) {
+		vfree(mhi->ramdump_buf);
+		mhi->ramdump_buf = NULL;
+	}
+
+	if (mhi->paging_dump_buf) {
+		vfree(mhi->paging_dump_buf);
+		mhi->paging_dump_buf = NULL;
+	}
+
+	if (reg->reg_buf) {
+		vfree(reg->reg_buf);
+		reg->reg_buf = NULL;
+	}
+
+	if (reg->reg_rddm_buf) {
+		vfree(reg->reg_rddm_buf);
+		reg->reg_rddm_buf = NULL;
+	}
+}
+
+void ath11k_mhi_pm_rddm_worker(struct work_struct *work)
+{
+	struct ath11k_pci *ab_pci = container_of(work,
+						 struct ath11k_pci,
+						 rddm_worker);
+	struct ath11k_base *ab = ab_pci->ab;
+
+	mhi_download_rddm_image(ab_pci->mhi_ctrl, false);
+
+	ath11k_coredump_fw_rddm_dump(ab_pci, ab_pci->mhi_ctrl);
+	ath11k_coredump_fw_paging_dump(ab_pci, ab_pci->mhi_ctrl);
+	ath11k_qmi_remote_dump(ab_pci->ab);
+	ath11k_pci_register_dump(ab_pci);
+
+	ath11k_coredump_submit(ab_pci);
+	ath11k_coredump_buf_release(ab_pci);
+
+	if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))
+		return;
+
+	ath11k_info(ab, "start to reset for rddm\n");
+	queue_work(ab->workqueue_aux, &ab->reset_work);
+}
diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h
new file mode 100644
index 000000000000..d8cae2e5d45f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/coredump.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _COREDUMP_H_
+#define _COREDUMP_H_
+
+#define ATH11K_FW_CRASH_DUMP_VERSION 1
+
+enum ath11k_fw_crash_dump_type {
+	ATH11K_FW_CRASH_PAGING_DATA,
+	ATH11K_FW_CRASH_RDDM_DATA,
+	ATH11K_FW_REMOTE_MEM_DATA,
+	ATH11K_FW_REGISTER_DATA,
+	ATH11K_FW_REGISTER_RDDM_DATA,
+	ATH11K_FW_CRASH_DUMP_MAX,
+};
+
+struct ath11k_tlv_dump_data {
+	/* see ath11k_fw_crash_dump_type above */
+	__le32 type;
+	/* in bytes */
+	__le32 tlv_len;
+	/* pad to 32-bit boundaries as needed */
+	u8 tlv_data[];
+} __packed;
+
+struct ath11k_dump_file_data {
+	/* "ATH11K-FW-DUMP" */
+	char df_magic[16];
+	__le32 len;
+	/* file dump version */
+	__le32 version;
+	guid_t guid;
+	/* time-of-day stamp */
+	__le64 tv_sec;
+	/* time-of-day stamp, nano-seconds */
+	__le64 tv_nsec;
+	/* room for growth w/out changing binary format */
+	u8 unused[8];
+	/* struct ath11k_tlv_dump_data + more */
+	u8 data[0];
+} __packed;
+
+void ath11k_mhi_pm_rddm_worker(struct work_struct *work);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath11k/coredump_fw.c b/drivers/net/wireless/ath/ath11k/coredump_fw.c
new file mode 100644
index 000000000000..73ae511b6ce7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/coredump_fw.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include "coredump_fw.h"
+#include "pci.h"
+#include "debug.h"
+
+static size_t ath11k_get_paging_buf_len(struct mhi_controller *mhi_cntrl)
+{
+	struct image_info *img = mhi_cntrl->fbc_image;
+	int i = 0;
+	size_t len = 0;
+	u32 entries = img->entries;
+	size_t seg_size = mhi_cntrl->seg_len;
+
+	for (i = 0; i < entries; ++i) {
+		size_t vec_size = seg_size;
+
+		if (i == entries - 1)
+			vec_size = sizeof(struct ath11k_vec_entry) * i;
+		len += vec_size;
+	}
+
+	return len;
+}
+
+int ath11k_coredump_fw_paging_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl)
+{
+	struct image_info *img = mhi_cntrl->fbc_image;
+	struct ath11k_mhi_fw_crash_data *crash_data = &ab_pci->mhi_fw_crash_data;
+	char *buf = NULL;
+	unsigned int size = 0;
+	int seg = 0;
+	u32 offset = 0;
+	u32 fw_vec_entry_num = img->entries - 1;
+	size_t paging_dump_buf_len = ath11k_get_paging_buf_len(mhi_cntrl);
+
+	crash_data->paging_dump_buf_len = paging_dump_buf_len;
+	dev_info(&mhi_cntrl->mhi_dev->dev,
+		 "%s FW paging dump buffer len=%lu\n",
+		 __func__, paging_dump_buf_len);
+	crash_data->paging_dump_buf = vzalloc(paging_dump_buf_len);
+	if (!crash_data->paging_dump_buf)
+		return -ENOMEM;
+
+	for (seg = 0; seg < fw_vec_entry_num; seg++) {
+		buf = img->mhi_buf[seg].buf;
+		size = img->mhi_buf[seg].len;
+		memcpy(crash_data->paging_dump_buf + offset, buf, size);
+		offset += size;
+	}
+
+	buf = crash_data->paging_dump_buf + offset;
+	size = img->mhi_buf[img->entries - 1].len;
+	ath11k_info(ab_pci->ab, "to write last block: mem: 0x%p, size: 0x%x\n",
+		    buf, size);
+	memcpy(buf, img->mhi_buf[img->entries - 1].buf, size);
+
+	return 0;
+}
+
+int ath11k_coredump_fw_rddm_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl)
+{
+	struct ath11k_mhi_fw_crash_data *crash_data = &ab_pci->mhi_fw_crash_data;
+	struct image_info *img = mhi_cntrl->rddm_image;
+	char *buf = NULL;
+	unsigned int size = 0;
+	int seg = 0;
+	u32 offset = 0;
+	u32 rddm_vec_entry_num;
+	u32 entries = mhi_cntrl->rddm_image->entries;
+
+	rddm_vec_entry_num = DIV_ROUND_UP(RDDM_DUMP_SIZE,
+					  mhi_cntrl->seg_len);
+	crash_data->ramdump_buf_len = (entries - 1) * mhi_cntrl->seg_len +
+				      rddm_vec_entry_num * sizeof(struct ath11k_vec_entry);
+
+	ath11k_info(ab_pci->ab, "rddm_vec_entry_num=%d entries=%d\n",
+		    rddm_vec_entry_num, img->entries);
+
+	crash_data->ramdump_buf = vzalloc(crash_data->ramdump_buf_len);
+	if (!crash_data->ramdump_buf)
+		return -ENOMEM;
+
+	for (seg = 0; seg < rddm_vec_entry_num; seg++) {
+		buf = img->mhi_buf[seg].buf;
+		size = img->mhi_buf[seg].len;
+		ath11k_info(ab_pci->ab,
+			    "write rddm memory: mem: 0x%p, size: 0x%x\n",
+			    buf, size);
+		memcpy(crash_data->ramdump_buf + offset, buf, size);
+		offset += size;
+	}
+
+	buf = crash_data->ramdump_buf + offset;
+	size = img->mhi_buf[img->entries - 1].len;
+	ath11k_info(ab_pci->ab,
+		    "to write vector table block: mem: 0x%p, size: 0x%x\n",
+		    buf, size);
+	memcpy(buf, img->mhi_buf[img->entries - 1].buf, size);
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/ath11k/coredump_fw.h b/drivers/net/wireless/ath/ath11k/coredump_fw.h
new file mode 100644
index 000000000000..e254ceec7d93
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/coredump_fw.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _COREDUMP_FW_H_
+#define _COREDUMP_FW_H_
+
+#include <linux/mhi.h>
+
+struct ath11k_pci;
+
+#define RDDM_DUMP_SIZE				0x420000
+
+struct ath11k_vec_entry {
+	u64 dma_addr;
+	u64 size;
+};
+
+struct ath11k_mhi_fw_crash_data {
+	u8 *paging_dump_buf;
+	size_t paging_dump_buf_len;
+	u8 *ramdump_buf;
+	size_t ramdump_buf_len;
+};
+
+int ath11k_coredump_fw_paging_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl);
+int ath11k_coredump_fw_rddm_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h
index 91545640c47b..2553cf902093 100644
--- a/drivers/net/wireless/ath/ath11k/debug.h
+++ b/drivers/net/wireless/ath/ath11k/debug.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _ATH11K_DEBUG_H_
@@ -33,6 +34,7 @@ __printf(2, 3) void ath11k_err(struct ath11k_base *ab, const char *fmt, ...);
 __printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...);
 
 extern unsigned int ath11k_debug_mask;
+extern unsigned int ath11k_ftm_mode;
 
 #ifdef CONFIG_ATH11K_DEBUG
 __printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab,
diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c
index 5bb6fd17fdf6..7fad6e447229 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs.c
@@ -468,6 +468,78 @@ static const struct file_operations fops_bcn_stats = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath11k_debugfs_write_coex_config(struct file *file,
+		const char __user *user_buf,
+		size_t count, loff_t *ppos)
+{
+	struct ath11k_base *ab = file->private_data;
+	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
+	char buf[64] = {0};
+	ssize_t ret;
+	int rc;
+	u32 config_type, config_arg1;
+	char sep[] = " ";
+	char *token, *cur;
+	struct wmi_coex_config_params param;
+
+	if (*ppos != 0 || count >= sizeof(buf) || count == 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (ret < 0)
+		goto out;
+
+	/* drop the possible '\n' from the end */
+	if (buf[*ppos - 1] == '\n')
+		buf[*ppos - 1] = '\0';
+
+	ath11k_info(ab, "%s: %s\n", __func__, buf);
+	ret = count;
+	cur = buf;
+
+	token = strsep(&cur, sep);
+	rc = kstrtou32(token, 0, &config_type);
+	if (rc) {
+		ath11k_warn(ab, "%s convert error: config_type %s\n", __func__, token);
+		ret = -EFAULT;
+		goto out;
+	}
+
+	token = strim(cur);
+	rc = kstrtou32(token, 0, &config_arg1);
+	if (rc) {
+		ath11k_warn(ab, "%s convert error: config_arg1 %s\n", __func__, token);
+		ret = -EFAULT;
+		goto out;
+	}
+
+
+	memset(&param, 0, sizeof(struct wmi_coex_config_params));
+	param.config_type = config_type;
+	param.config_arg1 = config_arg1;
+
+	mutex_lock(&ar->conf_mutex);
+
+	rc = ath11k_mac_send_coex_config(ar, &param);
+	if (rc) {
+		ath11k_warn(ab, "failed to send coex config using debugfs %d\n", rc);
+		ret = -EFAULT;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+out:
+	return ret;
+}
+
+static const struct file_operations fops_coex_config = {
+	.write = ath11k_debugfs_write_coex_config,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static ssize_t ath11k_read_simulate_fw_crash(struct file *file,
 					     char __user *user_buf,
 					     size_t count, loff_t *ppos)
@@ -532,6 +604,10 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
 		ath11k_info(ab, "user requested hw restart\n");
 		queue_work(ab->workqueue_aux, &ab->reset_work);
 		ret = 0;
+	} else if (!strcmp(buf, "mhi-rddm")) {
+		ath11k_info(ab, "force target rddm\n");
+		ath11k_hif_force_rddm(ab);
+		ret = 0;
 	} else {
 		ret = -EINVAL;
 		goto exit;
@@ -957,9 +1033,12 @@ static ssize_t ath11k_read_sram_dump(struct file *file,
 
 static int ath11k_release_sram_dump(struct inode *inode, struct file *file)
 {
+	struct ath11k_base *ab = inode->i_private;
 	vfree(file->private_data);
 	file->private_data = NULL;
 
+	debugfs_create_file("coex_config", 0600, ab->debugfs_soc, ab,
+			    &fops_coex_config);
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index d070bcb3fe24..6b6d0ce4f790 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -254,9 +254,9 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
 	}
 
 	if (!cached)
-		ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
+		ring->vaddr_unaligned = ath11k_core_dma_alloc_coherent(ab->dev, ring->size,
 							   &ring->paddr_unaligned,
-							   GFP_KERNEL);
+							   GFP_KERNEL | GFP_DMA32);
 
 	if (!ring->vaddr_unaligned)
 		return -ENOMEM;
@@ -527,9 +527,9 @@ static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab,
 		return -EINVAL;
 
 	for (i = 0; i < num_scatter_buf; i++) {
-		slist[i].vaddr = dma_alloc_coherent(ab->dev,
+		slist[i].vaddr = ath11k_core_dma_alloc_coherent(ab->dev,
 						    HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX,
-						    &slist[i].paddr, GFP_KERNEL);
+						    &slist[i].paddr, GFP_KERNEL | GFP_DMA32);
 		if (!slist[i].vaddr) {
 			ret = -ENOMEM;
 			goto err;
@@ -607,9 +607,9 @@ static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab,
 			desc_sz = last_bank_sz;
 
 		desc_bank[i].vaddr_unaligned =
-					dma_alloc_coherent(ab->dev, desc_sz,
+					ath11k_core_dma_alloc_coherent(ab->dev, desc_sz,
 							   &desc_bank[i].paddr_unaligned,
-							   GFP_KERNEL);
+							   GFP_KERNEL | GFP_DMA32);
 		if (!desc_bank[i].vaddr_unaligned) {
 			ret = -ENOMEM;
 			goto err;
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 38be646bc021..9e4ab2ae7912 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -691,13 +691,18 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab)
 	struct ath11k_dp *dp = &ab->dp;
 	struct dp_reo_cmd *cmd, *tmp;
 	struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache;
+	struct dp_rx_tid *rx_tid;
 
 	spin_lock_bh(&dp->reo_cmd_lock);
 	list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) {
 		list_del(&cmd->list);
-		dma_unmap_single(ab->dev, cmd->data.paddr,
-				 cmd->data.size, DMA_BIDIRECTIONAL);
-		kfree(cmd->data.vaddr);
+		rx_tid = &cmd->data;
+		if (rx_tid->vaddr) {
+			dma_unmap_single(ab->dev, rx_tid->paddr,
+					 rx_tid->size, DMA_BIDIRECTIONAL);
+			kfree(rx_tid->vaddr);
+			rx_tid->vaddr = NULL;
+		}
 		kfree(cmd);
 	}
 
@@ -705,9 +710,13 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab)
 				 &dp->reo_cmd_cache_flush_list, list) {
 		list_del(&cmd_cache->list);
 		dp->reo_cmd_cache_flush_count--;
-		dma_unmap_single(ab->dev, cmd_cache->data.paddr,
-				 cmd_cache->data.size, DMA_BIDIRECTIONAL);
-		kfree(cmd_cache->data.vaddr);
+		rx_tid = &cmd_cache->data;
+		if (rx_tid->vaddr) {
+			dma_unmap_single(ab->dev, rx_tid->paddr,
+					 rx_tid->size, DMA_BIDIRECTIONAL);
+			kfree(rx_tid->vaddr);
+			rx_tid->vaddr = NULL;
+		}
 		kfree(cmd_cache);
 	}
 	spin_unlock_bh(&dp->reo_cmd_lock);
@@ -721,10 +730,12 @@ static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx,
 	if (status != HAL_REO_CMD_SUCCESS)
 		ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n",
 			    rx_tid->tid, status);
-
-	dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size,
-			 DMA_BIDIRECTIONAL);
-	kfree(rx_tid->vaddr);
+	if (rx_tid->vaddr) {
+		dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size,
+				 DMA_BIDIRECTIONAL);
+		kfree(rx_tid->vaddr);
+		rx_tid->vaddr = NULL;
+	}
 }
 
 static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab,
@@ -763,6 +774,7 @@ static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab,
 		dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
 				 DMA_BIDIRECTIONAL);
 		kfree(rx_tid->vaddr);
+		rx_tid->vaddr = NULL;
 	}
 }
 
@@ -815,6 +827,7 @@ free_desc:
 	dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
 			 DMA_BIDIRECTIONAL);
 	kfree(rx_tid->vaddr);
+	rx_tid->vaddr = NULL;
 }
 
 void ath11k_peer_rx_tid_delete(struct ath11k *ar,
@@ -827,6 +840,8 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar,
 	if (!rx_tid->active)
 		return;
 
+	rx_tid->active = false;
+
 	cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
 	cmd.addr_lo = lower_32_bits(rx_tid->paddr);
 	cmd.addr_hi = upper_32_bits(rx_tid->paddr);
@@ -841,9 +856,11 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar,
 		dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size,
 				 DMA_BIDIRECTIONAL);
 		kfree(rx_tid->vaddr);
+		rx_tid->vaddr = NULL;
 	}
 
-	rx_tid->active = false;
+	rx_tid->paddr = 0;
+	rx_tid->size = 0;
 }
 
 static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab,
@@ -990,6 +1007,7 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab,
 	dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
 			 DMA_BIDIRECTIONAL);
 	kfree(rx_tid->vaddr);
+	rx_tid->vaddr = NULL;
 
 	rx_tid->active = false;
 
@@ -1090,7 +1108,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
 	return ret;
 
 err_mem_free:
-	kfree(vaddr);
+	kfree(rx_tid->vaddr);
+	rx_tid->vaddr = NULL;
 
 	return ret;
 }
@@ -3609,7 +3628,7 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar,
 		goto out_unlock;
 	}
 
-	if (frag_no > __fls(rx_tid->rx_frag_bitmap))
+	if (!rx_tid->rx_frag_bitmap || (frag_no > __fls(rx_tid->rx_frag_bitmap)))
 		__skb_queue_tail(&rx_tid->rx_frags, msdu);
 	else
 		ath11k_dp_rx_h_sort_frags(ar, &rx_tid->rx_frags, msdu);
diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c
index 2fd224480d45..3f9f5cfe9d7c 100644
--- a/drivers/net/wireless/ath/ath11k/hal.c
+++ b/drivers/net/wireless/ath/ath11k/hal.c
@@ -8,6 +8,7 @@
 #include "debug.h"
 #include "hal_desc.h"
 #include "hif.h"
+#include "core.h"
 
 static const struct hal_srng_config hw_srng_config_template[] = {
 	/* TODO: max_rings can populated by querying HW capabilities */
@@ -196,8 +197,8 @@ static int ath11k_hal_alloc_cont_rdp(struct ath11k_base *ab)
 	size_t size;
 
 	size = sizeof(u32) * HAL_SRNG_RING_ID_MAX;
-	hal->rdp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr,
-					    GFP_KERNEL);
+	hal->rdp.vaddr = ath11k_core_dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr,
+					    GFP_KERNEL | GFP_DMA32);
 	if (!hal->rdp.vaddr)
 		return -ENOMEM;
 
@@ -224,8 +225,8 @@ static int ath11k_hal_alloc_cont_wrp(struct ath11k_base *ab)
 	size_t size;
 
 	size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS;
-	hal->wrp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr,
-					    GFP_KERNEL);
+	hal->wrp.vaddr = ath11k_core_dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr,
+					    GFP_KERNEL | GFP_DMA32);
 	if (!hal->wrp.vaddr)
 		return -ENOMEM;
 
diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h
index 659b80d2abd4..7eca43a91a42 100644
--- a/drivers/net/wireless/ath/ath11k/hif.h
+++ b/drivers/net/wireless/ath/ath11k/hif.h
@@ -30,6 +30,7 @@ struct ath11k_hif_ops {
 	void (*ce_irq_enable)(struct ath11k_base *ab);
 	void (*ce_irq_disable)(struct ath11k_base *ab);
 	void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
+	int (*target_crash)(struct ath11k_base *ab);
 };
 
 static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)
@@ -90,6 +91,13 @@ static inline int ath11k_hif_resume(struct ath11k_base *ab)
 	return 0;
 }
 
+static inline int ath11k_hif_force_rddm(struct ath11k_base *ab)
+{
+	if (ab->hif.ops->target_crash)
+		return ab->hif.ops->target_crash(ab);
+	return 0;
+}
+
 static inline u32 ath11k_hif_read32(struct ath11k_base *sc, u32 address)
 {
 	return sc->hif.ops->read32(sc, address);
diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
index dbcc0c4035b6..3b764d501416 100644
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -100,6 +100,7 @@ static void ath11k_init_wmi_config_qca6390(struct ath11k_base *ab,
 	config->num_wow_filters = 0x16;
 	config->num_keep_alive_pattern = 0;
 	config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64;
+	config->host_service_flags |= WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT;
 }
 
 static void ath11k_hw_ipq8074_reo_setup(struct ath11k_base *ab)
@@ -844,6 +845,18 @@ static u32 ath11k_hw_wcn6750_get_tcl_ring_selector(struct sk_buff *skb)
 	return skb_get_hash(skb);
 }
 
+bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar)
+{
+	return (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT,
+			 ar->ab->wmi_ab.svc_map)) && ar->supports_6ghz;
+}
+
+bool ath11k_hw_supports_tpc_ext(struct ath11k *ar)
+{
+	return ath11k_hw_supports_6g_cc_ext(ar) &&
+	       test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map);
+}
+
 const struct ath11k_hw_ops ipq8074_ops = {
 	.get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id,
 	.wmi_init_config = ath11k_init_wmi_config_ipq8074,
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index 8a3f24862edc..95a833382ebb 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -219,6 +219,12 @@ struct ath11k_hw_params {
 	bool tcl_ring_retry;
 	u32 tx_ring_size;
 	bool smp2p_wow_exit;
+	bool wakeup_mhi;
+        u32 rfkill_pin;
+        u32 rfkill_cfg;
+        u32 rfkill_on_level;
+	bool coex_isolation;
+	bool support_fw_mac_sequence;
 };
 
 struct ath11k_hw_ops {
@@ -308,6 +314,9 @@ static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw,
 	return 0;
 }
 
+bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar);
+bool ath11k_hw_supports_tpc_ext(struct ath11k *ar);
+
 struct ath11k_fw_ie {
 	__le32 id;
 	__le32 len;
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index cb77dd6ce966..e7b3b0dfe8f3 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <net/mac80211.h>
@@ -23,6 +23,7 @@
 #include "debugfs_sta.h"
 #include "hif.h"
 #include "wow.h"
+#include "unitest.h"
 
 #define CHAN2G(_channel, _freq, _flags) { \
 	.band                   = NL80211_BAND_2GHZ, \
@@ -250,6 +251,10 @@ static const u32 ath11k_smps_map[] = {
 	[WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
 };
 
+static struct wiphy_vendor_command ath11k_vendor_cmds[] = {
+	ath11k_unit_test_command,
+};
+
 static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
 				   struct ieee80211_vif *vif);
 
@@ -625,6 +630,17 @@ struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id)
 	return NULL;
 }
 
+enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar)
+{
+	struct ath11k_vif *arvif;
+
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		return arvif->vdev_type;
+	}
+
+	return WMI_VDEV_TYPE_UNSPEC;
+}
+
 struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id)
 {
 	int i;
@@ -639,7 +655,10 @@ struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id)
 		return NULL;
 
 	for (i = 0; i < ab->num_radios; i++) {
-		pdev = rcu_dereference(ab->pdevs_active[i]);
+		if (ab->fw_mode == ATH11K_FIRMWARE_MODE_FTM)
+			pdev = &ab->pdevs[i];
+		else
+			pdev = rcu_dereference(ab->pdevs_active[i]);
 
 		if (pdev && pdev->pdev_id == pdev_id)
 			return (pdev->ar ? pdev->ar : NULL);
@@ -2695,6 +2714,138 @@ static int ath11k_setup_peer_smps(struct ath11k *ar, struct ath11k_vif *arvif,
 					 ath11k_smps_map[smps]);
 }
 
+static bool ath11k_mac_set_he_txbf_conf(struct ath11k_vif *arvif)
+{
+	struct ath11k *ar = arvif->ar;
+	u32 param, value;
+	int ret;
+
+	if (!arvif->vif->bss_conf.he_support)
+		return true;
+
+	param = WMI_VDEV_PARAM_SET_HEMU_MODE;
+	value = 0;
+	if (arvif->vif->bss_conf.he_su_beamformer) {
+		value |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE);
+		if (arvif->vif->bss_conf.he_mu_beamformer &&
+		    arvif->vdev_type == WMI_VDEV_TYPE_AP)
+			value |= FIELD_PREP(HE_MODE_MU_TX_BFER, HE_MU_BFER_ENABLE);
+	}
+
+	if (arvif->vif->type != NL80211_IFTYPE_MESH_POINT) {
+		value |= FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) |
+			 FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE);
+
+		if (arvif->vif->bss_conf.he_full_ul_mumimo)
+			value |= FIELD_PREP(HE_MODE_UL_MUMIMO, HE_UL_MUMIMO_ENABLE);
+
+		if (arvif->vif->bss_conf.he_su_beamformee)
+			value |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE);
+	}
+
+	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, value);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to set vdev %d HE MU mode: %d\n",
+			    arvif->vdev_id, ret);
+		return false;
+	}
+
+	param = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE;
+	value =	FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) |
+		FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE,
+			   HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE);
+	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+					    param, value);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to set vdev %d sounding mode: %d\n",
+			    arvif->vdev_id, ret);
+		return false;
+	}
+	return true;
+}
+
+static bool ath11k_mac_vif_recalc_sta_he_txbf(struct ath11k *ar,
+					      struct ieee80211_vif *vif,
+					      struct ieee80211_sta_he_cap *he_cap)
+{
+	struct ath11k_vif *arvif = (void *)vif->drv_priv;
+	struct ieee80211_he_cap_elem he_cap_elem = {0};
+	struct ieee80211_sta_he_cap *cap_band = NULL;
+	struct cfg80211_chan_def def;
+	u32 param = WMI_VDEV_PARAM_SET_HEMU_MODE;
+	u32 hemode = 0;
+	int ret;
+
+	if (!vif->bss_conf.he_support)
+		return true;
+
+	if (vif->type != NL80211_IFTYPE_STATION)
+		return false;
+
+	if (WARN_ON(ath11k_mac_vif_chan(vif, &def)))
+		return false;
+
+	if (def.chan->band == NL80211_BAND_2GHZ)
+		cap_band = &ar->mac.iftype[NL80211_BAND_2GHZ][vif->type].he_cap;
+	else
+		cap_band = &ar->mac.iftype[NL80211_BAND_5GHZ][vif->type].he_cap;
+
+	memcpy(&he_cap_elem, &cap_band->he_cap_elem, sizeof(he_cap_elem));
+
+	if (HECAP_PHY_SUBFME_GET(he_cap_elem.phy_cap_info)) {
+		if (HECAP_PHY_SUBFMR_GET(he_cap->he_cap_elem.phy_cap_info))
+			hemode |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE);
+		if (HECAP_PHY_MUBFMR_GET(he_cap->he_cap_elem.phy_cap_info))
+			hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE);
+	}
+
+	if (vif->type != NL80211_IFTYPE_MESH_POINT) {
+		hemode |= FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) |
+			  FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE);
+
+		if (HECAP_PHY_ULMUMIMO_GET(he_cap_elem.phy_cap_info))
+			if (HECAP_PHY_ULMUMIMO_GET(he_cap->he_cap_elem.phy_cap_info))
+				hemode |= FIELD_PREP(HE_MODE_UL_MUMIMO,
+						     HE_UL_MUMIMO_ENABLE);
+
+		if (FIELD_GET(HE_MODE_MU_TX_BFEE, hemode))
+			hemode |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE);
+
+		if (FIELD_GET(HE_MODE_MU_TX_BFER, hemode))
+			hemode |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE);
+	}
+
+	ath11k_info(ar->ab, "mac0-5 cap %x-%x-%x-%x-%x-%x\n",
+		he_cap_elem.mac_cap_info[0],
+		he_cap_elem.mac_cap_info[1],
+		he_cap_elem.mac_cap_info[2],
+		he_cap_elem.mac_cap_info[3],
+		he_cap_elem.mac_cap_info[4],
+		he_cap_elem.mac_cap_info[5]);
+	ath11k_info(ar->ab, "phy0-5 cap %x-%x-%x-%x-%x-%x\n",
+		he_cap_elem.phy_cap_info[0],
+		he_cap_elem.phy_cap_info[1],
+		he_cap_elem.phy_cap_info[2],
+		he_cap_elem.phy_cap_info[3],
+		he_cap_elem.phy_cap_info[4],
+		he_cap_elem.phy_cap_info[5]);
+	ath11k_info(ar->ab, "phy6-10 cap %x-%x-%x-%x-%x\n",
+		he_cap_elem.phy_cap_info[6],
+		he_cap_elem.phy_cap_info[7],
+		he_cap_elem.phy_cap_info[8],
+		he_cap_elem.phy_cap_info[9],
+		he_cap_elem.phy_cap_info[10]);
+	ath11k_info(ar->ab, "WMI_VDEV_PARAM_SET_HEMU_MODE 3 0x%x\n", hemode);
+	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, hemode);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to submit vdev param txbf 0x%x: %d\n",
+			    hemode, ret);
+		return false;
+	}
+
+	return true;
+}
+
 static void ath11k_bss_assoc(struct ieee80211_hw *hw,
 			     struct ieee80211_vif *vif,
 			     struct ieee80211_bss_conf *bss_conf)
@@ -2705,6 +2856,7 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw,
 	struct ieee80211_sta *ap_sta;
 	struct ath11k_peer *peer;
 	bool is_auth = false;
+	struct ieee80211_sta_he_cap  he_cap;
 	int ret;
 
 	lockdep_assert_held(&ar->conf_mutex);
@@ -2722,11 +2874,21 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw,
 		return;
 	}
 
+	/* he_cap here is updated at assoc success for sta mode only */
+	he_cap  = ap_sta->deflink.he_cap;
+
 	ath11k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg, false);
 
 	rcu_read_unlock();
 
+	if (!ath11k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) {
+		ath11k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n",
+			    arvif->vdev_id, bss_conf->bssid);
+		return;
+	}
+
 	peer_arg.is_assoc = true;
+
 	ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg);
 	if (ret) {
 		ath11k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
@@ -3090,6 +3252,16 @@ static int ath11k_mac_config_obss_pd(struct ath11k *ar,
 	return 0;
 }
 
+static bool ath11k_mac_supports_station_tpc(struct ath11k *ar,
+					    struct ath11k_vif *arvif,
+					    const struct cfg80211_chan_def *chandef)
+{
+	return ath11k_hw_supports_tpc_ext(ar) &&
+	       arvif->vdev_type == WMI_VDEV_TYPE_STA &&
+	       chandef->chan &&
+	       chandef->chan->band == NL80211_BAND_6GHZ;
+}
+
 static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
 					   struct ieee80211_vif *vif,
 					   struct ieee80211_bss_conf *info,
@@ -3198,6 +3370,8 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
 		ether_addr_copy(arvif->bssid, info->bssid);
 
 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
+		if (info->enable_beacon)
+			ath11k_mac_set_he_txbf_conf(arvif);
 		ath11k_control_beaconing(arvif, info);
 
 		if (arvif->is_up && vif->bss_conf.he_support &&
@@ -3288,8 +3462,13 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
 		ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev_id %i txpower %d\n",
 			   arvif->vdev_id, info->txpower);
 
-		arvif->txpower = info->txpower;
-		ath11k_mac_txpower_recalc(ar);
+		if (ath11k_mac_supports_station_tpc(ar, arvif, &info->chandef)) {
+			ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+				   "discard tx power, change to set TPC power\n");
+		} else {
+			arvif->txpower = info->txpower;
+			ath11k_mac_txpower_recalc(ar);
+		}
 	}
 
 	if (changed & BSS_CHANGED_PS &&
@@ -3613,6 +3792,18 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw,
 	int ret = 0;
 	int i;
 
+	/* Firmwares advertising the support of triggering 11D algorithm
+	 * on the scan results of a regular scan expects driver to send
+	 * WMI_11D_SCAN_START_CMDID before sending WMI_START_SCAN_CMDID.
+	 * With this feature, separate 11D scan can be avoided since
+	 * regdomain can be determined with the scan results of the
+	 * regular scan.
+	 */
+	if (ar->state_11d == ATH11K_11D_PREPARING &&
+	    test_bit(WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN,
+		     ar->ab->wmi_ab.svc_map))
+		ath11k_mac_11d_scan_start(ar, arvif->vdev_id);
+
 	mutex_lock(&ar->conf_mutex);
 
 	spin_lock_bh(&ar->data_lock);
@@ -5009,8 +5200,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif)
 	if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
 		nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
 		nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
-		if (nsts > (ar->num_rx_chains - 1))
-			nsts = ar->num_rx_chains - 1;
 		value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET);
 	}
 
@@ -5051,7 +5240,7 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif)
 static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
 {
 	bool subfer, subfee;
-	int sound_dim = 0, nsts = 0;
+	int sound_dim = 0;
 
 	subfer = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE));
 	subfee = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE));
@@ -5061,11 +5250,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
 		subfer = false;
 	}
 
-	if (ar->num_rx_chains < 2) {
-		*vht_cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
-		subfee = false;
-	}
-
 	/* If SU Beaformer is not set, then disable MU Beamformer Capability */
 	if (!subfer)
 		*vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
@@ -5078,9 +5262,7 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
 	sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
 	*vht_cap &= ~IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
 
-	nsts = (*vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK);
-	nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
-	*vht_cap &= ~IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+	/* TODO: Need to check invalid STS and Sound_dim values set by FW? */
 
 	/* Enable Sounding Dimension Field only if SU BF is enabled */
 	if (subfer) {
@@ -5092,15 +5274,9 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
 		*vht_cap |= sound_dim;
 	}
 
-	/* Enable Beamformee STS Field only if SU BF is enabled */
-	if (subfee) {
-		if (nsts > (ar->num_rx_chains - 1))
-			nsts = ar->num_rx_chains - 1;
-
-		nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
-		nsts &=  IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
-		*vht_cap |= nsts;
-	}
+	/* Use the STS advertised by FW unless SU Beamformee is not supported*/
+	if (!subfee)
+		*vht_cap &= ~(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK);
 }
 
 static struct ieee80211_sta_vht_cap
@@ -5316,6 +5492,43 @@ static __le16 ath11k_mac_setup_he_6ghz_cap(struct ath11k_pdev_cap *pcap,
 	return cpu_to_le16(bcap->he_6ghz_capa);
 }
 
+static void ath11k_mac_set_hemcsmap(struct ath11k *ar,
+				    struct ath11k_pdev_cap *cap,
+				    struct ieee80211_sta_he_cap *he_cap,
+				    int band)
+{
+	u16 txmcs_map, rxmcs_map;
+	u32 i;
+
+	rxmcs_map = 0;
+	txmcs_map = 0;
+	for (i = 0; i < 8; i++) {
+		if (i < ar->num_tx_chains &&
+		    (ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
+			txmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2);
+		else
+			txmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
+
+		if (i < ar->num_rx_chains &&
+		    (ar->cfg_rx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
+			rxmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2);
+		else
+			rxmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
+	}
+	he_cap->he_mcs_nss_supp.rx_mcs_80 =
+		cpu_to_le16(rxmcs_map & 0xffff);
+	he_cap->he_mcs_nss_supp.tx_mcs_80 =
+		cpu_to_le16(txmcs_map & 0xffff);
+	he_cap->he_mcs_nss_supp.rx_mcs_160 =
+		cpu_to_le16(rxmcs_map & 0xffff);
+	he_cap->he_mcs_nss_supp.tx_mcs_160 =
+		cpu_to_le16(txmcs_map & 0xffff);
+	he_cap->he_mcs_nss_supp.rx_mcs_80p80 =
+		cpu_to_le16(rxmcs_map & 0xffff);
+	he_cap->he_mcs_nss_supp.tx_mcs_80p80 =
+		cpu_to_le16(txmcs_map & 0xffff);
+}
+
 static int ath11k_mac_copy_he_cap(struct ath11k *ar,
 				  struct ath11k_pdev_cap *cap,
 				  struct ieee80211_sband_iftype_data *data,
@@ -5373,18 +5586,7 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar,
 			break;
 		}
 
-		he_cap->he_mcs_nss_supp.rx_mcs_80 =
-			cpu_to_le16(band_cap->he_mcs & 0xffff);
-		he_cap->he_mcs_nss_supp.tx_mcs_80 =
-			cpu_to_le16(band_cap->he_mcs & 0xffff);
-		he_cap->he_mcs_nss_supp.rx_mcs_160 =
-			cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff);
-		he_cap->he_mcs_nss_supp.tx_mcs_160 =
-			cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff);
-		he_cap->he_mcs_nss_supp.rx_mcs_80p80 =
-			cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff);
-		he_cap->he_mcs_nss_supp.tx_mcs_80p80 =
-			cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff);
+		ath11k_mac_set_hemcsmap(ar, cap, he_cap, band);
 
 		memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres));
 		if (he_cap_elem->phy_cap_info[6] &
@@ -5794,6 +5996,11 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw)
 	struct ath11k_pdev *pdev = ar->pdev;
 	int ret;
 
+	if (ath11k_ftm_mode) {
+		ath11k_err(ab, "fail to start mac operations in ftm mode\n");
+		return -EWOULDBLOCK;
+	}
+
 	ath11k_mac_drain_tx(ar);
 	mutex_lock(&ar->conf_mutex);
 
@@ -5808,6 +6015,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw)
 	case ATH11K_STATE_RESTARTED:
 	case ATH11K_STATE_WEDGED:
 	case ATH11K_STATE_ON:
+	case ATH11K_STATE_TM:
 		WARN_ON(1);
 		ret = -EINVAL;
 		goto err;
@@ -5918,6 +6126,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
 {
 	struct ath11k *ar = hw->priv;
 	struct htt_ppdu_stats_info *ppdu_stats, *tmp;
+	struct scan_chan_list_params *params, *tmp_ch;
 	int ret;
 
 	ath11k_mac_drain_tx(ar);
@@ -5933,6 +6142,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_delayed_work_sync(&ar->scan.timeout);
+	cancel_work_sync(&ar->channel_update_work);
 	cancel_work_sync(&ar->regd_update_work);
 	cancel_work_sync(&ar->ab->update_11d_work);
 
@@ -5948,6 +6158,13 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
 	}
 	spin_unlock_bh(&ar->data_lock);
 
+	spin_lock_bh(&ar->channel_update_lock);
+	list_for_each_entry_safe(params, tmp_ch, &ar->channel_update_queue, list) {
+		list_del(&params->list);
+		kfree(params);
+	}
+	spin_unlock_bh(&ar->channel_update_lock);
+
 	rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL);
 
 	synchronize_rcu();
@@ -5982,69 +6199,6 @@ ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif,
 	}
 }
 
-static u32
-ath11k_mac_prepare_he_mode(struct ath11k_pdev *pdev, u32 viftype)
-{
-	struct ath11k_pdev_cap *pdev_cap = &pdev->cap;
-	struct ath11k_band_cap *cap_band = NULL;
-	u32 *hecap_phy_ptr = NULL;
-	u32 hemode = 0;
-
-	if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP)
-		cap_band = &pdev_cap->band[NL80211_BAND_2GHZ];
-	else
-		cap_band = &pdev_cap->band[NL80211_BAND_5GHZ];
-
-	hecap_phy_ptr = &cap_band->he_cap_phy_info[0];
-
-	hemode = FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE) |
-		 FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) |
-		 FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr));
-
-	/* TODO WDS and other modes */
-	if (viftype == NL80211_IFTYPE_AP) {
-		hemode |= FIELD_PREP(HE_MODE_MU_TX_BFER,
-			  HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) |
-			  FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) |
-			  FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE);
-	} else {
-		hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE);
-	}
-
-	return hemode;
-}
-
-static int ath11k_set_he_mu_sounding_mode(struct ath11k *ar,
-					  struct ath11k_vif *arvif)
-{
-	u32 param_id, param_value;
-	struct ath11k_base *ab = ar->ab;
-	int ret = 0;
-
-	param_id = WMI_VDEV_PARAM_SET_HEMU_MODE;
-	param_value = ath11k_mac_prepare_he_mode(ar->pdev, arvif->vif->type);
-	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
-					    param_id, param_value);
-	if (ret) {
-		ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d param_value %x\n",
-			    arvif->vdev_id, ret, param_value);
-		return ret;
-	}
-	param_id = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE;
-	param_value =
-		FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) |
-		FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE,
-			   HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE);
-	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
-					    param_id, param_value);
-	if (ret) {
-		ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d\n",
-			    arvif->vdev_id, ret);
-		return ret;
-	}
-	return ret;
-}
-
 static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
 					     struct ieee80211_vif *vif)
 {
@@ -6211,6 +6365,40 @@ void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab)
 	}
 }
 
+static int ath11k_mac_vdev_delete(struct ath11k *ar, struct ath11k_vif *arvif)
+{
+	unsigned long time_left;
+	struct ieee80211_vif *vif = arvif->vif;
+	int ret = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	reinit_completion(&ar->vdev_delete_done);
+
+	ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to delete WMI vdev %d: %d\n",
+			    arvif->vdev_id, ret);
+		return ret;
+	}
+
+	time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
+						ATH11K_VDEV_DELETE_TIMEOUT_HZ);
+	if (time_left == 0) {
+		ath11k_warn(ar->ab, "Timeout in receiving vdev delete response\n");
+		return -ETIMEDOUT;
+	}
+
+	ar->ab->free_vdev_map |= 1LL << (arvif->vdev_id);
+	ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
+	ar->num_created_vdevs--;
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n",
+		   vif->addr, arvif->vdev_id);
+
+	return ret;
+}
+
 static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
 				       struct ieee80211_vif *vif)
 {
@@ -6433,6 +6621,14 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
 
 	ath11k_debugfs_add_interface(arvif);
 
+	if (ath11k_hw_supports_6g_cc_ext(ar)) {
+		struct cur_regulatory_info *reg_info;
+
+		reg_info = &ab->reg_info_store[ar->pdev_idx];
+		ath11k_dbg(ab, ATH11K_DBG_MAC, "mac interface added to change reg rules\n");
+		ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_LPI_AP);
+	}
+
 	mutex_unlock(&ar->conf_mutex);
 
 	return 0;
@@ -6448,10 +6644,7 @@ err_peer_del:
 	}
 
 err_vdev_del:
-	ath11k_wmi_vdev_delete(ar, arvif->vdev_id);
-	ar->num_created_vdevs--;
-	ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
-	ab->free_vdev_map |= 1LL << arvif->vdev_id;
+	ath11k_mac_vdev_delete(ar, arvif);
 	spin_lock_bh(&ar->data_lock);
 	list_del(&arvif->list);
 	spin_unlock_bh(&ar->data_lock);
@@ -6480,7 +6673,6 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
 	struct ath11k *ar = hw->priv;
 	struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 	struct ath11k_base *ab = ar->ab;
-	unsigned long time_left;
 	int ret;
 	int i;
 
@@ -6501,29 +6693,13 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
 				    arvif->vdev_id, ret);
 	}
 
-	reinit_completion(&ar->vdev_delete_done);
-
-	ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id);
+	ret = ath11k_mac_vdev_delete(ar, arvif);
 	if (ret) {
-		ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n",
+		ath11k_warn(ab, "failed to delete vdev %d: %d\n",
 			    arvif->vdev_id, ret);
 		goto err_vdev_del;
 	}
 
-	time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
-						ATH11K_VDEV_DELETE_TIMEOUT_HZ);
-	if (time_left == 0) {
-		ath11k_warn(ab, "Timeout in receiving vdev delete response\n");
-		goto err_vdev_del;
-	}
-
-	ab->free_vdev_map |= 1LL << (arvif->vdev_id);
-	ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
-	ar->num_created_vdevs--;
-
-	ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n",
-		   vif->addr, arvif->vdev_id);
-
 	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
 		clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags);
 		ar->monitor_vdev_id = -1;
@@ -6702,7 +6878,6 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif,
 	struct ath11k_base *ab = ar->ab;
 	struct wmi_vdev_start_req_arg arg = {};
 	const struct cfg80211_chan_def *chandef = &ctx->def;
-	int he_support = arvif->vif->bss_conf.he_support;
 	int ret = 0;
 
 	lockdep_assert_held(&ar->conf_mutex);
@@ -6743,15 +6918,6 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif,
 		spin_lock_bh(&ab->base_lock);
 		arg.regdomain = ar->ab->dfs_region;
 		spin_unlock_bh(&ab->base_lock);
-
-		if (he_support) {
-			ret = ath11k_set_he_mu_sounding_mode(ar, arvif);
-			if (ret) {
-				ath11k_warn(ar->ab, "failed to set he mode vdev %i\n",
-					    arg.vdev_id);
-				return ret;
-			}
-		}
 	}
 
 	arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR);
@@ -6775,6 +6941,12 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif,
 		return ret;
 	}
 
+	if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) {
+		ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx);
+		ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
+						   &arvif->reg_tpc_info);
+	}
+
 	if (!restart)
 		ar->num_started_vdevs++;
 
@@ -7088,6 +7260,469 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
 	return 0;
 }
 
+static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt)
+{
+	switch (txpwr_intrprt) {
+	/* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield
+	 * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of
+	 * "IEEE Std 802.11ax 2021".
+	 */
+	case IEEE80211_TPE_LOCAL_EIRP:
+	case IEEE80211_TPE_REG_CLIENT_EIRP:
+		txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3;
+		txpwr_cnt = txpwr_cnt + 1;
+		break;
+	/* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield
+	 * if Maximum Transmit Power Interpretation subfield is 1 or 3" of
+	 * "IEEE Std 802.11ax 2021".
+	 */
+	case IEEE80211_TPE_LOCAL_EIRP_PSD:
+	case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
+		txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4;
+		txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1;
+		break;
+	}
+
+	return txpwr_cnt;
+}
+
+static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
+{
+	if (chan_def->chan->flags & IEEE80211_CHAN_PSD) {
+		switch (chan_def->width) {
+		case NL80211_CHAN_WIDTH_20:
+			return 1;
+		case NL80211_CHAN_WIDTH_40:
+			return 2;
+		case NL80211_CHAN_WIDTH_80:
+			return 4;
+		case NL80211_CHAN_WIDTH_80P80:
+		case NL80211_CHAN_WIDTH_160:
+			return 8;
+		default:
+			return 1;
+		}
+	} else {
+		switch (chan_def->width) {
+		case NL80211_CHAN_WIDTH_20:
+			return 1;
+		case NL80211_CHAN_WIDTH_40:
+			return 2;
+		case NL80211_CHAN_WIDTH_80:
+			return 3;
+		case NL80211_CHAN_WIDTH_80P80:
+		case NL80211_CHAN_WIDTH_160:
+			return 4;
+		default:
+			return 1;
+		}
+	}
+}
+
+static u16 ath11k_mac_get_6g_start_frequency(struct cfg80211_chan_def *chan_def)
+{
+	u16 diff_seq;
+
+	/* It is to get the lowest channel number's center frequency of the chan.
+	 * For example,
+	 * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
+	 * with center frequency 5955, its diff is 5965 - 5955 = 10.
+	 * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
+	 * with center frequency 5955, its diff is 5985 - 5955 = 30.
+	 * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
+	 * with center frequency 5955, its diff is 6025 - 5955 = 70.
+	 */
+	switch (chan_def->width) {
+	case NL80211_CHAN_WIDTH_160:
+		diff_seq = 70;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+	case NL80211_CHAN_WIDTH_80P80:
+		diff_seq = 30;
+		break;
+	case NL80211_CHAN_WIDTH_40:
+		diff_seq = 10;
+		break;
+	default:
+		diff_seq = 0;
+	}
+
+	return chan_def->center_freq1 - diff_seq;
+}
+
+static u16 ath11k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
+				   u16 start_seq, u8 seq)
+{
+	u16 seg_seq;
+
+	/* It is to get the center frequency of the specific bandwidth.
+	 * start_seq means the lowest channel number's center frequency.
+	 * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz&80P80.
+	 * For example,
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
+	 */
+	if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3)
+		return chan_def->center_freq2;
+
+	seg_seq = 10 * (BIT(seq) - 1);
+	return seg_seq + start_seq;
+}
+
+static void ath11k_mac_get_psd_channel(struct ath11k *ar,
+				       u16 step_freq,
+				       u16 *start_freq,
+				       u16 *center_freq,
+				       u8 i,
+				       struct ieee80211_channel **temp_chan,
+				       s8 *tx_power)
+{
+	/* It is to get the the center frequency for each 20 MHz.
+	 * For example, if the chan is 160 MHz and center frequency is 6025,
+	 * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
+	 * channel number 1's center frequency is 5955, it is parameter start_freq.
+	 * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
+	 * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
+	 * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
+	 * the gap is 20 for each channel, parameter step_freq means the gap.
+	 * after get the center frequency of each channel, it is easy to find the
+	 * struct ieee80211_channel of it and get the max_reg_power.
+	 */
+	*center_freq = *start_freq + i * step_freq;
+	*temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+	*tx_power = (*temp_chan)->max_reg_power;
+}
+
+static void ath11k_mac_get_eirp_power(struct ath11k *ar,
+				      u16 *start_freq,
+				      u16 *center_freq,
+				      u8 i,
+				      struct ieee80211_channel **temp_chan,
+				      struct cfg80211_chan_def *def,
+				      s8 *tx_power)
+{
+	/* It is to get the the center frequency for 20 MHz/40 MHz/80 MHz/
+	 * 160 MHz&80P80 bandwidth, and then plus 10 to the center frequency,
+	 * it is the center frequency of a channel number.
+	 * For example, when configured channel number is 1.
+	 * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
+	 * then it is channel number 5.
+	 * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
+	 * then it is channel number 9.
+	 * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
+	 * then it is channel number 17.
+	 * after get the center frequency of each channel, it is easy to find the
+	 * struct ieee80211_channel of it and get the max_reg_power.
+	 */
+	*center_freq = ath11k_mac_get_seg_freq(def, *start_freq, i);
+
+	/* For the 20 MHz, its center frequency is same with same channel */
+	if (i != 0)
+		*center_freq += 10;
+
+	*temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+	*tx_power = (*temp_chan)->max_reg_power;
+}
+
+void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_chanctx_conf *ctx)
+{
+	struct ath11k_base *ab = ar->ab;
+	struct ath11k_vif *arvif = (void *)vif->drv_priv;
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+	struct ath11k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
+	struct ieee80211_channel *chan, *temp_chan;
+	u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
+	bool is_psd_power = false, is_tpe_present = false;
+	s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
+		psd_power, tx_power, eirp_power;
+	u16 oper_freq, start_freq, center_freq;
+
+	chan = ctx->def.chan;
+	oper_freq = ctx->def.chan->center_freq;
+	start_freq = ath11k_mac_get_6g_start_frequency(&ctx->def);
+	pwr_reduction = bss_conf->pwr_reduction;
+
+	if (arvif->reg_tpc_info.num_pwr_levels) {
+		is_tpe_present = true;
+		num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
+	} else {
+		num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def);
+	}
+
+	for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
+		/* STA received TPE IE*/
+		if (is_tpe_present) {
+			/* local power is PSD power*/
+			if (chan->flags & IEEE80211_CHAN_PSD) {
+				/* Connecting AP is psd power */
+				if (reg_tpc_info->is_psd_power) {
+					is_psd_power = true;
+					ath11k_mac_get_psd_channel(ar, 20,
+								   &start_freq,
+								   &center_freq,
+								   pwr_lvl_idx,
+								   &temp_chan,
+								   &tx_power);
+					psd_power = temp_chan->psd;
+					eirp_power = tx_power;
+					max_tx_power[pwr_lvl_idx] =
+						min_t(s8,
+						      psd_power,
+						      reg_tpc_info->tpe[pwr_lvl_idx]);
+				/* Connecting AP is not psd power */
+				} else {
+					ath11k_mac_get_eirp_power(ar,
+								  &start_freq,
+								  &center_freq,
+								  pwr_lvl_idx,
+								  &temp_chan,
+								  &ctx->def,
+								  &tx_power);
+					psd_power = temp_chan->psd;
+					/* convert psd power to EIRP power based
+					 * on channel width
+					 */
+					tx_power =
+						min_t(s8, tx_power,
+						      psd_power + 13 + pwr_lvl_idx * 3);
+					max_tx_power[pwr_lvl_idx] =
+						min_t(s8,
+						      tx_power,
+						      reg_tpc_info->tpe[pwr_lvl_idx]);
+				}
+			/* local power is not PSD power */
+			} else {
+				/* Connecting AP is psd power */
+				if (reg_tpc_info->is_psd_power) {
+					is_psd_power = true;
+					ath11k_mac_get_psd_channel(ar, 20,
+								   &start_freq,
+								   &center_freq,
+								   pwr_lvl_idx,
+								   &temp_chan,
+								   &tx_power);
+					eirp_power = tx_power;
+					max_tx_power[pwr_lvl_idx] =
+						reg_tpc_info->tpe[pwr_lvl_idx];
+				/* Connecting AP is not psd power */
+				} else {
+					ath11k_mac_get_eirp_power(ar,
+								  &start_freq,
+								  &center_freq,
+								  pwr_lvl_idx,
+								  &temp_chan,
+								  &ctx->def,
+								  &tx_power);
+					max_tx_power[pwr_lvl_idx] =
+						min_t(s8,
+						      tx_power,
+						      reg_tpc_info->tpe[pwr_lvl_idx]);
+				}
+			}
+		/* STA not received TPE IE */
+		} else {
+			/* local power is PSD power*/
+			if (chan->flags & IEEE80211_CHAN_PSD) {
+				is_psd_power = true;
+				ath11k_mac_get_psd_channel(ar, 20,
+							   &start_freq,
+							   &center_freq,
+							   pwr_lvl_idx,
+							   &temp_chan,
+							   &tx_power);
+				psd_power = temp_chan->psd;
+				eirp_power = tx_power;
+				max_tx_power[pwr_lvl_idx] = psd_power;
+			} else {
+				ath11k_mac_get_eirp_power(ar,
+							  &start_freq,
+							  &center_freq,
+							  pwr_lvl_idx,
+							  &temp_chan,
+							  &ctx->def,
+							  &tx_power);
+				max_tx_power[pwr_lvl_idx] =
+					min_t(s8,
+					      tx_power,
+					      reg_tpc_info->tpe[pwr_lvl_idx]);
+			}
+		}
+
+		if (is_psd_power) {
+			/* If AP local power constraint is present */
+			if (pwr_reduction)
+				eirp_power = eirp_power - pwr_reduction;
+
+			/* If firmware updated max tx power is non zero, then take
+			 * the min of firmware updated ap tx power
+			 * and max power derived from above mentioned parameters.
+			 */
+			ath11k_dbg(ab, ATH11K_DBG_MAC,
+				   "eirp power : %d firmware report power : %d\n",
+				   eirp_power, ar->max_allowed_tx_power);
+			if (ar->max_allowed_tx_power)
+				eirp_power = min_t(s8,
+						   eirp_power,
+						   ar->max_allowed_tx_power);
+		} else {
+			/* If AP local power constraint is present */
+			if (pwr_reduction)
+				max_tx_power[pwr_lvl_idx] =
+					max_tx_power[pwr_lvl_idx] - pwr_reduction;
+			/* If firmware updated max tx power is non zero, then take
+			 * the min of firmware updated ap tx power
+			 * and max power derived from above mentioned parameters.
+			 */
+			if (ar->max_allowed_tx_power)
+				max_tx_power[pwr_lvl_idx] =
+					min_t(s8,
+					      max_tx_power[pwr_lvl_idx],
+					      ar->max_allowed_tx_power);
+		}
+		reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
+		reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
+			max_tx_power[pwr_lvl_idx];
+	}
+
+	reg_tpc_info->num_pwr_levels = num_pwr_levels;
+	reg_tpc_info->is_psd_power = is_psd_power;
+	reg_tpc_info->eirp_power = eirp_power;
+	reg_tpc_info->power_type_6g =
+		ath11k_ieee80211_ap_pwr_type_convert(vif->bss_conf.power_type);
+}
+
+static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
+					struct ieee80211_vif *vif,
+					struct ieee80211_chanctx_conf *ctx)
+{
+	struct ath11k_base *ab = ar->ab;
+	struct ath11k_vif *arvif = (void *)vif->drv_priv;
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+	struct ieee80211_tx_pwr_env *single_tpe;
+	enum wmi_reg_6ghz_client_type client_type;
+	struct cur_regulatory_info *reg_info;
+	int i;
+	u8 pwr_count, pwr_interpret, pwr_category;
+	u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0;
+	bool use_local_tpe, non_psd_set = false, psd_set = false;
+
+	reg_info = &ab->reg_info_store[ar->pdev_idx];
+	client_type = reg_info->client_type;
+
+	for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
+		single_tpe = &bss_conf->tx_pwr_env[i];
+		pwr_category = u8_get_bits(single_tpe->tx_power_info,
+					   IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
+		pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+					    IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+
+		if (pwr_category == client_type) {
+			if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP ||
+			    pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD)
+				local_tpe_count++;
+			else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP ||
+				 pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD)
+				reg_tpe_count++;
+		}
+	}
+
+	if (!reg_tpe_count && !local_tpe_count) {
+		ath11k_warn(ab,
+			    "no transmit power envelope match client power type %d\n",
+			    client_type);
+		return;
+	} else if (!reg_tpe_count) {
+		use_local_tpe = true;
+	} else {
+		use_local_tpe = false;
+	}
+
+	for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
+		single_tpe = &bss_conf->tx_pwr_env[i];
+		pwr_category = u8_get_bits(single_tpe->tx_power_info,
+					   IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
+		pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+					    IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+
+		if (pwr_category != client_type)
+			continue;
+
+		/* get local transmit power envelope */
+		if (use_local_tpe) {
+			if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) {
+				non_psd_index = i;
+				non_psd_set = true;
+			} else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) {
+				psd_index = i;
+				psd_set = true;
+			}
+		/* get regulatory transmit power envelope */
+		} else {
+			if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) {
+				non_psd_index = i;
+				non_psd_set = true;
+			} else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) {
+				psd_index = i;
+				psd_set = true;
+			}
+		}
+	}
+
+	if (non_psd_set && !psd_set) {
+		single_tpe = &bss_conf->tx_pwr_env[non_psd_index];
+		pwr_count = u8_get_bits(single_tpe->tx_power_info,
+					IEEE80211_TX_PWR_ENV_INFO_COUNT);
+		pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+					    IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+		arvif->reg_tpc_info.is_psd_power = false;
+		arvif->reg_tpc_info.eirp_power = 0;
+
+		arvif->reg_tpc_info.num_pwr_levels =
+			ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
+		for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
+			ath11k_dbg(ab, ATH11K_DBG_MAC,
+				   "non PSD power[%d] : %d\n",
+				   i, single_tpe->tx_power[i]);
+			arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
+		}
+	}
+
+	if (psd_set) {
+		single_tpe = &bss_conf->tx_pwr_env[psd_index];
+		pwr_count = u8_get_bits(single_tpe->tx_power_info,
+					IEEE80211_TX_PWR_ENV_INFO_COUNT);
+		pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+					    IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+		arvif->reg_tpc_info.is_psd_power = true;
+
+		if (pwr_count == 0) {
+			ath11k_dbg(ab, ATH11K_DBG_MAC,
+				   "TPE PSD power : %d\n", single_tpe->tx_power[0]);
+			arvif->reg_tpc_info.num_pwr_levels =
+				ath11k_mac_get_num_pwr_levels(&ctx->def);
+			for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++)
+				arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2;
+		} else {
+			arvif->reg_tpc_info.num_pwr_levels =
+				ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
+			for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
+				ath11k_dbg(ab, ATH11K_DBG_MAC,
+					   "TPE PSD power[%d] : %d\n",
+					   i, single_tpe->tx_power[i]);
+				arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
+			}
+		}
+	}
+}
+
 static int
 ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
 				 struct ieee80211_vif *vif,
@@ -7099,6 +7734,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
 	struct ath11k_vif *arvif = (void *)vif->drv_priv;
 	int ret;
 	struct peer_create_params param;
+	struct cur_regulatory_info *reg_info;
+	enum ieee80211_ap_reg_power power_type;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -7106,6 +7743,20 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
 		   "mac chanctx assign ptr %pK vdev_id %i\n",
 		   ctx, arvif->vdev_id);
 
+	if (ath11k_hw_supports_6g_cc_ext(ar) &&
+	    ctx->def.chan->band == NL80211_BAND_6GHZ &&
+	    arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+		reg_info = &ab->reg_info_store[ar->pdev_idx];
+		power_type = vif->bss_conf.power_type;
+		ath11k_dbg(ab, ATH11K_DBG_MAC, "mac chanctx power type %d\n",
+			   power_type);
+		if (power_type == IEEE80211_REG_UNSET_AP)
+			power_type = IEEE80211_REG_LPI_AP;
+		ath11k_reg_handle_chan_list(ab, reg_info, power_type);
+		arvif->chanctx = *ctx;
+		ath11k_mac_parse_tx_pwr_env(ar, vif, ctx);
+	}
+
 	/* for QCA6390 bss peer must be created before vdev_start */
 	if (ab->hw_params.vdev_start_delay &&
 	    arvif->vdev_type != WMI_VDEV_TYPE_AP &&
@@ -7977,6 +8628,7 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw,
 	struct ath11k *ar = hw->priv;
 	struct ath11k_base *ab = ar->ab;
 	int recovery_count;
+	struct ath11k_vif *arvif;
 
 	if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
 		return;
@@ -8012,6 +8664,12 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw,
 				ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n");
 			}
 		}
+		if (ar->ab->hw_params.support_fw_mac_sequence) {
+			list_for_each_entry(arvif, &ar->arvifs, list) {
+				if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA)
+					ieee80211_hw_restart_disconnect(arvif->vif);
+			}
+		}
 	}
 
 	mutex_unlock(&ar->conf_mutex);
@@ -8774,6 +9432,31 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
 	return 0;
 }
 
+static void ath11k_mac_setup_mac_address_list(struct ath11k *ar)
+{
+	struct mac_address *addresses;
+	u16 n_addresses;
+	int i;
+
+	if (!ar->ab->hw_params.single_pdev_only || ar->ab->hw_params.num_rxmda_per_pdev < 2)
+		return;
+
+	n_addresses = 3;
+	addresses = kcalloc(n_addresses, sizeof(*addresses), GFP_KERNEL);
+	if (!addresses)
+		return;
+
+	memcpy(addresses[0].addr, ar->mac_addr, ETH_ALEN);
+	for (i = 1; i < n_addresses; i++) {
+		memcpy(addresses[i].addr, ar->mac_addr, ETH_ALEN);
+		addresses[i].addr[0] |= 0x2;
+		addresses[i].addr[0] += (i - 1) << 4;
+	}
+
+	ar->hw->wiphy->addresses = addresses;
+	ar->hw->wiphy->n_addresses = n_addresses;
+}
+
 static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
 {
 	struct ath11k_base *ab = ar->ab;
@@ -8793,28 +9476,43 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
 		return -ENOMEM;
 	}
 
-	limits[0].max = 1;
-	limits[0].types |= BIT(NL80211_IFTYPE_STATION);
-
-	limits[1].max = 16;
-	limits[1].types |= BIT(NL80211_IFTYPE_AP);
+	if (ab->hw_params.single_pdev_only && ar->ab->hw_params.num_rxmda_per_pdev > 1) {
+		limits[0].max = 2;
+		limits[0].types |= BIT(NL80211_IFTYPE_STATION);
 
-	if (IS_ENABLED(CONFIG_MAC80211_MESH) &&
-	    ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
-		limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+		limits[1].max = 1;
+		limits[1].types |= BIT(NL80211_IFTYPE_AP);
 
-	combinations[0].limits = limits;
-	combinations[0].n_limits = n_limits;
-	combinations[0].max_interfaces = 16;
-	combinations[0].num_different_channels = 1;
-	combinations[0].beacon_int_infra_match = true;
-	combinations[0].beacon_int_min_gcd = 100;
-	combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-						BIT(NL80211_CHAN_WIDTH_20) |
-						BIT(NL80211_CHAN_WIDTH_40) |
-						BIT(NL80211_CHAN_WIDTH_80) |
-						BIT(NL80211_CHAN_WIDTH_80P80) |
-						BIT(NL80211_CHAN_WIDTH_160);
+		combinations[0].limits = limits;
+		combinations[0].n_limits = 2;
+		combinations[0].max_interfaces = 3;
+		combinations[0].num_different_channels = 2;
+		combinations[0].beacon_int_infra_match = true;
+		combinations[0].beacon_int_min_gcd = 100;
+	} else {
+		limits[0].max = 1;
+		limits[0].types |= BIT(NL80211_IFTYPE_STATION);
+
+		limits[1].max = 16;
+		limits[1].types |= BIT(NL80211_IFTYPE_AP);
+
+		if (IS_ENABLED(CONFIG_MAC80211_MESH) &&
+		    ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
+			limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+		combinations[0].limits = limits;
+		combinations[0].n_limits = 2;
+		combinations[0].max_interfaces = 16;
+		combinations[0].num_different_channels = 1;
+		combinations[0].beacon_int_infra_match = true;
+		combinations[0].beacon_int_min_gcd = 100;
+		combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+							BIT(NL80211_CHAN_WIDTH_20) |
+							BIT(NL80211_CHAN_WIDTH_40) |
+							BIT(NL80211_CHAN_WIDTH_80) |
+							BIT(NL80211_CHAN_WIDTH_80P80) |
+							BIT(NL80211_CHAN_WIDTH_160);
+	}
 
 	ar->hw->wiphy->iface_combinations = combinations;
 	ar->hw->wiphy->n_iface_combinations = 1;
@@ -8861,6 +9559,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = {
 
 static void __ath11k_mac_unregister(struct ath11k *ar)
 {
+	cancel_work_sync(&ar->channel_update_work);
 	cancel_work_sync(&ar->regd_update_work);
 
 	ieee80211_unregister_hw(ar->hw);
@@ -8875,6 +9574,8 @@ static void __ath11k_mac_unregister(struct ath11k *ar)
 	kfree(ar->hw->wiphy->iface_combinations[0].limits);
 	kfree(ar->hw->wiphy->iface_combinations);
 
+	kfree(ar->hw->wiphy->addresses);
+
 	SET_IEEE80211_DEV(ar->hw, NULL);
 }
 
@@ -8917,6 +9618,7 @@ static int __ath11k_mac_register(struct ath11k *ar)
 	ath11k_pdev_caps_update(ar);
 
 	SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
+	ath11k_mac_setup_mac_address_list(ar);
 
 	SET_IEEE80211_DEV(ar->hw, ab->dev);
 
@@ -9054,6 +9756,8 @@ static int __ath11k_mac_register(struct ath11k *ar)
 	ar->hw->wiphy->iftype_ext_capab = ath11k_iftypes_ext_capa;
 	ar->hw->wiphy->num_iftype_ext_capab =
 		ARRAY_SIZE(ath11k_iftypes_ext_capa);
+	ar->hw->wiphy->vendor_commands = ath11k_vendor_cmds;
+	ar->hw->wiphy->n_vendor_commands = ARRAY_SIZE(ath11k_vendor_cmds);
 
 	if (ar->supports_6ghz) {
 		wiphy_ext_feature_set(ar->hw->wiphy,
@@ -9245,6 +9949,9 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
 
 		INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work);
 		INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work);
+		INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work);
+		INIT_LIST_HEAD(&ar->channel_update_queue);
+		spin_lock_init(&ar->channel_update_lock);
 
 		INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work);
 		skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
@@ -9315,3 +10022,27 @@ int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
 
 	return 0;
 }
+
+static int ath11k_mac_vif_send_coex_config(struct ath11k_vif *arvif,
+		struct wmi_coex_config_params *param)
+{
+	return ath11k_wmi_send_coex_config(arvif->ar, param);
+}
+
+int ath11k_mac_send_coex_config(struct ath11k *ar,
+		struct wmi_coex_config_params *param)
+{
+	struct ath11k_vif *arvif;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		param->vdev_id = arvif->vdev_id;
+		ret = ath11k_mac_vif_send_coex_config(arvif, param);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index 0231783ad754..205140e315be 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -158,7 +158,7 @@ struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab);
 
 struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id);
 struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id);
-
+enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar);
 void ath11k_mac_drain_tx(struct ath11k *ar);
 void ath11k_mac_peer_cleanup_all(struct ath11k *ar);
 int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
@@ -175,4 +175,9 @@ int ath11k_mac_wait_tx_complete(struct ath11k *ar);
 int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
 				 enum wmi_sta_keepalive_method method,
 				 u32 interval);
+int ath11k_mac_send_coex_config(struct ath11k *ar,
+				struct wmi_coex_config_params *param);
+void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_chanctx_conf *ctx);
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index a62ee05c5409..e3ade8e5ce43 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -324,6 +324,7 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
 				    enum mhi_callback cb)
 {
 	struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
+	struct ath11k_pci *ar_pci = ath11k_pci_priv(ab);
 
 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "mhi notify status reason %s\n",
 		   ath11k_mhi_op_callback_to_str(cb));
@@ -334,7 +335,7 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
 		break;
 	case MHI_CB_EE_RDDM:
 		if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
-			queue_work(ab->workqueue_aux, &ab->reset_work);
+			queue_work(ab->workqueue_aux, &ar_pci->rddm_worker);
 		break;
 	default:
 		break;
@@ -414,7 +415,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
 			goto free_controller;
 	} else {
 		mhi_ctrl->iova_start = 0;
-		mhi_ctrl->iova_stop = 0xFFFFFFFF;
+		mhi_ctrl->iova_stop = 0xFFFFFFFFF;
 	}
 
 	mhi_ctrl->rddm_size = RDDM_DUMP_SIZE;
@@ -434,6 +435,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
 	case ATH11K_HW_QCA6390_HW20:
 	case ATH11K_HW_WCN6855_HW20:
 	case ATH11K_HW_WCN6855_HW21:
+	case ATH11K_HW_QCA206X_HW21:
 		ath11k_mhi_config = &ath11k_mhi_config_qca6390;
 		break;
 	default:
@@ -525,3 +527,18 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
 
 	return 0;
 }
+
+
+int ath11k_mhi_force_rddm(struct ath11k_pci *ab_pci)
+{
+	struct ath11k_base *ab = ab_pci->ab;
+	int ret;
+
+	ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl);
+	if (ret) {
+		ath11k_warn(ab, "failed to resume mhi: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h
index 8d9f852da695..215428b87fc4 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.h
+++ b/drivers/net/wireless/ath/ath11k/mhi.h
@@ -25,5 +25,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab);
 
 int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
 int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
+int ath11k_mhi_force_rddm(struct ath11k_pci *ab_pci);
 
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index 3c6005ab9a71..5af419aa10ca 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -15,9 +15,10 @@
 #include "mhi.h"
 #include "debug.h"
 #include "pcic.h"
+#include "coredump.h"
 
 #define ATH11K_PCI_BAR_NUM		0
-#define ATH11K_PCI_DMA_MASK		32
+#define ATH11K_PCI_DMA_MASK		36
 
 #define TCSR_SOC_HW_VERSION		0x0224
 #define TCSR_SOC_HW_VERSION_MAJOR_MASK	GENMASK(11, 8)
@@ -27,6 +28,8 @@
 #define QCN9074_DEVICE_ID		0x1104
 #define WCN6855_DEVICE_ID		0x1103
 
+#define SUB_VERSION	0x1910010
+
 static const struct pci_device_id ath11k_pci_id_table[] = {
 	{ PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) },
 	{ PCI_VDEVICE(QCOM, WCN6855_DEVICE_ID) },
@@ -36,6 +39,124 @@ static const struct pci_device_id ath11k_pci_id_table[] = {
 
 MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table);
 
+static struct cnss_pci_reg register_rddm_fail_debug[] = {
+	{"PCIE_BHI_VERSION_LOWER", HWIO_PCIE_PCIE_BHI_VERSION_LOWER_ADDR, 0},
+	{"PCIE_BHI_VERSION_UPPER", HWIO_PCIE_PCIE_BHI_VERSION_UPPER_ADDR, 0},
+	{"PCIE_BHI_IMGADDR_LOWER", HWIO_PCIE_PCIE_BHI_IMGADDR_LOWER_ADDR, 0},
+	{"PCIE_BHI_IMGADDR_UPPER", HWIO_PCIE_PCIE_BHI_IMGADDR_UPPER_ADDR, 0},
+	{"PCIE_BHI_IMGSIZE", HWIO_PCIE_PCIE_BHI_IMGSIZE_ADDR, 0},
+	{"PCIE_BHI_IMGTXDB", HWIO_PCIE_PCIE_BHI_IMGTXDB_ADDR, 0},
+	{"PCIE_BHI_INTVEC", HWIO_PCIE_PCIE_BHI_INTVEC_ADDR, 0},
+	{"PCIE_BHI_EXECENV", HWIO_PCIE_PCIE_BHI_EXECENV_ADDR, 0},
+	{"PCIE_BHI_STATUS", HWIO_PCIE_PCIE_BHI_STATUS_ADDR, 0},
+	{"PCIE_BHI_ERRCODE", HWIO_PCIE_PCIE_BHI_ERRCODE_ADDR, 0},
+	{"PCIE_BHI_ERRDBG1", HWIO_PCIE_PCIE_BHI_ERRDBG1_ADDR, 0},
+	{"PCIE_BHI_ERRDBG2", HWIO_PCIE_PCIE_BHI_ERRDBG2_ADDR, 0},
+	{"PCIE_BHI_ERRDBG3", HWIO_PCIE_PCIE_BHI_ERRDBG3_ADDR, 0},
+	{"PCIE_BHI_SERIALNUM", HWIO_PCIE_PCIE_BHI_SERIALNUM_ADDR, 0},
+	{"PCIE_BHI_SBLANTIROLLVER", HWIO_PCIE_PCIE_BHI_SBLANTIROLLVER_ADDR, 0},
+	{"PCIE_BHI_NUMSEG", HWIO_PCIE_PCIE_BHI_NUMSEG_ADDR, 0},
+	{"PCIE_BHI_MSMHWID_0", HWIO_PCIE_PCIE_BHI_MSMHWID_0_ADDR, 0},
+	{"PCIE_BHI_MSMHWID_1", HWIO_PCIE_PCIE_BHI_MSMHWID_1_ADDR, 0},
+	{"PCIE_BHI_MSMHWID_2", HWIO_PCIE_PCIE_BHI_MSMHWID_2_ADDR, 0},
+	{"PCIE_BHI_MSMHWID_3", HWIO_PCIE_PCIE_BHI_MSMHWID_3_ADDR, 0},
+	{"PCIE_BHI_MSMHWID_4", HWIO_PCIE_PCIE_BHI_MSMHWID_4_ADDR, 0},
+	{"PCIE_BHI_MSMHWID_5", HWIO_PCIE_PCIE_BHI_MSMHWID_5_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_0", HWIO_PCIE_PCIE_BHI_OEMPKHASH_0_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_1", HWIO_PCIE_PCIE_BHI_OEMPKHASH_1_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_2", HWIO_PCIE_PCIE_BHI_OEMPKHASH_2_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_3", HWIO_PCIE_PCIE_BHI_OEMPKHASH_3_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_4", HWIO_PCIE_PCIE_BHI_OEMPKHASH_4_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_5", HWIO_PCIE_PCIE_BHI_OEMPKHASH_5_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_6", HWIO_PCIE_PCIE_BHI_OEMPKHASH_6_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_7", HWIO_PCIE_PCIE_BHI_OEMPKHASH_7_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_8", HWIO_PCIE_PCIE_BHI_OEMPKHASH_8_ADDR, 0},
+	{"PCIE_BHI_OEMPKHASH_9", HWIO_PCIE_PCIE_BHI_OEMPKHASH_9_ADDR, 0},
+	{"PCIE_BHI_TXVECDB", HWIO_PCIE_PCIE_BHI_TXVECDB_ADDR, 0},
+	{"PCIE_BHI_TXVECSTATUS", HWIO_PCIE_PCIE_BHI_TXVECSTATUS_ADDR, 0},
+	{"PCIE_BHI_RXVECDB", HWIO_PCIE_PCIE_BHI_RXVECDB_ADDR, 0},
+	{"PCIE_BHI_RXVECSTATUS", HWIO_PCIE_PCIE_BHI_RXVECSTATUS_ADDR, 0},
+	/* After dump this register, recovery will fail for QCA6490 */
+	//{"PCIE_WLAON_RESET_DBG_SW_ENTRY", WLAON_RESET_DBG_SW_ENTRY, 0},
+	{NULL},
+};
+
+static struct cnss_pci_reg register_to_dump[] = {
+	{"QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL", QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL, 0},
+	{"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF, 0},
+	{"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0, 0},
+	{"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1, 0},
+	{"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0, 0},
+	{"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1, 0},
+	{"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2, 0},
+	{NULL},
+};
+
+#define LINE_LEN_MAX 80
+
+static int ath11k_register_dump(struct ath11k_base *ab,
+				u8 **buf,
+				size_t *buf_len,
+				struct cnss_pci_reg *regs,
+				size_t num)
+{
+	u32 i;
+	u32 offset = 0;
+	size_t line_len;
+	ssize_t len;
+	char (*line_ptr)[LINE_LEN_MAX];
+
+	*buf_len = 0;
+
+	line_ptr = (char (*)[LINE_LEN_MAX])vzalloc(num * LINE_LEN_MAX);
+	if (!line_ptr)
+		return -ENOMEM;
+
+	for (i = 0; regs[i].name; i++) {
+		regs[i].value = ath11k_pcic_read32(ab, regs[i].offset);
+		ath11k_info(ab, "%s[0x%x] = 0x%x\n",
+			    regs[i].name, regs[i].offset, regs[i].value);
+		len = snprintf(line_ptr[i], LINE_LEN_MAX, "%s[0x%x] = 0x%x\n",
+			       regs[i].name, regs[i].offset, regs[i].value);
+		*buf_len += len;
+	}
+
+	ath11k_info(ab, "%s buf len=%lu\n", __func__, *buf_len);
+	*buf = vzalloc(*buf_len);
+	if (!*buf)
+		return -ENOMEM;
+
+	for (i = 0; i < num; ++i) {
+		line_len = strlen(line_ptr[i]);
+		memcpy(*buf + offset, line_ptr[i], line_len);
+		offset += line_len;
+	}
+
+	vfree(line_ptr);
+
+	return 0;
+}
+
+void ath11k_pci_register_dump(struct ath11k_pci *ab_pci)
+{
+	size_t num;
+	struct register_crash_data *crash_data = &ab_pci->reg_data;
+
+	num = sizeof(register_to_dump) / sizeof(struct cnss_pci_reg) - 1;
+	ath11k_register_dump(ab_pci->ab,
+			     &crash_data->reg_buf,
+			     &crash_data->reg_buf_len,
+			     register_to_dump,
+			     num);
+
+	num = sizeof(register_rddm_fail_debug) / sizeof(struct cnss_pci_reg) - 1;
+	ath11k_register_dump(ab_pci->ab,
+			     &crash_data->reg_rddm_buf,
+			     &crash_data->reg_rddm_buf_len,
+			     register_rddm_fail_debug,
+			     num);
+}
+
 static int ath11k_pci_bus_wake_up(struct ath11k_base *ab)
 {
 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
@@ -107,7 +228,12 @@ static u32 ath11k_pci_window_read32(struct ath11k_base *ab, u32 offset)
 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
 	u32 window_start, val;
 
-	window_start = ath11k_pci_get_window_start(ab, offset);
+	if (ab->hw_params.static_window_map)
+        	window_start = ath11k_pci_get_window_start(ab, offset);
+      	else
+        	window_start = ATH11K_PCI_WINDOW_START;
+
+	//window_start = ath11k_pci_get_window_start(ab, offset);
 
 	if (window_start == ATH11K_PCI_WINDOW_START) {
 		spin_lock_bh(&ab_pci->window_lock);
@@ -667,19 +793,21 @@ static int ath11k_pci_start(struct ath11k_base *ab)
 {
 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
 
-	/* TODO: for now don't restore ASPM in case of single MSI
-	 * vector as MHI register reading in M2 causes system hang.
-	 */
-	if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
-		ath11k_pci_aspm_restore(ab_pci);
-	else
-		ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n");
+	ath11k_pci_aspm_restore(ab_pci);
 
 	ath11k_pcic_start(ab);
 
 	return 0;
 }
 
+static int ath11k_pci_force_rddm(struct ath11k_base *ab)
+{
+	struct ath11k_pci *ar_pci;
+
+	ar_pci = ath11k_pci_priv(ab);
+	return ath11k_mhi_force_rddm(ar_pci);
+}
+
 static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
 	.start = ath11k_pci_start,
 	.stop = ath11k_pcic_stop,
@@ -698,6 +826,7 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
 	.ce_irq_enable = ath11k_pci_hif_ce_irq_enable,
 	.ce_irq_disable = ath11k_pci_hif_ce_irq_disable,
 	.get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx,
+	.target_crash = ath11k_pci_force_rddm,
 };
 
 static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor)
@@ -731,6 +860,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 	u32 soc_hw_version_major, soc_hw_version_minor, addr;
 	const struct ath11k_pci_ops *pci_ops;
 	int ret;
+	u32 sub_version;
+	int ops_init = 0;
 
 	ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI);
 
@@ -746,8 +877,10 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 	ab_pci->ab = ab;
 	ab_pci->pdev = pdev;
 	ab->hif.ops = &ath11k_pci_hif_ops;
+	ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
 	pci_set_drvdata(pdev, ab);
 	spin_lock_init(&ab_pci->window_lock);
+	INIT_WORK(&ab_pci->rddm_worker, ath11k_mhi_pm_rddm_worker);
 
 	/* Set fixed_mem_region to true for platforms support reserved memory
 	 * from DT. If memory is reserved from DT for FW, ath11k driver need not
@@ -776,8 +909,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 	case QCA6390_DEVICE_ID:
 		ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
 					   &soc_hw_version_minor);
-		switch (soc_hw_version_major) {
-		case 2:
+ 		switch (soc_hw_version_major) {
+ 		case 2:
 			ab->hw_rev = ATH11K_HW_QCA6390_HW20;
 			break;
 		default:
@@ -797,6 +930,13 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 		ab->id.bdf_search = ATH11K_BDF_SEARCH_BUS_AND_BOARD;
 		ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
 					   &soc_hw_version_minor);
+		pci_ops = &ath11k_pci_ops_qca6390;
+		ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
+		if (ret) {
+			ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
+			goto err_pci_free_region;
+		}
+		ops_init = 1;
 		switch (soc_hw_version_major) {
 		case 2:
 			switch (soc_hw_version_minor) {
@@ -806,7 +946,19 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 				break;
 			case 0x10:
 			case 0x11:
-				ab->hw_rev = ATH11K_HW_WCN6855_HW21;
+				//ab->hw_rev = ATH11K_HW_WCN6855_HW21;
+				sub_version = ath11k_pcic_read32(ab, SUB_VERSION);
+				ath11k_dbg(ab, ATH11K_DBG_PCI, "sub_version 0x%x\n", sub_version);
+				switch (sub_version) {
+				case 0x1019A0E1:
+				case 0x1019B0E1:
+				case 0x1019C0E1:
+				case 0x1019D0E1:
+					ab->hw_rev = ATH11K_HW_QCA206X_HW21;
+					break;
+				default:
+					ab->hw_rev = ATH11K_HW_WCN6855_HW21;
+				}
 				break;
 			default:
 				goto unsupported_wcn6855_soc;
@@ -820,7 +972,6 @@ unsupported_wcn6855_soc:
 			goto err_pci_free_region;
 		}
 
-		pci_ops = &ath11k_pci_ops_qca6390;
 		break;
 	default:
 		dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n",
@@ -829,11 +980,13 @@ unsupported_wcn6855_soc:
 		goto err_pci_free_region;
 	}
 
-	ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
-	if (ret) {
-		ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
-		goto err_pci_free_region;
-	}
+	if(ops_init == 1){
+		ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
+		if (ret) {
+			ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
+			goto err_pci_free_region;
+		}
+ 	}
 
 	ret = ath11k_pcic_init_msi_config(ab);
 	if (ret) {
@@ -851,10 +1004,16 @@ unsupported_wcn6855_soc:
 	if (ret)
 		goto err_pci_disable_msi;
 
+	ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0));
+	if (ret) {
+		ath11k_err(ab, "failed to set irq affinity %d\n", ret);
+		goto err_pci_disable_msi;
+	}
+
 	ret = ath11k_mhi_register(ab_pci);
 	if (ret) {
 		ath11k_err(ab, "failed to register mhi: %d\n", ret);
-		goto err_pci_disable_msi;
+		goto err_irq_affinity_cleanup;
 	}
 
 	ret = ath11k_hal_srng_init(ab);
@@ -875,12 +1034,6 @@ unsupported_wcn6855_soc:
 		goto err_ce_free;
 	}
 
-	ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0));
-	if (ret) {
-		ath11k_err(ab, "failed to set irq affinity %d\n", ret);
-		goto err_free_irq;
-	}
-
 	/* kernel may allocate a dummy vector before request_irq and
 	 * then allocate a real vector when request_irq is called.
 	 * So get msi_data here again to avoid spurious interrupt
@@ -889,19 +1042,16 @@ unsupported_wcn6855_soc:
 	ret = ath11k_pci_config_msi_data(ab_pci);
 	if (ret) {
 		ath11k_err(ab, "failed to config msi_data: %d\n", ret);
-		goto err_irq_affinity_cleanup;
+		goto err_free_irq;
 	}
 
 	ret = ath11k_core_init(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to init core: %d\n", ret);
-		goto err_irq_affinity_cleanup;
+		goto err_free_irq;
 	}
 	return 0;
 
-err_irq_affinity_cleanup:
-	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
-
 err_free_irq:
 	ath11k_pcic_free_irq(ab);
 
@@ -914,6 +1064,9 @@ err_hal_srng_deinit:
 err_mhi_unregister:
 	ath11k_mhi_unregister(ab_pci);
 
+err_irq_affinity_cleanup:
+	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
+
 err_pci_disable_msi:
 	ath11k_pci_free_msi(ab_pci);
 
@@ -1017,6 +1170,7 @@ static struct pci_driver ath11k_pci_driver = {
 static int ath11k_pci_init(void)
 {
 	int ret;
+	u32 sub_version;
 
 	ret = pci_register_driver(&ath11k_pci_driver);
 	if (ret)
diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h
index e9a01f344ec6..d39c8d3cf8e4 100644
--- a/drivers/net/wireless/ath/ath11k/pci.h
+++ b/drivers/net/wireless/ath/ath11k/pci.h
@@ -9,11 +9,13 @@
 #include <linux/mhi.h>
 
 #include "core.h"
+#include "coredump_fw.h"
 
 #define PCIE_SOC_GLOBAL_RESET			0x3008
 #define PCIE_SOC_GLOBAL_RESET_V			1
 
 #define WLAON_WARM_SW_ENTRY			0x1f80504
+#define WLAON_RESET_DBG_SW_ENTRY		0x01F80508
 #define WLAON_SOC_RESET_CAUSE_REG		0x01f8060c
 
 #define PCIE_Q6_COOKIE_ADDR			0x01f80500
@@ -53,6 +55,65 @@
 #define WLAON_QFPROM_PWR_CTRL_REG		0x01f8031c
 #define QFPROM_PWR_CTRL_VDD4BLOW_MASK		0x4
 
+#define HWIO_PCIE_PCIE_BHI_VERSION_LOWER_ADDR           (0x1e0e200)
+#define HWIO_PCIE_PCIE_BHI_VERSION_UPPER_ADDR           (0x1e0e204)
+#define HWIO_PCIE_PCIE_BHI_IMGADDR_LOWER_ADDR           (0x1e0e208)
+#define HWIO_PCIE_PCIE_BHI_IMGADDR_UPPER_ADDR           (0x1e0e20c)
+#define HWIO_PCIE_PCIE_BHI_IMGSIZE_ADDR                 (0x1e0e210)
+#define HWIO_PCIE_PCIE_BHI_IMGTXDB_ADDR                 (0x1e0e218)
+#define HWIO_PCIE_PCIE_BHI_INTVEC_ADDR                  (0x1e0e220)
+#define HWIO_PCIE_PCIE_BHI_EXECENV_ADDR                 (0x1e0e228)
+#define HWIO_PCIE_PCIE_BHI_STATUS_ADDR                  (0x1e0e22c)
+#define HWIO_PCIE_PCIE_BHI_ERRCODE_ADDR                 (0x1e0e230)
+#define HWIO_PCIE_PCIE_BHI_ERRDBG1_ADDR                 (0x1e0e234)
+#define HWIO_PCIE_PCIE_BHI_ERRDBG2_ADDR                 (0x1e0e238)
+#define HWIO_PCIE_PCIE_BHI_ERRDBG3_ADDR                 (0x1e0e23c)
+#define HWIO_PCIE_PCIE_BHI_SERIALNUM_ADDR               (0x1e0e240)
+#define HWIO_PCIE_PCIE_BHI_SBLANTIROLLVER_ADDR          (0x1e0e244)
+#define HWIO_PCIE_PCIE_BHI_NUMSEG_ADDR                  (0x1e0e248)
+#define HWIO_PCIE_PCIE_BHI_MSMHWID_0_ADDR               (0x1e0e24c)
+#define HWIO_PCIE_PCIE_BHI_MSMHWID_1_ADDR               (0x1e0e250)
+#define HWIO_PCIE_PCIE_BHI_MSMHWID_2_ADDR               (0x1e0e254)
+#define HWIO_PCIE_PCIE_BHI_MSMHWID_3_ADDR               (0x1e0e258)
+#define HWIO_PCIE_PCIE_BHI_MSMHWID_4_ADDR               (0x1e0e25c)
+#define HWIO_PCIE_PCIE_BHI_MSMHWID_5_ADDR               (0x1e0e260)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_0_ADDR             (0x1e0e264)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_1_ADDR             (0x1e0e268)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_2_ADDR             (0x1e0e26c)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_3_ADDR             (0x1e0e270)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_4_ADDR             (0x1e0e274)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_5_ADDR             (0x1e0e278)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_6_ADDR             (0x1e0e27c)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_7_ADDR             (0x1e0e280)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_8_ADDR             (0x1e0e284)
+#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_9_ADDR             (0x1e0e288)
+#define HWIO_PCIE_PCIE_BHI_TXVECDB_ADDR                 (0x1e0e360)
+#define HWIO_PCIE_PCIE_BHI_TXVECSTATUS_ADDR             (0x1e0e368)
+#define HWIO_PCIE_PCIE_BHI_RXVECDB_ADDR                 (0x1e0e394)
+#define HWIO_PCIE_PCIE_BHI_RXVECSTATUS_ADDR             (0x1e0e39c)
+
+#define QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL  (0x1C0106C)
+#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF  (0x1C01070)
+#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0  (0x1C01074)
+#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1  (0x1C01078)
+#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0 (0x00DA102C)
+#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1 (0x00DA202C)
+#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2 (0x00DA302C)
+
+struct cnss_pci_reg {
+	char *name;
+	u32 offset;
+	u32 value;
+};
+
+struct register_crash_data {
+	u8 *reg_buf;
+	size_t reg_buf_len;
+	u8 *reg_rddm_buf;
+	size_t reg_rddm_buf_len;
+};
+
+
 enum ath11k_pci_flags {
 	ATH11K_PCI_ASPM_RESTORE,
 };
@@ -72,6 +133,10 @@ struct ath11k_pci {
 	/* enum ath11k_pci_flags */
 	unsigned long flags;
 	u16 link_ctl;
+	struct register_crash_data reg_data;
+	struct ath11k_mhi_fw_crash_data mhi_fw_crash_data;
+	struct work_struct rddm_worker;
+
 };
 
 static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
@@ -80,4 +145,5 @@ static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
 }
 
 int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector);
+void ath11k_pci_register_dump(struct ath11k_pci *ab_pci);
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c
index 380f9d37b644..b2d740313ce0 100644
--- a/drivers/net/wireless/ath/ath11k/pcic.c
+++ b/drivers/net/wireless/ath/ath11k/pcic.c
@@ -115,6 +115,17 @@ static const struct ath11k_msi_config ath11k_msi_config[] = {
 		},
 		.hw_rev = ATH11K_HW_WCN6750_HW10,
 	},
+	{
+		.total_vectors = 32,
+		.total_users = 4,
+		.users = (struct ath11k_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_QCA206X_HW21,
+	},
 };
 
 int ath11k_pcic_init_msi_config(struct ath11k_base *ab)
@@ -218,9 +229,16 @@ int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)
 	if (wakeup_required && ab->pci.ops->wakeup) {
 		ret = ab->pci.ops->wakeup(ab);
 		if (ret) {
-			ath11k_warn(ab, "failed to wakeup for read from 0x%x: %d\n",
-				    start, ret);
-			return ret;
+			ath11k_warn(ab,
+				    "wakeup failed, data may be invalid: %d",
+				    ret);
+			/* Even though wakeup() failed, continue processing rather
+			 * than returning because some parts of the data may still
+			 * be valid and useful in some cases, e.g. could give us
+			 * some clues on firmware crash.
+			 * Mislead due to invalid data could be avoided because we
+			 * are aware of the wakeup failure.
+			 */
 		}
 	}
 
@@ -453,18 +471,17 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
 {
 	int i;
 
-	set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
-
 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
 
 		if (!irq_grp->napi_enabled) {
-			dev_set_threaded(&irq_grp->napi_ndev, true);
 			napi_enable(&irq_grp->napi);
 			irq_grp->napi_enabled = true;
 		}
 		ath11k_pcic_ext_grp_enable(irq_grp);
 	}
+
+	set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
 }
 EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);
 
@@ -592,7 +609,10 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
 			ath11k_dbg(ab, ATH11K_DBG_PCI,
 				   "irq:%d group:%d\n", irq, i);
 
-			irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
+			if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
+				irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY | IRQ_MOVE_PCNTXT);
+			else
+				irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
 			ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,
 					  irq_flags, "DP_EXT_IRQ", irq_grp);
 			if (ret) {
@@ -642,6 +662,8 @@ int ath11k_pcic_config_irq(struct ath11k_base *ab)
 
 		tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);
 
+		if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
+			irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
 		ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,
 				  irq_flags, irq_name[irq_idx], ce_pipe);
 		if (ret) {
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index 01b02c03fa89..f85b7563fa9e 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -1971,6 +1971,51 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab)
 	}
 }
 
+static size_t ath11k_qmi_get_remote_buf_len(struct fw_remote_mem *fw_mem)
+{
+	unsigned int i;
+	size_t len = 0;
+
+	for (i = 0; i < ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01; i++) {
+		if (fw_mem[i].vaddr && fw_mem[i].size)
+			len += fw_mem[i].size;
+	}
+	return len;
+}
+
+int ath11k_qmi_remote_dump(struct ath11k_base *ab)
+{
+	struct fw_remote_crash_data *crash_data = &ab->remote_crash_data;
+	struct fw_remote_mem *fw_mem = ab->remote_mem;
+	u8 i;
+	u32 offset = 0;
+
+	crash_data->remote_buf_len = ath11k_qmi_get_remote_buf_len(fw_mem);
+	ath11k_info(ab, "%s remote buffer len=%lu\n", __func__, crash_data->remote_buf_len);
+	crash_data->remote_buf = vzalloc(crash_data->remote_buf_len);
+	if (!crash_data->remote_buf)
+		return -ENOMEM;
+
+	for (i = 0; i < ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01; i++) {
+		if (fw_mem[i].vaddr && fw_mem[i].size) {
+			ath11k_info(ab, "remote mem: 0x%p, size: 0x%lx\n", fw_mem[i].vaddr,
+				    fw_mem[i].size);
+			memcpy(crash_data->remote_buf + offset, fw_mem[i].vaddr, fw_mem[i].size);
+			offset += fw_mem[i].size;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ath11k_qmi_remote_dump);
+
+static void ath11k_qmi_set_remote_mem(struct fw_remote_mem *fw_mem,
+				      void *vaddr, size_t size,
+				      uint32_t segnum)
+{
+	fw_mem[segnum].vaddr = vaddr;
+	fw_mem[segnum].size = size;
+}
+
 static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
 {
 	int i;
@@ -1995,10 +2040,10 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
 			chunk->vaddr = NULL;
 		}
 
-		chunk->vaddr = dma_alloc_coherent(ab->dev,
+		chunk->vaddr = ath11k_core_dma_alloc_coherent(ab->dev,
 						  chunk->size,
 						  &chunk->paddr,
-						  GFP_KERNEL | __GFP_NOWARN);
+						  GFP_KERNEL | __GFP_NOWARN | GFP_DMA32);
 		if (!chunk->vaddr) {
 			if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) {
 				ath11k_dbg(ab, ATH11K_DBG_QMI,
@@ -2015,6 +2060,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
 				   chunk->type);
 			return -EINVAL;
 		}
+
+		if (chunk->type == QMI_WLANFW_MEM_TYPE_DDR_V01) {
+			ath11k_qmi_set_remote_mem(ab->remote_mem,
+						  chunk->vaddr,
+						  chunk->size,
+						  i);
+			ath11k_info(ab, "vaddr=0x%p size=0x%x\n", chunk->vaddr, chunk->size);
+		}
+
 		chunk->prev_type = chunk->type;
 		chunk->prev_size = chunk->size;
 	}
@@ -2510,9 +2564,9 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab)
 	if (m3_mem->vaddr || m3_mem->size)
 		goto skip_m3_alloc;
 
-	m3_mem->vaddr = dma_alloc_coherent(ab->dev,
+	m3_mem->vaddr = ath11k_core_dma_alloc_coherent(ab->dev,
 					   fw->size, &m3_mem->paddr,
-					   GFP_KERNEL);
+					   GFP_KERNEL  | GFP_DMA32);
 	if (!m3_mem->vaddr) {
 		ath11k_err(ab, "failed to allocate memory for M3 with size %zu\n",
 			   fw->size);
@@ -3169,6 +3223,9 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work)
 		case ATH11K_QMI_EVENT_SERVER_EXIT:
 			set_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags);
 			set_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags);
+
+			if (!ab->is_reset)
+				ath11k_core_pre_reconfigure_recovery(ab);
 			break;
 		case ATH11K_QMI_EVENT_REQUEST_MEM:
 			ret = ath11k_qmi_event_mem_request(qmi);
@@ -3269,6 +3326,9 @@ int ath11k_qmi_init_service(struct ath11k_base *ab)
 	spin_lock_init(&ab->qmi.event_lock);
 	INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work);
 
+	memset(ab->remote_mem, 0,
+	       sizeof(struct fw_remote_mem) * ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01);
+
 	ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01,
 			     ATH11K_QMI_WLFW_SERVICE_VERS_V01,
 			     ab->qmi.service_ins_id);
diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h
index 0909d53cefeb..e4fa39ddbdfc 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.h
+++ b/drivers/net/wireless/ath/ath11k/qmi.h
@@ -519,5 +519,6 @@ void ath11k_qmi_msg_recv_work(struct work_struct *work);
 void ath11k_qmi_deinit_service(struct ath11k_base *ab);
 int ath11k_qmi_init_service(struct ath11k_base *ab);
 void ath11k_qmi_free_resource(struct ath11k_base *ab);
+int ath11k_qmi_remote_dump(struct ath11k_base *ab);
 
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 6fae4e61ede7..9f28c82c2acb 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -55,6 +55,17 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 	ath11k_dbg(ar->ab, ATH11K_DBG_REG,
 		   "Regulatory Notification received for %s\n", wiphy_name(wiphy));
 
+	if ((request->initiator == NL80211_REGDOM_SET_BY_DRIVER) &&
+	    (ar->state == ATH11K_STATE_ON)) {
+		ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+			   "dynamically updated by driver\n");
+		ret = ath11k_reg_update_chan_list(ar, true);
+		if (ret)
+			ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret);
+
+		return;
+	}
+
 	/* Currently supporting only General User Hints. Cell base user
 	 * hints to be handled later.
 	 * Hints from other sources like Core, Beacons are not expected for
@@ -112,32 +123,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
 	struct channel_param *ch;
 	enum nl80211_band band;
 	int num_channels = 0;
-	int i, ret, left;
-
-	if (wait && ar->state_11d != ATH11K_11D_IDLE) {
-		left = wait_for_completion_timeout(&ar->completed_11d_scan,
-						   ATH11K_SCAN_TIMEOUT_HZ);
-		if (!left) {
-			ath11k_dbg(ar->ab, ATH11K_DBG_REG,
-				   "failed to receive 11d scan complete: timed out\n");
-			ar->state_11d = ATH11K_11D_IDLE;
-		}
-		ath11k_dbg(ar->ab, ATH11K_DBG_REG,
-			   "reg 11d scan wait left time %d\n", left);
-	}
-
-	if (wait &&
-	    (ar->scan.state == ATH11K_SCAN_STARTING ||
-	    ar->scan.state == ATH11K_SCAN_RUNNING)) {
-		left = wait_for_completion_timeout(&ar->scan.completed,
-						   ATH11K_SCAN_TIMEOUT_HZ);
-		if (!left)
-			ath11k_dbg(ar->ab, ATH11K_DBG_REG,
-				   "failed to receive hw scan complete: timed out\n");
-
-		ath11k_dbg(ar->ab, ATH11K_DBG_REG,
-			   "reg hw scan wait left time %d\n", left);
-	}
+	int i, ret = 0;
 
 	if (ar->state == ATH11K_STATE_RESTARTING)
 		return 0;
@@ -219,8 +205,15 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
 		}
 	}
 
-	ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
-	kfree(params);
+	if (wait) {
+		spin_lock_bh(&ar->channel_update_lock);
+		list_add_tail(&params->list, &ar->channel_update_queue);
+		spin_unlock_bh(&ar->channel_update_lock);
+		queue_work(ar->ab->workqueue, &ar->channel_update_work);
+	} else {
+		ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
+		kfree(params);
+	}
 
 	return ret;
 }
@@ -294,12 +287,6 @@ int ath11k_regd_update(struct ath11k *ar)
 	if (ret)
 		goto err;
 
-	if (ar->state == ATH11K_STATE_ON) {
-		ret = ath11k_reg_update_chan_list(ar, true);
-		if (ret)
-			goto err;
-	}
-
 	return 0;
 err:
 	ath11k_warn(ab, "failed to perform regd update : %d\n", ret);
@@ -413,6 +400,10 @@ static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,
 
 	/* Use the flags of both the rules */
 	new_rule->flags = rule1->flags | rule2->flags;
+	if ((rule1->flags & NL80211_RRF_PSD) && (rule2->flags & NL80211_RRF_PSD))
+		new_rule->psd = min_t(s8, rule1->psd, rule2->psd);
+	else
+		new_rule->flags &= ~NL80211_RRF_PSD;
 
 	/* To be safe, lts use the max cac timeout of both rules */
 	new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
@@ -516,13 +507,14 @@ ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw)
 static void
 ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq,
 		       u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr,
-		       u32 reg_flags)
+		       s8 psd, u32 reg_flags)
 {
 	reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq);
 	reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq);
 	reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw);
 	reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain);
 	reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr);
+	reg_rule->psd = psd;
 	reg_rule->flags = reg_flags;
 }
 
@@ -552,7 +544,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
 				       reg_rule->start_freq,
 				       ETSI_WEATHER_RADAR_BAND_LOW, bw,
 				       reg_rule->ant_gain, reg_rule->reg_power,
-				       flags);
+				       reg_rule->psd_eirp, flags);
 
 		ath11k_dbg(ab, ATH11K_DBG_REG,
 			   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
@@ -573,7 +565,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
 
 		ath11k_reg_update_rule(regd->reg_rules + i, start_freq,
 				       end_freq, bw, reg_rule->ant_gain,
-				       reg_rule->reg_power, flags);
+				       reg_rule->reg_power, reg_rule->psd_eirp, flags);
 
 		regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT;
 
@@ -594,7 +586,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
 				       ETSI_WEATHER_RADAR_BAND_HIGH,
 				       reg_rule->end_freq, bw,
 				       reg_rule->ant_gain, reg_rule->reg_power,
-				       flags);
+				       reg_rule->psd_eirp, flags);
 
 		ath11k_dbg(ab, ATH11K_DBG_REG,
 			   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
@@ -607,19 +599,64 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
 	*rule_idx = i;
 }
 
+enum wmi_reg_6ghz_ap_type
+ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type)
+{
+	switch (power_type) {
+	case IEEE80211_REG_LPI_AP:
+		return WMI_REG_INDOOR_AP;
+	case IEEE80211_REG_SP_AP:
+		return WMI_REG_STANDARD_POWER_AP;
+	case IEEE80211_REG_VLP_AP:
+		return WMI_REG_VERY_LOW_POWER_AP;
+	default:
+		return WMI_REG_MAX_AP_TYPE;
+	}
+}
+
 struct ieee80211_regdomain *
 ath11k_reg_build_regd(struct ath11k_base *ab,
-		      struct cur_regulatory_info *reg_info, bool intersect)
+		      struct cur_regulatory_info *reg_info, bool intersect,
+		      enum wmi_vdev_type vdev_type,
+		      enum ieee80211_ap_reg_power power_type)
 {
 	struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
-	struct cur_reg_rule *reg_rule;
-	u8 i = 0, j = 0;
+	struct cur_reg_rule *reg_rule, *reg_rule_6ghz;
+	u8 i = 0, j = 0, k = 0;
 	u8 num_rules;
 	u16 max_bw;
-	u32 flags;
+	u32 flags, reg_6ghz_number, max_bw_6ghz;
 	char alpha2[3];
 
-	num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules;
+	num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules;
+
+	if (reg_info->is_ext_reg_event) {
+		if (vdev_type == WMI_VDEV_TYPE_STA) {
+			enum wmi_reg_6ghz_ap_type ap_type;
+
+			ap_type = ath11k_ieee80211_ap_pwr_type_convert(power_type);
+
+			if (ap_type == WMI_REG_MAX_AP_TYPE)
+				ap_type = WMI_REG_INDOOR_AP;
+			reg_6ghz_number = reg_info->num_6ghz_rules_client
+					[ap_type][WMI_REG_DEFAULT_CLIENT];
+			if (reg_6ghz_number == 0) {
+				ap_type = WMI_REG_INDOOR_AP;
+				reg_6ghz_number = reg_info->num_6ghz_rules_client
+						[ap_type][WMI_REG_DEFAULT_CLIENT];
+			}
+			reg_rule_6ghz = reg_info->reg_rules_6ghz_client_ptr
+					[ap_type][WMI_REG_DEFAULT_CLIENT];
+			max_bw_6ghz = reg_info->max_bw_6ghz_client
+					[ap_type][WMI_REG_DEFAULT_CLIENT];
+		} else {
+			reg_6ghz_number = reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP];
+			reg_rule_6ghz =
+				reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP];
+			max_bw_6ghz = reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP];
+		}
+		num_rules += reg_6ghz_number;
+	}
 
 	if (!num_rules)
 		goto ret;
@@ -640,24 +677,24 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
 	tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region);
 
 	ath11k_dbg(ab, ATH11K_DBG_REG,
-		   "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n",
+		   "Country %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n",
 		   alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region),
 		   reg_info->dfs_region, num_rules);
 	/* Update reg_rules[] below. Firmware is expected to
-	 * send these rules in order(2G rules first and then 5G)
+	 * send these rules in order(2 GHz rules first and then 5 GHz)
 	 */
 	for (; i < num_rules; i++) {
-		if (reg_info->num_2g_reg_rules &&
-		    (i < reg_info->num_2g_reg_rules)) {
-			reg_rule = reg_info->reg_rules_2g_ptr + i;
+		if (reg_info->num_2ghz_reg_rules &&
+		    (i < reg_info->num_2ghz_reg_rules)) {
+			reg_rule = reg_info->reg_rules_2ghz_ptr + i;
 			max_bw = min_t(u16, reg_rule->max_bw,
-				       reg_info->max_bw_2g);
+				       reg_info->max_bw_2ghz);
 			flags = 0;
-		} else if (reg_info->num_5g_reg_rules &&
-			   (j < reg_info->num_5g_reg_rules)) {
-			reg_rule = reg_info->reg_rules_5g_ptr + j++;
+		} else if (reg_info->num_5ghz_reg_rules &&
+			   (j < reg_info->num_5ghz_reg_rules)) {
+			reg_rule = reg_info->reg_rules_5ghz_ptr + j++;
 			max_bw = min_t(u16, reg_rule->max_bw,
-				       reg_info->max_bw_5g);
+				       reg_info->max_bw_5ghz);
 
 			/* FW doesn't pass NL80211_RRF_AUTO_BW flag for
 			 * BW Auto correction, we can enable this by default
@@ -666,6 +703,13 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
 			 * per other BW rule flags we pass from here
 			 */
 			flags = NL80211_RRF_AUTO_BW;
+		} else if (reg_info->is_ext_reg_event && reg_6ghz_number &&
+			   (k < reg_6ghz_number)) {
+			reg_rule = reg_rule_6ghz + k++;
+			max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz);
+			flags = NL80211_RRF_AUTO_BW;
+			if (reg_rule->psd_flag)
+				flags |= NL80211_RRF_PSD;
 		} else {
 			break;
 		}
@@ -676,7 +720,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
 				       reg_rule->start_freq,
 				       reg_rule->end_freq, max_bw,
 				       reg_rule->ant_gain, reg_rule->reg_power,
-				       flags);
+				       reg_rule->psd_eirp, flags);
 
 		/* Update dfs cac timeout if the dfs domain is ETSI and the
 		 * new rule covers weather radar band.
@@ -693,12 +737,21 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
 			continue;
 		}
 
-		ath11k_dbg(ab, ATH11K_DBG_REG,
-			   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
-			   i + 1, reg_rule->start_freq, reg_rule->end_freq,
-			   max_bw, reg_rule->ant_gain, reg_rule->reg_power,
-			   tmp_regd->reg_rules[i].dfs_cac_ms,
-			   flags);
+		if (reg_info->is_ext_reg_event) {
+			ath11k_dbg(ab, ATH11K_DBG_REG,
+				   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n",
+				   i + 1, reg_rule->start_freq, reg_rule->end_freq,
+				   max_bw, reg_rule->ant_gain, reg_rule->reg_power,
+				   tmp_regd->reg_rules[i].dfs_cac_ms, flags,
+				   reg_rule->psd_flag, reg_rule->psd_eirp);
+		} else {
+			ath11k_dbg(ab, ATH11K_DBG_REG,
+				   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
+				   i + 1, reg_rule->start_freq, reg_rule->end_freq,
+				   max_bw, reg_rule->ant_gain, reg_rule->reg_power,
+				   tmp_regd->reg_rules[i].dfs_cac_ms,
+				   flags);
+		}
 	}
 
 	tmp_regd->n_reg_rules = i;
@@ -723,6 +776,50 @@ ret:
 	return new_regd;
 }
 
+void ath11k_regd_update_chan_list_work(struct work_struct *work)
+{
+	struct ath11k *ar = container_of(work, struct ath11k,
+					 channel_update_work);
+	struct scan_chan_list_params *params, *tmp;
+	int left;
+
+	spin_lock_bh(&ar->channel_update_lock);
+
+	list_for_each_entry_safe(params, tmp, &ar->channel_update_queue, list) {
+		list_del(&params->list);
+		spin_unlock_bh(&ar->channel_update_lock);
+
+		if (ar->state_11d != ATH11K_11D_IDLE) {
+			left = wait_for_completion_timeout(&ar->completed_11d_scan,
+							   ATH11K_SCAN_TIMEOUT_HZ);
+			if (!left) {
+				ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+					   "failed to receive 11d scan complete: timed out\n");
+				ar->state_11d = ATH11K_11D_IDLE;
+			}
+			ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+				   "reg 11d scan wait left time %d\n", left);
+		}
+
+		if ((ar->scan.state == ATH11K_SCAN_STARTING ||
+		     ar->scan.state == ATH11K_SCAN_RUNNING)) {
+			left = wait_for_completion_timeout(&ar->scan.completed,
+							   ATH11K_SCAN_TIMEOUT_HZ);
+			if (!left)
+				ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+					   "failed to receive hw scan complete: timed out\n");
+
+			ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+				   "reg hw scan wait left time %d\n", left);
+		}
+
+		ath11k_wmi_send_scan_chan_list_cmd(ar, params);
+		kfree(params);
+		spin_lock_bh(&ar->channel_update_lock);
+	}
+	spin_unlock_bh(&ar->channel_update_lock);
+}
+
 void ath11k_regd_update_work(struct work_struct *work)
 {
 	struct ath11k *ar = container_of(work, struct ath11k,
@@ -743,6 +840,7 @@ void ath11k_regd_update_work(struct work_struct *work)
 void ath11k_reg_init(struct ath11k *ar)
 {
 	ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
+	ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER;
 	ar->hw->wiphy->reg_notifier = ath11k_reg_notifier;
 }
 
@@ -750,6 +848,12 @@ void ath11k_reg_free(struct ath11k_base *ab)
 {
 	int i;
 
+	for (i = 0; i < ab->num_radios; i++)
+		ath11k_reg_reset_info(&ab->reg_info_store[i]);
+
+	kfree(ab->reg_info_store);
+	ab->reg_info_store = NULL;
+
 	for (i = 0; i < ab->hw_params.max_radios; i++) {
 		kfree(ab->default_regd[i]);
 		kfree(ab->new_regd[i]);
diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h
index 2f284f26378d..fef5927c622f 100644
--- a/drivers/net/wireless/ath/ath11k/reg.h
+++ b/drivers/net/wireless/ath/ath11k/reg.h
@@ -28,9 +28,14 @@ enum ath11k_dfs_region {
 void ath11k_reg_init(struct ath11k *ar);
 void ath11k_reg_free(struct ath11k_base *ab);
 void ath11k_regd_update_work(struct work_struct *work);
+void ath11k_regd_update_chan_list_work(struct work_struct *work);
 struct ieee80211_regdomain *
 ath11k_reg_build_regd(struct ath11k_base *ab,
-		      struct cur_regulatory_info *reg_info, bool intersect);
+		      struct cur_regulatory_info *reg_info, bool intersect,
+		      enum wmi_vdev_type vdev_type,
+		      enum ieee80211_ap_reg_power power_type);
+enum wmi_reg_6ghz_ap_type
+ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type);
 int ath11k_regd_update(struct ath11k *ar);
 int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait);
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c
index 4bf1931adbaa..5603938f6c36 100644
--- a/drivers/net/wireless/ath/ath11k/testmode.c
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include "testmode.h"
@@ -11,6 +12,9 @@
 #include "core.h"
 #include "testmode_i.h"
 
+#define ATH11K_FTM_SEGHDR_CURRENT_SEQ		GENMASK(3, 0)
+#define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS	GENMASK(7, 4)
+
 static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
 	[ATH11K_TM_ATTR_CMD]		= { .type = NLA_U32 },
 	[ATH11K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
@@ -20,58 +24,163 @@ static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
 	[ATH11K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
 };
 
-/* Returns true if callee consumes the skb and the skb should be discarded.
- * Returns false if skb is not used. Does not sleep.
+static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab)
+{
+	struct ath11k_pdev *pdev;
+	struct ath11k *ar = NULL;
+	int i;
+
+	for (i = 0; i < ab->num_radios; i++) {
+		pdev = &ab->pdevs[i];
+		ar = pdev->ar;
+		if (ar) {
+			if (ar->state == ATH11K_STATE_TM)
+				break;
+		}
+	}
+
+	return ar;
+}
+
+/* This function handles unsegmented events. Data in various events are aggregated
+ * in application layer, this event is unsegmented from host perspective.
  */
-bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb)
+void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
+				     struct sk_buff *skb)
 {
 	struct sk_buff *nl_skb;
-	bool consumed;
-	int ret;
+	struct ath11k *ar;
 
-	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
-		   "testmode event wmi cmd_id %d skb %pK skb->len %d\n",
-		   cmd_id, skb, skb->len);
+	ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
+		   "testmode event wmi cmd_id %d skb length %d\n",
+		   cmd_id, skb->len);
+	ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
 
-	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+	ar = ath11k_tm_get_ar(ab);
+	if (!ar) {
+		ath11k_warn(ab, "testmode event not handled due to invalid pdev\n");
+		return;
+	}
 
 	spin_lock_bh(&ar->data_lock);
 
-	consumed = true;
-
 	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
-						   2 * sizeof(u32) + skb->len,
+						   2 * nla_total_size(sizeof(u32)) +
+						   nla_total_size(skb->len),
 						   GFP_ATOMIC);
 	if (!nl_skb) {
-		ath11k_warn(ar->ab,
+		ath11k_warn(ab,
 			    "failed to allocate skb for testmode wmi event\n");
 		goto out;
 	}
 
-	ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI);
-	if (ret) {
-		ath11k_warn(ar->ab,
-			    "failed to put testmode wmi event cmd attribute: %d\n",
-			    ret);
+	if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) ||
+	    nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
+	    nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) {
+		ath11k_warn(ab, "failed to populate testmode unsegmented event\n");
 		kfree_skb(nl_skb);
 		goto out;
 	}
 
-	ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id);
-	if (ret) {
-		ath11k_warn(ar->ab,
-			    "failed to put testmode wmi even cmd_id: %d\n",
-			    ret);
-		kfree_skb(nl_skb);
+	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+	spin_unlock_bh(&ar->data_lock);
+	return;
+
+out:
+	spin_unlock_bh(&ar->data_lock);
+	ath11k_warn(ab, "Failed to send testmode event to higher layers\n");
+}
+
+/* This function handles segmented events.
+ * Data of various events received from fw is aggregated and
+ * sent to application layer
+ */
+int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
+			    const struct wmi_ftm_event_msg *ftm_msg,
+			    u16 length)
+{
+	struct sk_buff *nl_skb;
+	int ret = 0;
+	struct ath11k *ar;
+	u8 const *buf_pos;
+	u16 datalen;
+	u8 total_segments, current_seq;
+	u32 data_pos;
+	u32 pdev_id;
+
+	ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
+		   "testmode event wmi cmd_id %d ftm event msg %pK datalen %d\n",
+		   cmd_id, ftm_msg, length);
+	ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length);
+	pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id);
+
+	if (pdev_id >= ab->num_radios) {
+		ath11k_warn(ab, "testmode event not handled due to invalid pdev id\n");
+		return -EINVAL;
+	}
+
+	ar = ab->pdevs[pdev_id].ar;
+	if (!ar) {
+		ath11k_warn(ab, "testmode event not handled due to absence of pdev\n");
+		return -ENODEV;
+	}
+
+	current_seq = FIELD_GET(ATH11K_FTM_SEGHDR_CURRENT_SEQ,
+				ftm_msg->seg_hdr.segmentinfo);
+	total_segments = FIELD_GET(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS,
+				   ftm_msg->seg_hdr.segmentinfo);
+	datalen = length - (sizeof(struct wmi_ftm_seg_hdr));
+	buf_pos = ftm_msg->data;
+
+	spin_lock_bh(&ar->data_lock);
+	if (current_seq == 0) {
+		ab->ftm_event_obj.expected_seq = 0;
+		ab->ftm_event_obj.data_pos = 0;
+	}
+
+	data_pos = ab->ftm_event_obj.data_pos;
+
+	if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) {
+		ath11k_warn(ab,
+			    "Invalid event length date_pos[%d] datalen[%d]\n",
+			    data_pos, datalen);
+		ret = -EINVAL;
 		goto out;
 	}
 
-	ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data);
-	if (ret) {
-		ath11k_warn(ar->ab,
-			    "failed to copy skb to testmode wmi event: %d\n",
-			    ret);
+	memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen);
+	data_pos += datalen;
+
+	if (++ab->ftm_event_obj.expected_seq != total_segments) {
+		ab->ftm_event_obj.data_pos = data_pos;
+		ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
+			   "partial data received current_seq[%d], total_seg[%d]\n",
+			    current_seq, total_segments);
+		goto out;
+	}
+
+	ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
+		   "total data length[%d] = [%d]\n",
+		    data_pos, ftm_msg->seg_hdr.len);
+	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
+						   2 * nla_total_size(sizeof(u32)) +
+						   nla_total_size(data_pos),
+						   GFP_ATOMIC);
+	if (!nl_skb) {
+		ath11k_warn(ab,
+			    "failed to allocate skb for testmode wmi event\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD,
+			ATH11K_TM_CMD_WMI_FTM) ||
+	    nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
+	    nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos,
+		    &ab->ftm_event_obj.eventdata[0])) {
+		ath11k_warn(ab, "failed to populate testmode event");
 		kfree_skb(nl_skb);
+		ret = -ENOBUFS;
 		goto out;
 	}
 
@@ -79,14 +188,12 @@ bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb)
 
 out:
 	spin_unlock_bh(&ar->data_lock);
-
-	return consumed;
+	return ret;
 }
 
 static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
 {
 	struct sk_buff *skb;
-	int ret;
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
 		   "testmode cmd get version_major %d version_minor %d\n",
@@ -98,21 +205,50 @@ static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
 	if (!skb)
 		return -ENOMEM;
 
-	ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR,
-			  ATH11K_TESTMODE_VERSION_MAJOR);
-	if (ret) {
+	if (nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR,
+			ATH11K_TESTMODE_VERSION_MAJOR) ||
+	    nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR,
+			ATH11K_TESTMODE_VERSION_MINOR)) {
 		kfree_skb(skb);
-		return ret;
+		return -ENOBUFS;
 	}
 
-	ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR,
-			  ATH11K_TESTMODE_VERSION_MINOR);
-	if (ret) {
-		kfree_skb(skb);
-		return ret;
+	return cfg80211_testmode_reply(skb);
+}
+
+static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[])
+{
+	int ret;
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, " enter testmode cmd fw start\n");
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state == ATH11K_STATE_TM) {
+		ret = -EALREADY;
+		goto err;
 	}
 
-	return cfg80211_testmode_reply(skb);
+	/* start utf only when the driver is not in use  */
+	if (ar->state != ATH11K_STATE_OFF) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	ar->ab->ftm_event_obj.eventdata =
+		kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);
+	if (!ar->ab->ftm_event_obj.eventdata) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ar->state = ATH11K_STATE_TM;
+	ar->ftm_msgref = 0;
+	mutex_unlock(&ar->conf_mutex);
+	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, " enter testmode cmd started\n");
+	return 0;
+err:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
 }
 
 static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[])
@@ -125,11 +261,6 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[])
 
 	mutex_lock(&ar->conf_mutex);
 
-	if (ar->state != ATH11K_STATE_ON) {
-		ret = -ENETDOWN;
-		goto out;
-	}
-
 	if (!tb[ATH11K_TM_ATTR_DATA]) {
 		ret = -EINVAL;
 		goto out;
@@ -142,11 +273,17 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[])
 
 	buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
 	buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
+	if (!buf_len) {
+		ath11k_warn(ar->ab, "No data present in testmode command\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
 	cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]);
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
-		   "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
-		   cmd_id, buf, buf_len);
+		   "testmode cmd wmi cmd_id %d buf length %d\n",
+		   cmd_id, buf_len);
 
 	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
 
@@ -173,10 +310,91 @@ out:
 	return ret;
 }
 
+static int ath11k_tm_cmd_process_ftm(struct ath11k *ar, struct nlattr *tb[])
+{
+	struct ath11k_pdev_wmi *wmi = ar->wmi;
+	struct sk_buff *skb;
+	u32 cmd_id, buf_len, hdr_info;
+	int ret;
+	void *buf;
+	u8 segnumber = 0, seginfo;
+	u16 chunk_len, total_bytes, num_segments;
+	u8 *bufpos;
+	struct wmi_ftm_cmd *ftm_cmd;
+
+	mutex_lock(&ar->conf_mutex);
+	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "ar->state  %d\n", ar->state);
+
+	if (ar->state != ATH11K_STATE_TM) {
+		ret = -ENETDOWN;
+		goto out;
+	}
+
+	if (!tb[ATH11K_TM_ATTR_DATA]) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
+	buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
+	cmd_id = WMI_PDEV_UTF_CMDID;
+	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
+		   "testmode cmd wmi cmd_id %d buffer length %d\n",
+		   cmd_id, buf_len);
+	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
+	bufpos = buf;
+	total_bytes = buf_len;
+	num_segments = total_bytes / MAX_WMI_UTF_LEN;
+
+	if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
+		num_segments++;
+
+	while (buf_len) {
+		if (buf_len > MAX_WMI_UTF_LEN)
+			chunk_len = MAX_WMI_UTF_LEN;    /* MAX message */
+		else
+			chunk_len = buf_len;
+
+		skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len +
+					   sizeof(struct wmi_ftm_cmd)));
+		if (!skb) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
+		hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
+			   FIELD_PREP(WMI_TLV_LEN, (chunk_len +
+				      sizeof(struct wmi_ftm_seg_hdr)));
+		ftm_cmd->tlv_header = hdr_info;
+		ftm_cmd->seg_hdr.len = total_bytes;
+		ftm_cmd->seg_hdr.msgref = ar->ftm_msgref;
+		seginfo = FIELD_PREP(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
+			  FIELD_PREP(ATH11K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
+		ftm_cmd->seg_hdr.segmentinfo = seginfo;
+		segnumber++;
+		memcpy(&ftm_cmd->data, bufpos, chunk_len);
+		ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
+		if (ret) {
+			ath11k_warn(ar->ab, "ftm wmi command fail: %d\n", ret);
+			goto out;
+		}
+
+		buf_len -= chunk_len;
+		bufpos += chunk_len;
+	}
+	++ar->ftm_msgref;
+	ret = 0;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
 int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		  void *data, int len)
 {
 	struct ath11k *ar = hw->priv;
+	struct ath11k_base *ab = ar->ab;
 	struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1];
 	int ret;
 
@@ -191,8 +409,15 @@ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) {
 	case ATH11K_TM_CMD_GET_VERSION:
 		return ath11k_tm_cmd_get_version(ar, tb);
+	case ATH11K_TM_CMD_TESTMODE_START:
+		return ath11k_tm_cmd_testmode_start(ar, tb);
 	case ATH11K_TM_CMD_WMI:
 		return ath11k_tm_cmd_wmi(ar, tb);
+	case ATH11K_TM_CMD_WMI_FTM:
+		set_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags);
+		return ath11k_tm_cmd_process_ftm(ar, tb);
+	case ATH11K_TM_CMD_TESTMODE_STOP:
+		return 0;
 	default:
 		return -EOPNOTSUPP;
 	}
diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h
index aaa122ed9069..a944086cf283 100644
--- a/drivers/net/wireless/ath/ath11k/testmode.h
+++ b/drivers/net/wireless/ath/ath11k/testmode.h
@@ -1,22 +1,34 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include "core.h"
 
 #ifdef CONFIG_NL80211_TESTMODE
 
-bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb);
+void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
+				     struct sk_buff *skb);
+int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
+			    const struct wmi_ftm_event_msg *ftm_msg,
+			    u16 length);
 int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		  void *data, int len);
 
 #else
 
-static inline bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id,
-				       struct sk_buff *skb)
+static inline void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab,
+						   u32 cmd_id,
+						   struct sk_buff *skb)
 {
-	return false;
+}
+
+static inline int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
+					  const struct wmi_ftm_event_msg *msg,
+					  u16 length)
+{
+	return 0;
 }
 
 static inline int ath11k_tm_cmd(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/ath11k/testmode_i.h
index 4bae2a9eeea4..2a03b4029d88 100644
--- a/drivers/net/wireless/ath/ath11k/testmode_i.h
+++ b/drivers/net/wireless/ath/ath11k/testmode_i.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 /* "API" level of the ath11k testmode interface. Bump it after every
@@ -14,6 +15,7 @@
 #define ATH11K_TESTMODE_VERSION_MINOR 0
 
 #define ATH11K_TM_DATA_MAX_LEN		5000
+#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048
 
 enum ath11k_tm_attr {
 	__ATH11K_TM_ATTR_INVALID		= 0,
@@ -47,4 +49,21 @@ enum ath11k_tm_cmd {
 	 * ATH11K_TM_ATTR_DATA.
 	 */
 	ATH11K_TM_CMD_WMI = 1,
+
+	/* Boots the UTF firmware, the netdev interface must be down at the
+	 * time.
+	 */
+	ATH11K_TM_CMD_TESTMODE_START = 2,
+
+	/* Shuts down the UTF firmware and puts the driver back into OFF
+	 * state.
+	 */
+	ATH11K_TM_CMD_TESTMODE_STOP = 3,
+
+	/* The command used to transmit a FTM WMI command to the firmware
+	 * and the event to receive WMI events from the firmware.The data
+	 * received  only contain the payload, Need to add the tlv
+	 * header and send the cmd to fw with commandid WMI_PDEV_UTF_CMDID.
+	 */
+	ATH11K_TM_CMD_WMI_FTM = 4,
 };
diff --git a/drivers/net/wireless/ath/ath11k/unitest.c b/drivers/net/wireless/ath/ath11k/unitest.c
new file mode 100644
index 000000000000..541925af0fd0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/unitest.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/**
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <net/netlink.h>
+#include "debug.h"
+#include "unitest.h"
+
+const struct nla_policy ath11k_unitest_policy[UNITEST_MAX + 1] = {
+	[UNITEST_MODULE_ID] = {.type = NLA_U32},
+	[UNITEST_ARGS_NUM] = {.type = NLA_U32},
+	[UNITEST_ARGS] = {.type = NLA_BINARY,
+		.len = MAX_UNITEST_MEMORY_LEN},
+};
+
+/**
+ * ath11k_unit_test() - send unit test command
+ * @wiphy:    wiphy structure pointer
+ * @wdev:     Wireless device structure pointer
+ * @data:     Pointer to the data received
+ * @data_len: Length of @data
+ *
+ * Return: 0 on success; errno on failure
+ */
+
+int ath11k_unit_test(struct wiphy *wiphy, struct wireless_dev *wdev,
+		     const void *data, int data_len)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath11k *ar = hw->priv;
+	struct ath11k_base *ab = ar->ab;
+	struct nlattr *tb[UNITEST_MAX + 1];
+	int status = 0;
+	u32 module_id, num_args, temp_num_args, len;
+	bool sta_found = false;
+	struct ath11k_vif *arvif;
+	struct unit_test_cmd utest_cmd = {0};
+
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
+		    arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE)
+			continue;
+		sta_found = true;
+		break;
+	}
+	if (!sta_found) {
+		ath11k_warn(ar->ab, "no sta found.");
+		return -EINVAL;
+	}
+
+	utest_cmd.vdev_id = arvif->vdev_id;
+
+	if (nla_parse(tb, UNITEST_MAX, data, data_len, ath11k_unitest_policy, NULL)) {
+		ath11k_warn(ab, "Invalid ATTR");
+		return -EINVAL;
+	}
+
+	if (!tb[UNITEST_MODULE_ID]) {
+		ath11k_warn(ab, "attr unitest module id failed");
+		return -EINVAL;
+	}
+	module_id = nla_get_u32(tb[UNITEST_MODULE_ID]);
+	utest_cmd.module_id = module_id;
+
+	if (!tb[UNITEST_ARGS_NUM]) {
+		ath11k_warn(ab, "attr unitest args num failed");
+		return -EINVAL;
+	}
+	num_args = nla_get_u32(tb[UNITEST_ARGS_NUM]);
+	utest_cmd.num_args = num_args;
+	if (num_args > UNIT_TEST_MAX_NUM_ARGS) {
+		ath11k_warn(ab, "num args exceed");
+		return -EINVAL;
+	}
+
+	if (!tb[UNITEST_ARGS]) {
+		ath11k_warn(ab, "attr unitest args failed");
+		return -EINVAL;
+	}
+	len = nla_len(tb[UNITEST_ARGS]);
+	temp_num_args = len / sizeof(u32);
+	if (num_args != temp_num_args) {
+		ath11k_warn(ab, "num args mismatch");
+		return -EINVAL;
+	}
+	nla_memcpy(utest_cmd.args, tb[UNITEST_ARGS], len);
+
+	status = ath11k_wmi_set_unit_test(ar, &utest_cmd);
+	if (status) {
+		ath11k_warn(ab, "Unable to post unit test message (status-%d)", status);
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/ath11k/unitest.h b/drivers/net/wireless/ath/ath11k/unitest.h
new file mode 100644
index 000000000000..9fee1cd6873b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/unitest.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/**
+ * Copyright (c) 2020  The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _UNIT_TEST_H_
+#define _UNIT_TEST_H_
+
+#define VENDOR_ID	0x001374
+#define QCA_NL80211_VENDOR_UNITEST_SUBCMD	84
+#define MAX_UNITEST_MEMORY_LEN	128
+
+enum qca_wlan_vendor_attr_unit_test {
+	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MODULE_ID,
+	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS_NUM,
+	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX =
+		QCA_WLAN_VENDOR_ATTR_UNIT_TEST_AFTER_LAST - 1,
+};
+
+#define UNITEST_INVALID	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_INVALID
+#define UNITEST_MODULE_ID	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MODULE_ID
+#define UNITEST_ARGS	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS
+#define UNITEST_ARGS_NUM	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS_NUM
+#define UNITEST_MAX	QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX
+
+#define ath11k_unit_test_command \
+{\
+	.info.vendor_id = VENDOR_ID,\
+	.info.subcmd = QCA_NL80211_VENDOR_UNITEST_SUBCMD,\
+	.flags = WIPHY_VENDOR_CMD_NEED_WDEV |\
+		WIPHY_VENDOR_CMD_NEED_NETDEV |\
+		WIPHY_VENDOR_CMD_NEED_RUNNING,\
+	.doit = ath11k_unit_test,\
+	.policy = VENDOR_CMD_RAW_DATA,\
+	.maxattr = QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX,\
+}
+
+int ath11k_unit_test(struct wiphy *wiphy, struct wireless_dev *wdev,
+		     const void *data, int data_len);
+#endif
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 3e0a47f4a3eb..74fe5633936f 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021, 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #include <linux/skbuff.h>
 #include <linux/ctype.h>
@@ -19,6 +19,7 @@
 #include "mac.h"
 #include "hw.h"
 #include "peer.h"
+#include "testmode.h"
 
 struct wmi_tlv_policy {
 	size_t min_len;
@@ -82,6 +83,12 @@ struct wmi_tlv_fw_stats_parse {
 	bool chain_rssi_done;
 };
 
+struct wmi_tlv_mgmt_rx_parse {
+	const struct wmi_mgmt_rx_hdr *fixed;
+	const u8 *frame_buf;
+	bool frame_buf_done;
+};
+
 static const struct wmi_tlv_policy wmi_tlv_policies[] = {
 	[WMI_TAG_ARRAY_BYTE]
 		= { .min_len = 0 },
@@ -105,6 +112,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
 		= { .min_len = sizeof(struct wmi_vdev_stopped_event) },
 	[WMI_TAG_REG_CHAN_LIST_CC_EVENT]
 		= { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) },
+	[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]
+		= { .min_len = sizeof(struct wmi_reg_chan_list_cc_ext_event) },
 	[WMI_TAG_MGMT_RX_HDR]
 		= { .min_len = sizeof(struct wmi_mgmt_rx_hdr) },
 	[WMI_TAG_MGMT_TX_COMPL_EVENT]
@@ -863,7 +872,8 @@ static void ath11k_wmi_put_wmi_channel(struct wmi_channel *chan,
 
 		chan->band_center_freq2 = arg->channel.band_center_freq1;
 
-	} else if (arg->channel.mode == MODE_11AC_VHT80_80) {
+	} else if ((arg->channel.mode == MODE_11AC_VHT80_80) ||
+		   (arg->channel.mode == MODE_11AX_HE80_80)) {
 		chan->band_center_freq2 = arg->channel.band_center_freq2;
 	} else {
 		chan->band_center_freq2 = 0;
@@ -2338,6 +2348,69 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
 	return ret;
 }
 
+int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar,
+				       u32 vdev_id,
+				       struct ath11k_reg_tpc_power_info *param)
+{
+	struct ath11k_pdev_wmi *wmi = ar->wmi;
+	struct wmi_vdev_set_tpc_power_cmd *cmd;
+	struct wmi_vdev_ch_power_info *ch;
+	struct sk_buff *skb;
+	struct wmi_tlv *tlv;
+	u8 *ptr;
+	int i, ret, len;
+
+	len = sizeof(*cmd) + TLV_HDR_SIZE;
+	len += (sizeof(struct wmi_vdev_ch_power_info) * param->num_pwr_levels);
+
+	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+	if (!skb)
+		return -ENOMEM;
+
+	ptr = skb->data;
+
+	cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr;
+	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_TPC_POWER_CMD) |
+			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+	cmd->vdev_id = vdev_id;
+	cmd->psd_power = param->is_psd_power;
+	cmd->eirp_power = param->eirp_power;
+	cmd->power_type_6ghz = param->power_type_6g;
+	ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+		   "wmi tpc vdev_id %d is_psd_power %d eirp_power %d power_type_6g %d\n",
+		   vdev_id, param->is_psd_power, param->eirp_power, param->power_type_6g);
+
+	ptr += sizeof(*cmd);
+	tlv = (struct wmi_tlv *)ptr;
+	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
+		      FIELD_PREP(WMI_TLV_LEN, param->num_pwr_levels * sizeof(*ch));
+
+	ptr += TLV_HDR_SIZE;
+	ch = (struct wmi_vdev_ch_power_info *)ptr;
+
+	for (i = 0; i < param->num_pwr_levels; i++, ch++) {
+		ch->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+					    WMI_TAG_VDEV_CH_POWER_INFO) |
+				FIELD_PREP(WMI_TLV_LEN,
+					   sizeof(*ch) - TLV_HDR_SIZE);
+
+		ch->chan_cfreq = param->chan_power_info[i].chan_cfreq;
+		ch->tx_power = param->chan_power_info[i].tx_power;
+
+		ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+			   "wmi tpc chan_cfreq %d tx_power %d\n",
+			   ch->chan_cfreq, ch->tx_power);
+	}
+
+	ret = ath11k_wmi_cmd_send(wmi, skb,
+				  WMI_VDEV_SET_TPC_POWER_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+	return ret;
+}
+
 int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar,
 				  struct scan_cancel_param *param)
 {
@@ -3966,6 +4039,11 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg,
 	wmi_cfg->sched_params = tg_cfg->sched_params;
 	wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count;
 	wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count;
+	wmi_cfg->host_service_flags = tg_cfg->host_service_flags;
+	wmi_cfg->host_service_flags &=
+		~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT);
+	wmi_cfg->host_service_flags |= (tg_cfg->is_reg_cc_ext_event_supported <<
+					WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT);
 }
 
 static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi,
@@ -4184,6 +4262,10 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab)
 
 	ab->hw_params.hw_ops->wmi_init_config(ab, &config);
 
+	if (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT,
+		     ab->wmi_ab.svc_map))
+		config.is_reg_cc_ext_event_supported = 1;
+
 	memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config));
 
 	init_param.res_cfg = &wmi_sc->wlan_resource_config;
@@ -4627,6 +4709,11 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,
 		soc->pdevs[0].pdev_id = 0;
 	}
 
+	if (!soc->reg_info_store)
+		soc->reg_info_store = kcalloc(soc->num_radios,
+					      sizeof(*soc->reg_info_store),
+					      GFP_ATOMIC);
+
 	return 0;
 }
 
@@ -4902,11 +4989,32 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf
 	vdev_rsp->mac_id = ev->mac_id;
 	vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams;
 	vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams;
+	vdev_rsp->max_allowed_tx_power = ev->max_allowed_tx_power;
 
 	kfree(tb);
 	return 0;
 }
 
+static void ath11k_print_reg_rule(struct ath11k_base *ab, const char *band,
+				  u32 num_reg_rules,
+				  struct cur_reg_rule *reg_rule_ptr)
+{
+	struct cur_reg_rule *reg_rule = reg_rule_ptr;
+	u32 count;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI, "number of reg rules in %s band: %d\n",
+		   band, num_reg_rules);
+
+	for (count = 0; count < num_reg_rules; count++) {
+		ath11k_dbg(ab, ATH11K_DBG_WMI,
+			   "reg rule %d: (%d - %d @ %d) (%d, %d) (FLAGS %d)\n",
+			   count + 1, reg_rule->start_freq, reg_rule->end_freq,
+			   reg_rule->max_bw, reg_rule->ant_gain,
+			   reg_rule->reg_power, reg_rule->flags);
+		reg_rule++;
+	}
+}
+
 static struct cur_reg_rule
 *create_reg_rules_from_wmi(u32 num_reg_rules,
 			   struct wmi_regulatory_rule_struct *wmi_reg_rule)
@@ -4951,7 +5059,7 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
 	const void **tb;
 	const struct wmi_reg_chan_list_cc_event *chan_list_event_hdr;
 	struct wmi_regulatory_rule_struct *wmi_reg_rule;
-	u32 num_2g_reg_rules, num_5g_reg_rules;
+	u32 num_2ghz_reg_rules, num_5ghz_reg_rules;
 	int ret;
 
 	ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n");
@@ -4970,10 +5078,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
 		return -EPROTO;
 	}
 
-	reg_info->num_2g_reg_rules = chan_list_event_hdr->num_2g_reg_rules;
-	reg_info->num_5g_reg_rules = chan_list_event_hdr->num_5g_reg_rules;
+	reg_info->num_2ghz_reg_rules = chan_list_event_hdr->num_2ghz_reg_rules;
+	reg_info->num_5ghz_reg_rules = chan_list_event_hdr->num_5ghz_reg_rules;
 
-	if (!(reg_info->num_2g_reg_rules + reg_info->num_5g_reg_rules)) {
+	if (!(reg_info->num_2ghz_reg_rules + reg_info->num_5ghz_reg_rules)) {
 		ath11k_warn(ab, "No regulatory rules available in the event info\n");
 		kfree(tb);
 		return -EINVAL;
@@ -4987,61 +5095,68 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
 	reg_info->phy_id = chan_list_event_hdr->phy_id;
 	reg_info->ctry_code = chan_list_event_hdr->country_id;
 	reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code;
-	if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_PASS)
-		reg_info->status_code = REG_SET_CC_STATUS_PASS;
-	else if (chan_list_event_hdr->status_code == WMI_REG_CURRENT_ALPHA2_NOT_FOUND)
-		reg_info->status_code = REG_CURRENT_ALPHA2_NOT_FOUND;
-	else if (chan_list_event_hdr->status_code == WMI_REG_INIT_ALPHA2_NOT_FOUND)
-		reg_info->status_code = REG_INIT_ALPHA2_NOT_FOUND;
-	else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_CHANGE_NOT_ALLOWED)
-		reg_info->status_code = REG_SET_CC_CHANGE_NOT_ALLOWED;
-	else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_NO_MEMORY)
-		reg_info->status_code = REG_SET_CC_STATUS_NO_MEMORY;
-	else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL)
-		reg_info->status_code = REG_SET_CC_STATUS_FAIL;
-
-	reg_info->min_bw_2g = chan_list_event_hdr->min_bw_2g;
-	reg_info->max_bw_2g = chan_list_event_hdr->max_bw_2g;
-	reg_info->min_bw_5g = chan_list_event_hdr->min_bw_5g;
-	reg_info->max_bw_5g = chan_list_event_hdr->max_bw_5g;
-
-	num_2g_reg_rules = reg_info->num_2g_reg_rules;
-	num_5g_reg_rules = reg_info->num_5g_reg_rules;
 
 	ath11k_dbg(ab, ATH11K_DBG_WMI,
-		   "%s:cc %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d",
-		   __func__, reg_info->alpha2, reg_info->dfs_region,
-		   reg_info->min_bw_2g, reg_info->max_bw_2g,
-		   reg_info->min_bw_5g, reg_info->max_bw_5g);
+		   "status_code %s",
+		   ath11k_cc_status_to_str(reg_info->status_code));
+
+	reg_info->status_code =
+		ath11k_wmi_cc_setting_code_to_reg(chan_list_event_hdr->status_code);
+
+	reg_info->is_ext_reg_event = false;
+
+	reg_info->min_bw_2ghz = chan_list_event_hdr->min_bw_2ghz;
+	reg_info->max_bw_2ghz = chan_list_event_hdr->max_bw_2ghz;
+	reg_info->min_bw_5ghz = chan_list_event_hdr->min_bw_5ghz;
+	reg_info->max_bw_5ghz = chan_list_event_hdr->max_bw_5ghz;
+
+	num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules;
+	num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "cc %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d",
+		   reg_info->alpha2, reg_info->dfs_region,
+		   reg_info->min_bw_2ghz, reg_info->max_bw_2ghz,
+		   reg_info->min_bw_5ghz, reg_info->max_bw_5ghz);
 
 	ath11k_dbg(ab, ATH11K_DBG_WMI,
-		   "%s: num_2g_reg_rules %d num_5g_reg_rules %d", __func__,
-		   num_2g_reg_rules, num_5g_reg_rules);
+		   "num_2ghz_reg_rules %d num_5ghz_reg_rules %d",
+		   num_2ghz_reg_rules, num_5ghz_reg_rules);
 
 	wmi_reg_rule =
 		(struct wmi_regulatory_rule_struct *)((u8 *)chan_list_event_hdr
 						+ sizeof(*chan_list_event_hdr)
 						+ sizeof(struct wmi_tlv));
 
-	if (num_2g_reg_rules) {
-		reg_info->reg_rules_2g_ptr = create_reg_rules_from_wmi(num_2g_reg_rules,
-								       wmi_reg_rule);
-		if (!reg_info->reg_rules_2g_ptr) {
+	if (num_2ghz_reg_rules) {
+		reg_info->reg_rules_2ghz_ptr =
+				create_reg_rules_from_wmi(num_2ghz_reg_rules,
+							  wmi_reg_rule);
+		if (!reg_info->reg_rules_2ghz_ptr) {
 			kfree(tb);
-			ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n");
+			ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n");
 			return -ENOMEM;
 		}
+
+		ath11k_print_reg_rule(ab, "2 GHz",
+				      num_2ghz_reg_rules,
+				      reg_info->reg_rules_2ghz_ptr);
 	}
 
-	if (num_5g_reg_rules) {
-		wmi_reg_rule += num_2g_reg_rules;
-		reg_info->reg_rules_5g_ptr = create_reg_rules_from_wmi(num_5g_reg_rules,
-								       wmi_reg_rule);
-		if (!reg_info->reg_rules_5g_ptr) {
+	if (num_5ghz_reg_rules) {
+		wmi_reg_rule += num_2ghz_reg_rules;
+		reg_info->reg_rules_5ghz_ptr =
+				create_reg_rules_from_wmi(num_5ghz_reg_rules,
+							  wmi_reg_rule);
+		if (!reg_info->reg_rules_5ghz_ptr) {
 			kfree(tb);
-			ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n");
+			ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n");
 			return -ENOMEM;
 		}
+
+		ath11k_print_reg_rule(ab, "5 GHz",
+				      num_5ghz_reg_rules,
+				      reg_info->reg_rules_5ghz_ptr);
 	}
 
 	ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory channel list\n");
@@ -5050,6 +5165,429 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
 	return 0;
 }
 
+static struct cur_reg_rule
+*create_ext_reg_rules_from_wmi(u32 num_reg_rules,
+			       struct wmi_regulatory_ext_rule *wmi_reg_rule)
+{
+	struct cur_reg_rule *reg_rule_ptr;
+	u32 count;
+
+	reg_rule_ptr =  kcalloc(num_reg_rules, sizeof(*reg_rule_ptr), GFP_ATOMIC);
+
+	if (!reg_rule_ptr)
+		return NULL;
+
+	for (count = 0; count < num_reg_rules; count++) {
+		reg_rule_ptr[count].start_freq =
+			u32_get_bits(wmi_reg_rule[count].freq_info,
+				     REG_RULE_START_FREQ);
+		reg_rule_ptr[count].end_freq =
+			u32_get_bits(wmi_reg_rule[count].freq_info,
+				     REG_RULE_END_FREQ);
+		reg_rule_ptr[count].max_bw =
+			u32_get_bits(wmi_reg_rule[count].bw_pwr_info,
+				     REG_RULE_MAX_BW);
+		reg_rule_ptr[count].reg_power =
+			u32_get_bits(wmi_reg_rule[count].bw_pwr_info,
+				     REG_RULE_REG_PWR);
+		reg_rule_ptr[count].ant_gain =
+			u32_get_bits(wmi_reg_rule[count].bw_pwr_info,
+				     REG_RULE_ANT_GAIN);
+		reg_rule_ptr[count].flags =
+			u32_get_bits(wmi_reg_rule[count].flag_info,
+				     REG_RULE_FLAGS);
+		reg_rule_ptr[count].psd_flag =
+			u32_get_bits(wmi_reg_rule[count].psd_power_info,
+				     REG_RULE_PSD_INFO);
+		reg_rule_ptr[count].psd_eirp =
+			u32_get_bits(wmi_reg_rule[count].psd_power_info,
+				     REG_RULE_PSD_EIRP);
+	}
+
+	return reg_rule_ptr;
+}
+
+static u8
+ath11k_invalid_5ghz_reg_ext_rules_from_wmi(u32 num_reg_rules,
+					   const struct wmi_regulatory_ext_rule *rule)
+{
+	u8 num_invalid_5ghz_rules = 0;
+	u32 count, start_freq;
+
+	for (count = 0; count < num_reg_rules; count++) {
+		start_freq = u32_get_bits(rule[count].freq_info,
+					  REG_RULE_START_FREQ);
+
+		if (start_freq >= ATH11K_MIN_6G_FREQ)
+			num_invalid_5ghz_rules++;
+	}
+
+	return num_invalid_5ghz_rules;
+}
+
+static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
+						   struct sk_buff *skb,
+						   struct cur_regulatory_info *reg_info)
+{
+	const void **tb;
+	const struct wmi_reg_chan_list_cc_ext_event *ev;
+	struct wmi_regulatory_ext_rule *ext_wmi_reg_rule;
+	u32 num_2ghz_reg_rules, num_5ghz_reg_rules;
+	u32 num_6ghz_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+	u32 num_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+	u32 total_reg_rules = 0;
+	int ret, i, j, num_invalid_5ghz_ext_rules = 0;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory ext channel list\n");
+
+	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT];
+	if (!ev) {
+		ath11k_warn(ab, "failed to fetch reg chan list ext update ev\n");
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	reg_info->num_2ghz_reg_rules = ev->num_2ghz_reg_rules;
+	reg_info->num_5ghz_reg_rules = ev->num_5ghz_reg_rules;
+	reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] =
+			ev->num_6ghz_reg_rules_ap_lpi;
+	reg_info->num_6ghz_rules_ap[WMI_REG_STANDARD_POWER_AP] =
+			ev->num_6ghz_reg_rules_ap_sp;
+	reg_info->num_6ghz_rules_ap[WMI_REG_VERY_LOW_POWER_AP] =
+			ev->num_6ghz_reg_rules_ap_vlp;
+
+	for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+		reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i] =
+			ev->num_6ghz_reg_rules_client_lpi[i];
+		reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i] =
+			ev->num_6ghz_reg_rules_client_sp[i];
+		reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+			ev->num_6ghz_reg_rules_client_vlp[i];
+	}
+
+	num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules;
+	num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules;
+
+	total_reg_rules += num_2ghz_reg_rules;
+	total_reg_rules += num_5ghz_reg_rules;
+
+	if ((num_2ghz_reg_rules > MAX_REG_RULES) ||
+	    (num_5ghz_reg_rules > MAX_REG_RULES)) {
+		ath11k_warn(ab, "Num reg rules for 2.4 GHz/5 GHz exceeds max limit (num_2ghz_reg_rules: %d num_5ghz_reg_rules: %d max_rules: %d)\n",
+			    num_2ghz_reg_rules, num_5ghz_reg_rules, MAX_REG_RULES);
+		kfree(tb);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+		num_6ghz_reg_rules_ap[i] = reg_info->num_6ghz_rules_ap[i];
+
+		if (num_6ghz_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) {
+			ath11k_warn(ab, "Num 6 GHz reg rules for AP mode(%d) exceeds max limit (num_6ghz_reg_rules_ap: %d, max_rules: %d)\n",
+				    i, num_6ghz_reg_rules_ap[i], MAX_6GHZ_REG_RULES);
+			kfree(tb);
+			return -EINVAL;
+		}
+
+		total_reg_rules += num_6ghz_reg_rules_ap[i];
+	}
+
+	for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+		num_6ghz_client[WMI_REG_INDOOR_AP][i] =
+			reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i];
+		total_reg_rules += num_6ghz_client[WMI_REG_INDOOR_AP][i];
+
+		num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] =
+			reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i];
+		total_reg_rules += num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i];
+
+		num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+			reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i];
+		total_reg_rules += num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i];
+
+		if ((num_6ghz_client[WMI_REG_INDOOR_AP][i] > MAX_6GHZ_REG_RULES) ||
+		    (num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] >
+							     MAX_6GHZ_REG_RULES) ||
+		    (num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] >
+							     MAX_6GHZ_REG_RULES)) {
+			ath11k_warn(ab,
+				    "Num 6 GHz client reg rules exceeds max limit, for client(type: %d)\n",
+				    i);
+			kfree(tb);
+			return -EINVAL;
+		}
+	}
+
+	if (!total_reg_rules) {
+		ath11k_warn(ab, "No reg rules available\n");
+		kfree(tb);
+		return -EINVAL;
+	}
+
+	memcpy(reg_info->alpha2, &ev->alpha2, REG_ALPHA2_LEN);
+
+	reg_info->dfs_region = ev->dfs_region;
+	reg_info->phybitmap = ev->phybitmap;
+	reg_info->num_phy = ev->num_phy;
+	reg_info->phy_id = ev->phy_id;
+	reg_info->ctry_code = ev->country_id;
+	reg_info->reg_dmn_pair = ev->domain_code;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "status_code %s",
+		   ath11k_cc_status_to_str(reg_info->status_code));
+
+	reg_info->status_code =
+		ath11k_wmi_cc_setting_code_to_reg(ev->status_code);
+
+	reg_info->is_ext_reg_event = true;
+
+	reg_info->min_bw_2ghz = ev->min_bw_2ghz;
+	reg_info->max_bw_2ghz = ev->max_bw_2ghz;
+	reg_info->min_bw_5ghz = ev->min_bw_5ghz;
+	reg_info->max_bw_5ghz = ev->max_bw_5ghz;
+
+	reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP] =
+			ev->min_bw_6ghz_ap_lpi;
+	reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP] =
+			ev->max_bw_6ghz_ap_lpi;
+	reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] =
+			ev->min_bw_6ghz_ap_sp;
+	reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] =
+			ev->max_bw_6ghz_ap_sp;
+	reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] =
+			ev->min_bw_6ghz_ap_vlp;
+	reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] =
+			ev->max_bw_6ghz_ap_vlp;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "6 GHz AP BW: LPI (%d - %d), SP (%d - %d), VLP (%d - %d)\n",
+		   reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP],
+		   reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP],
+		   reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP],
+		   reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP],
+		   reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP],
+		   reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP]);
+
+	for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+		reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i] =
+				ev->min_bw_6ghz_client_lpi[i];
+		reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i] =
+				ev->max_bw_6ghz_client_lpi[i];
+		reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] =
+				ev->min_bw_6ghz_client_sp[i];
+		reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] =
+				ev->max_bw_6ghz_client_sp[i];
+		reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+				ev->min_bw_6ghz_client_vlp[i];
+		reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+				ev->max_bw_6ghz_client_vlp[i];
+
+		ath11k_dbg(ab, ATH11K_DBG_WMI,
+			   "6 GHz %s BW: LPI (%d - %d), SP (%d - %d), VLP (%d - %d)\n",
+			   ath11k_6ghz_client_type_to_str(i),
+			   reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i],
+			   reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i],
+			   reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i],
+			   reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i],
+			   reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i],
+			   reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i]);
+	}
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "cc_ext %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d",
+		   reg_info->alpha2, reg_info->dfs_region,
+		   reg_info->min_bw_2ghz, reg_info->max_bw_2ghz,
+		   reg_info->min_bw_5ghz, reg_info->max_bw_5ghz);
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "num_2ghz_reg_rules %d num_5ghz_reg_rules %d",
+		   num_2ghz_reg_rules, num_5ghz_reg_rules);
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "num_6ghz_reg_rules_ap_lpi: %d num_6ghz_reg_rules_ap_sp: %d num_6ghz_reg_rules_ap_vlp: %d",
+		   num_6ghz_reg_rules_ap[WMI_REG_INDOOR_AP],
+		   num_6ghz_reg_rules_ap[WMI_REG_STANDARD_POWER_AP],
+		   num_6ghz_reg_rules_ap[WMI_REG_VERY_LOW_POWER_AP]);
+
+	j = WMI_REG_DEFAULT_CLIENT;
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "6 GHz Regular client: num_6ghz_reg_rules_lpi: %d num_6ghz_reg_rules_sp: %d num_6ghz_reg_rules_vlp: %d",
+		   num_6ghz_client[WMI_REG_INDOOR_AP][j],
+		   num_6ghz_client[WMI_REG_STANDARD_POWER_AP][j],
+		   num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]);
+
+	j = WMI_REG_SUBORDINATE_CLIENT;
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "6 GHz Subordinate client: num_6ghz_reg_rules_lpi: %d num_6ghz_reg_rules_sp: %d num_6ghz_reg_rules_vlp: %d",
+		   num_6ghz_client[WMI_REG_INDOOR_AP][j],
+		   num_6ghz_client[WMI_REG_STANDARD_POWER_AP][j],
+		   num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]);
+
+	ext_wmi_reg_rule =
+		(struct wmi_regulatory_ext_rule *)((u8 *)ev + sizeof(*ev) +
+						   sizeof(struct wmi_tlv));
+	if (num_2ghz_reg_rules) {
+		reg_info->reg_rules_2ghz_ptr =
+			create_ext_reg_rules_from_wmi(num_2ghz_reg_rules,
+						      ext_wmi_reg_rule);
+
+		if (!reg_info->reg_rules_2ghz_ptr) {
+			kfree(tb);
+			ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n");
+			return -ENOMEM;
+		}
+
+		ath11k_print_reg_rule(ab, "2 GHz",
+				      num_2ghz_reg_rules,
+				      reg_info->reg_rules_2ghz_ptr);
+	}
+
+	ext_wmi_reg_rule += num_2ghz_reg_rules;
+
+	/* Firmware might include 6 GHz reg rule in 5 GHz rule list
+	 * for few countries along with separate 6 GHz rule.
+	 * Having same 6 GHz reg rule in 5 GHz and 6 GHz rules list
+	 * causes intersect check to be true, and same rules will be
+	 * shown multiple times in iw cmd.
+	 * Hence, avoid parsing 6 GHz rule from 5 GHz reg rule list
+	 */
+	num_invalid_5ghz_ext_rules =
+		ath11k_invalid_5ghz_reg_ext_rules_from_wmi(num_5ghz_reg_rules,
+							   ext_wmi_reg_rule);
+
+	if (num_invalid_5ghz_ext_rules) {
+		ath11k_dbg(ab, ATH11K_DBG_WMI,
+			   "CC: %s 5 GHz reg rules number %d from fw, %d number of invalid 5 GHz rules",
+			   reg_info->alpha2, reg_info->num_5ghz_reg_rules,
+			   num_invalid_5ghz_ext_rules);
+
+		num_5ghz_reg_rules = num_5ghz_reg_rules - num_invalid_5ghz_ext_rules;
+		reg_info->num_5ghz_reg_rules = num_5ghz_reg_rules;
+	}
+
+	if (num_5ghz_reg_rules) {
+		reg_info->reg_rules_5ghz_ptr =
+			create_ext_reg_rules_from_wmi(num_5ghz_reg_rules,
+						      ext_wmi_reg_rule);
+
+		if (!reg_info->reg_rules_5ghz_ptr) {
+			kfree(tb);
+			ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n");
+			return -ENOMEM;
+		}
+
+		ath11k_print_reg_rule(ab, "5 GHz",
+				      num_5ghz_reg_rules,
+				      reg_info->reg_rules_5ghz_ptr);
+	}
+
+	/* We have adjusted the number of 5 GHz reg rules above. But still those
+	 * many rules needs to be adjusted in ext_wmi_reg_rule.
+	 *
+	 * NOTE: num_invalid_5ghz_ext_rules will be 0 for rest other cases.
+	 */
+	ext_wmi_reg_rule += (num_5ghz_reg_rules + num_invalid_5ghz_ext_rules);
+
+	for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+		reg_info->reg_rules_6ghz_ap_ptr[i] =
+			create_ext_reg_rules_from_wmi(num_6ghz_reg_rules_ap[i],
+						      ext_wmi_reg_rule);
+
+		if (!reg_info->reg_rules_6ghz_ap_ptr[i]) {
+			kfree(tb);
+			ath11k_warn(ab, "Unable to Allocate memory for 6 GHz AP rules\n");
+			return -ENOMEM;
+		}
+
+		ath11k_print_reg_rule(ab, ath11k_6ghz_ap_type_to_str(i),
+				      num_6ghz_reg_rules_ap[i],
+				      reg_info->reg_rules_6ghz_ap_ptr[i]);
+
+		ext_wmi_reg_rule += num_6ghz_reg_rules_ap[i];
+	}
+
+	for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) {
+		ath11k_dbg(ab, ATH11K_DBG_WMI,
+			   "6 GHz AP type %s", ath11k_6ghz_ap_type_to_str(j));
+
+		for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+			reg_info->reg_rules_6ghz_client_ptr[j][i] =
+				create_ext_reg_rules_from_wmi(num_6ghz_client[j][i],
+							      ext_wmi_reg_rule);
+
+			if (!reg_info->reg_rules_6ghz_client_ptr[j][i]) {
+				kfree(tb);
+				ath11k_warn(ab, "Unable to Allocate memory for 6 GHz client rules\n");
+				return -ENOMEM;
+			}
+
+			ath11k_print_reg_rule(ab,
+					      ath11k_6ghz_client_type_to_str(i),
+					      num_6ghz_client[j][i],
+					      reg_info->reg_rules_6ghz_client_ptr[j][i]);
+
+			ext_wmi_reg_rule += num_6ghz_client[j][i];
+		}
+	}
+
+	reg_info->client_type = ev->client_type;
+	reg_info->rnr_tpe_usable = ev->rnr_tpe_usable;
+	reg_info->unspecified_ap_usable =
+			ev->unspecified_ap_usable;
+	reg_info->domain_code_6ghz_ap[WMI_REG_INDOOR_AP] =
+			ev->domain_code_6ghz_ap_lpi;
+	reg_info->domain_code_6ghz_ap[WMI_REG_STANDARD_POWER_AP] =
+			ev->domain_code_6ghz_ap_sp;
+	reg_info->domain_code_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] =
+			ev->domain_code_6ghz_ap_vlp;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "6 GHz reg info client type %s rnr_tpe_usable %d unspecified_ap_usable %d AP sub domain: lpi %s, sp %s, vlp %s\n",
+		   ath11k_6ghz_client_type_to_str(reg_info->client_type),
+		   reg_info->rnr_tpe_usable,
+		   reg_info->unspecified_ap_usable,
+		   ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_lpi),
+		   ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_sp),
+		   ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_vlp));
+
+	for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+		reg_info->domain_code_6ghz_client[WMI_REG_INDOOR_AP][i] =
+				ev->domain_code_6ghz_client_lpi[i];
+		reg_info->domain_code_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] =
+				ev->domain_code_6ghz_client_sp[i];
+		reg_info->domain_code_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+				ev->domain_code_6ghz_client_vlp[i];
+
+		ath11k_dbg(ab, ATH11K_DBG_WMI,
+			   "6 GHz client type %s client sub domain: lpi %s, sp %s, vlp %s\n",
+			   ath11k_6ghz_client_type_to_str(i),
+			   ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_lpi[i]),
+			   ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_sp[i]),
+			   ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_vlp[i])
+			  );
+	}
+
+	reg_info->domain_code_6ghz_super_id = ev->domain_code_6ghz_super_id;
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "6 GHz client_type %s 6 GHz super domain %s",
+		   ath11k_6ghz_client_type_to_str(reg_info->client_type),
+		   ath11k_super_reg_6ghz_to_str(reg_info->domain_code_6ghz_super_id));
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory ext channel list\n");
+
+	kfree(tb);
+	return 0;
+}
+
 static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *skb,
 					struct wmi_peer_delete_resp_event *peer_del_resp)
 {
@@ -5165,28 +5703,49 @@ static int ath11k_pull_vdev_stopped_param_tlv(struct ath11k_base *ab, struct sk_
 	return 0;
 }
 
+static int ath11k_wmi_tlv_mgmt_rx_parse(struct ath11k_base *ab,
+					u16 tag, u16 len,
+					const void *ptr, void *data)
+{
+	struct wmi_tlv_mgmt_rx_parse *parse = data;
+
+	switch (tag) {
+	case WMI_TAG_MGMT_RX_HDR:
+		parse->fixed = ptr;
+		break;
+	case WMI_TAG_ARRAY_BYTE:
+		if (!parse->frame_buf_done) {
+			parse->frame_buf = ptr;
+			parse->frame_buf_done = true;
+		}
+		break;
+	}
+	return 0;
+}
+
 static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab,
 					  struct sk_buff *skb,
 					  struct mgmt_rx_event_params *hdr)
 {
-	const void **tb;
+	struct wmi_tlv_mgmt_rx_parse parse = { };
 	const struct wmi_mgmt_rx_hdr *ev;
 	const u8 *frame;
 	int ret;
 
-	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
-	if (IS_ERR(tb)) {
-		ret = PTR_ERR(tb);
-		ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
+	ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+				  ath11k_wmi_tlv_mgmt_rx_parse,
+				  &parse);
+	if (ret) {
+		ath11k_warn(ab, "failed to parse mgmt rx tlv %d\n",
+			    ret);
 		return ret;
 	}
 
-	ev = tb[WMI_TAG_MGMT_RX_HDR];
-	frame = tb[WMI_TAG_ARRAY_BYTE];
+	ev = parse.fixed;
+	frame = parse.frame_buf;
 
 	if (!ev || !frame) {
 		ath11k_warn(ab, "failed to fetch mgmt rx hdr");
-		kfree(tb);
 		return -EPROTO;
 	}
 
@@ -5205,7 +5764,6 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab,
 
 	if (skb->len < (frame - skb->data) + hdr->buf_len) {
 		ath11k_warn(ab, "invalid length in mgmt rx hdr ev");
-		kfree(tb);
 		return -EPROTO;
 	}
 
@@ -5217,7 +5775,6 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab,
 
 	ath11k_ce_byte_swap(skb->data, hdr->buf_len);
 
-	kfree(tb);
 	return 0;
 }
 
@@ -6491,25 +7048,34 @@ static bool ath11k_reg_is_world_alpha(char *alpha)
 	return false;
 }
 
-static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb)
+void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info)
 {
-	struct cur_regulatory_info *reg_info = NULL;
-	struct ieee80211_regdomain *regd = NULL;
-	bool intersect = false;
-	int ret = 0, pdev_idx;
-	struct ath11k *ar;
+	int i, j;
 
-	reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
-	if (!reg_info) {
-		ret = -ENOMEM;
-		goto fallback;
-	}
+	if (reg_info) {
+		kfree(reg_info->reg_rules_2ghz_ptr);
 
-	ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
-	if (ret) {
-		ath11k_warn(ab, "failed to extract regulatory info from received event\n");
-		goto fallback;
+		kfree(reg_info->reg_rules_5ghz_ptr);
+
+		for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+			kfree(reg_info->reg_rules_6ghz_ap_ptr[i]);
+			for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++)
+				kfree(reg_info->reg_rules_6ghz_client_ptr[i][j]);
+		}
+
+		memset(reg_info, 0, sizeof(*reg_info));
 	}
+}
+
+int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
+				struct cur_regulatory_info *reg_info,
+				enum ieee80211_ap_reg_power power_type)
+{
+	struct ieee80211_regdomain *regd;
+	bool intersect = false;
+	int pdev_idx;
+	struct ath11k *ar;
+	enum wmi_vdev_type vdev_type;
 
 	if (reg_info->status_code != REG_SET_CC_STATUS_PASS) {
 		/* In case of failure to set the requested ctry,
@@ -6517,7 +7083,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
 		 * and return from here.
 		 */
 		ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n");
-		goto mem_free;
+		return -EINVAL;
 	}
 
 	pdev_idx = reg_info->phy_id;
@@ -6525,13 +7091,13 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
 	/* Avoid default reg rule updates sent during FW recovery if
 	 * it is already available
 	 */
-	spin_lock(&ab->base_lock);
+	spin_lock_bh(&ab->base_lock);
 	if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) &&
 	    ab->default_regd[pdev_idx]) {
-		spin_unlock(&ab->base_lock);
-		goto mem_free;
+		spin_unlock_bh(&ab->base_lock);
+		goto retfail;
 	}
-	spin_unlock(&ab->base_lock);
+	spin_unlock_bh(&ab->base_lock);
 
 	if (pdev_idx >= ab->num_radios) {
 		/* Process the event for phy0 only if single_pdev_only
@@ -6540,7 +7106,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
 		 */
 		if (ab->hw_params.single_pdev_only &&
 		    pdev_idx < ab->hw_params.num_rxmda_per_pdev)
-			goto mem_free;
+			goto retfail;
 		else
 			goto fallback;
 	}
@@ -6551,7 +7117,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
 	if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] &&
 	    !memcmp((char *)ab->default_regd[pdev_idx]->alpha2,
 		    (char *)reg_info->alpha2, 2))
-		goto mem_free;
+		goto retfail;
 
 	/* Intersect new rules with default regd if a new country setting was
 	 * requested, i.e a default regd was already set during initialization
@@ -6563,13 +7129,25 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
 	    !ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
 		intersect = true;
 
-	regd = ath11k_reg_build_regd(ab, reg_info, intersect);
+	ar = ab->pdevs[pdev_idx].ar;
+	vdev_type = ath11k_mac_get_ar_vdev_type(ar);
+
+	ath11k_dbg(ab, ATH11K_DBG_WMI,
+		   "wmi handle chan list power type %d vdev type %d intersect %d\n",
+		   power_type, vdev_type, intersect);
+
+	regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type);
 	if (!regd) {
 		ath11k_warn(ab, "failed to build regd from reg_info\n");
 		goto fallback;
 	}
 
-	spin_lock(&ab->base_lock);
+	if (power_type == IEEE80211_REG_UNSET_AP) {
+		ath11k_reg_reset_info(&ab->reg_info_store[pdev_idx]);
+		ab->reg_info_store[pdev_idx] = *reg_info;
+	}
+
+	spin_lock_bh(&ab->base_lock);
 	if (ab->default_regd[pdev_idx]) {
 		/* The initial rules from FW after WMI Init is to build
 		 * the default regd. From then on, any rules updated for
@@ -6589,9 +7167,9 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
 		ab->default_regd[pdev_idx] = regd;
 	}
 	ab->dfs_region = reg_info->dfs_region;
-	spin_unlock(&ab->base_lock);
+	spin_unlock_bh(&ab->base_lock);
 
-	goto mem_free;
+	return 0;
 
 fallback:
 	/* Fallback to older reg (by sending previous country setting
@@ -6603,12 +7181,45 @@ fallback:
 	 */
 	/* TODO: This is rare, but still should also be handled */
 	WARN_ON(1);
-mem_free:
-	if (reg_info) {
-		kfree(reg_info->reg_rules_2g_ptr);
-		kfree(reg_info->reg_rules_5g_ptr);
-		kfree(reg_info);
+
+retfail:
+
+	return -EINVAL;
+}
+
+static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb,
+				      enum wmi_reg_chan_list_cmd_type id)
+{
+	struct cur_regulatory_info *reg_info;
+	int ret;
+
+	reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
+	if (!reg_info)
+		return -ENOMEM;
+
+	if (id == WMI_REG_CHAN_LIST_CC_ID)
+		ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
+	else
+		ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
+
+	if (ret) {
+		ath11k_warn(ab, "failed to extract regulatory info from received event\n");
+		goto mem_free;
+	}
+
+	ret = ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_UNSET_AP);
+	if (ret) {
+		ath11k_dbg(ab, ATH11K_DBG_WMI,
+			   "failed to process regulatory info from received event\n");
+		goto mem_free;
 	}
+
+	kfree(reg_info);
+	return 0;
+
+mem_free:
+	ath11k_reg_reset_info(reg_info);
+	kfree(reg_info);
 	return ret;
 }
 
@@ -6762,7 +7373,7 @@ static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff
 	}
 
 	ar->last_wmi_vdev_start_status = 0;
-
+	ar->max_allowed_tx_power = vdev_start_resp.max_allowed_tx_power;
 	status = vdev_start_resp.status;
 
 	if (WARN_ON_ONCE(status)) {
@@ -7750,6 +8361,37 @@ exit:
 }
 
 static void
+ath11k_wmi_tm_event_segmented(struct ath11k_base *ab, u32 cmd_id,
+			      struct sk_buff *skb)
+{
+	const void **tb;
+	const struct wmi_ftm_event_msg *ev;
+	u16 length;
+	int ret;
+
+	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
+		return;
+	}
+
+	ev = tb[WMI_TAG_ARRAY_BYTE];
+	if (!ev) {
+		ath11k_warn(ab, "failed to fetch ftm msg\n");
+		kfree(tb);
+		return;
+	}
+
+	length = skb->len - TLV_HDR_SIZE;
+	ret = ath11k_tm_process_event(ab, cmd_id, ev, length);
+	if (ret)
+		ath11k_warn(ab, "Failed to process ftm event\n");
+
+	kfree(tb);
+}
+
+static void
 ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab,
 				  struct sk_buff *skb)
 {
@@ -8044,7 +8686,10 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
 		ath11k_service_ready_ext2_event(ab, skb);
 		break;
 	case WMI_REG_CHAN_LIST_CC_EVENTID:
-		ath11k_reg_chan_list_event(ab, skb);
+		ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_ID);
+		break;
+	case WMI_REG_CHAN_LIST_CC_EXT_EVENTID:
+		ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_EXT_ID);
 		break;
 	case WMI_READY_EVENTID:
 		ath11k_ready_event(ab, skb);
@@ -8101,6 +8746,12 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
 	case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID:
 		ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb);
 		break;
+	case WMI_PDEV_UTF_EVENTID:
+		if (test_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
+			ath11k_wmi_tm_event_segmented(ab, id, skb);
+		else
+			ath11k_tm_wmi_event_unsegmented(ab, id, skb);
+		break;
 	case WMI_PDEV_TEMPERATURE_EVENTID:
 		ath11k_wmi_pdev_temperature_event(ab, skb);
 		break;
@@ -9170,3 +9821,76 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
 
 	return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
 }
+
+int ath11k_wmi_set_unit_test(struct ath11k *ar, struct unit_test_cmd *unit_test)
+{
+	struct ath11k_pdev_wmi *wmi = ar->wmi;
+	struct sk_buff *skb;
+	struct wmi_unit_test_cmd_fixed_param *cmd;
+	u32 len, args_tlv_len;
+	u8 *buf_ptr;
+	u32 *args;
+	struct wmi_tlv *tlv;
+	u32 i;
+
+	args_tlv_len = TLV_HDR_SIZE + unit_test->num_args * sizeof(u32);
+
+	len = sizeof(struct wmi_unit_test_cmd_fixed_param) + args_tlv_len;
+	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_unit_test_cmd_fixed_param *)skb->data;
+	buf_ptr = (u8 *)cmd;
+	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+				     WMI_TAG_UNIT_TEST_CMD) |
+				     FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+	cmd->vdev_id = unit_test->vdev_id;
+	cmd->module_id = unit_test->module_id;
+	cmd->num_args = unit_test->num_args;
+
+	buf_ptr += sizeof(*cmd);
+
+	tlv  = (struct wmi_tlv *)buf_ptr;
+	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) |
+				 FIELD_PREP(WMI_TLV_LEN, unit_test->num_args * sizeof(u32));
+	args = (u32 *)(buf_ptr + TLV_HDR_SIZE);
+	ath11k_info(ar->ab, "module id = 0x%x, num args = %u",
+		    unit_test->module_id, unit_test->num_args);
+	for (i = 0; (i < unit_test->num_args && i < UNIT_TEST_MAX_NUM_ARGS); i++) {
+		args[i] = unit_test->args[i];
+		ath11k_info(ar->ab, "0x%x,", unit_test->args[i]);
+	}
+
+	return ath11k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID);
+}
+
+int ath11k_wmi_send_coex_config(struct ath11k *ar,
+				struct wmi_coex_config_params *param)
+{
+	struct ath11k_pdev_wmi *wmi = ar->wmi;
+	struct wmi_coex_config_cmd *cmd;
+	struct sk_buff *skb;
+
+	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_coex_config_cmd *)skb->data;
+	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_COEX_CONFIG_CMD) |
+			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+	cmd->vdev_id = param->vdev_id;
+	cmd->config_type = param->config_type;
+	cmd->config_arg1 = param->config_arg1;
+	cmd->config_arg2 = param->config_arg2;
+	cmd->config_arg3 = param->config_arg3;
+	cmd->config_arg4 = param->config_arg4;
+	cmd->config_arg5 = param->config_arg5;
+	cmd->config_arg6 = param->config_arg6;
+	ath11k_warn(ar->ab, "wmi send coex cfg vdev %d type %u args %u %u %u %u %u %u\n",
+			cmd->vdev_id, cmd->config_type, cmd->config_arg1,
+			cmd->config_arg2, cmd->config_arg3, cmd->config_arg4,
+			cmd->config_arg5, cmd->config_arg6);
+
+	return ath11k_wmi_cmd_send(wmi, skb, WMI_COEX_CONFIG_CMDID);
+}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 8f2c07d70a4a..e47f621ebb99 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef ATH11K_WMI_H
@@ -14,6 +15,8 @@ struct ath11k;
 struct ath11k_fw_stats;
 struct ath11k_fw_dbglog;
 struct ath11k_vif;
+struct ath11k_base;
+struct ath11k_reg_tpc_power_info;
 
 #define PSOC_HOST_MAX_NUM_SS (8)
 
@@ -68,6 +71,7 @@ struct wmi_tlv {
 
 #define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1
 
+#define MAX_WMI_UTF_LEN 252
 #define WMI_BA_MODE_BUFFER_SIZE_256  3
 /*
  * HW mode config type replicated from FW header
@@ -317,6 +321,36 @@ enum wmi_tlv_cmd_id {
 	WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID,
 	WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID,
 	WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID,
+	/** WMI commands related to dbg arp stats */
+	WMI_VDEV_SET_ARP_STAT_CMDID,
+	WMI_VDEV_GET_ARP_STAT_CMDID,
+	/** get tx power for the current vdev */
+	WMI_VDEV_GET_TX_POWER_CMDID,
+	/* limit STA offchannel activity */
+	WMI_VDEV_LIMIT_OFFCHAN_CMDID,
+	/** To set custom software retries per-AC for vdev */
+	WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID,
+	/** To set chainmask configuration for vdev */
+	WMI_VDEV_CHAINMASK_CONFIG_CMDID,
+	WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID,
+	/* request LTE-Coex info */
+	WMI_VDEV_GET_MWS_COEX_INFO_CMDID,
+	/** delete all peer (excluding bss peer) */
+	WMI_VDEV_DELETE_ALL_PEER_CMDID,
+	/* To set bss max idle time related parameters */
+	WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID,
+	/** Indicates firmware to trigger Audio sync */
+	WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID,
+	/** Gives Qtimer value to firmware */
+	WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID,
+	/** Preferred channel list for each vdev */
+	WMI_VDEV_SET_PCL_CMDID,
+	/** VDEV_GET_BIG_DATA_CMD IS DEPRECATED - DO NOT USE */
+	WMI_VDEV_GET_BIG_DATA_CMDID,
+	/** Get per vdev BIG DATA stats phase 2 */
+	WMI_VDEV_GET_BIG_DATA_P2_CMDID,
+	/** set TPC PSD/non-PSD power */
+	WMI_VDEV_SET_TPC_POWER_CMDID,
 	WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER),
 	WMI_PEER_DELETE_CMDID,
 	WMI_PEER_FLUSH_TIDS_CMDID,
@@ -797,6 +831,7 @@ enum wmi_tlv_event_id {
 	WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC),
 	WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY),
 	WMI_11D_NEW_COUNTRY_EVENTID,
+	WMI_REG_CHAN_LIST_CC_EXT_EVENTID,
 	WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE),
 	WMI_NDP_INITIATOR_RSP_EVENTID,
 	WMI_NDP_RESPONDER_RSP_EVENTID,
@@ -1864,6 +1899,10 @@ enum wmi_tlv_tag {
 	WMI_TAG_PDEV_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD,
 	WMI_TAG_PDEV_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD,
 	WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD,
+	WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9,
+	WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT,
+	WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5,
+	WMI_TAG_VDEV_CH_POWER_INFO,
 	WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8,
 	WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD,
 	WMI_TAG_MAX
@@ -2095,7 +2134,10 @@ enum wmi_tlv_service {
 
 	/* The second 128 bits */
 	WMI_MAX_EXT_SERVICE = 256,
+	WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280,
+	WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
 	WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326,
+	WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN = 357,
 
 	/* The third 128 bits */
 	WMI_MAX_EXT2_SERVICE = 384
@@ -2309,6 +2351,9 @@ struct wmi_init_cmd {
 } __packed;
 
 #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)
+#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT       BIT(0)
+
+#define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4
 
 struct wmi_resource_config {
 	u32 tlv_header;
@@ -2369,6 +2414,15 @@ struct wmi_resource_config {
 	u32 sched_params;
 	u32 twt_ap_pdev_count;
 	u32 twt_ap_sta_count;
+	u32 max_nlo_ssids;
+	u32 num_packet_filters;
+	u32 num_max_sta_vdevs;
+	u32 max_bssid_indicator;
+	u32 ul_resp_config;
+	u32 msdu_flow_override_config0;
+	u32 msdu_flow_override_config1;
+	u32 flags2;
+	u32 host_service_flags;
 } __packed;
 
 struct wmi_service_ready_event {
@@ -2851,36 +2905,40 @@ struct rx_reorder_queue_remove_params {
 #define REG_RULE_MAX_BW				0x0000ffff
 #define REG_RULE_REG_PWR			0x00ff0000
 #define REG_RULE_ANT_GAIN			0xff000000
+#define REG_RULE_PSD_INFO			BIT(0)
+#define REG_RULE_PSD_EIRP			0xff0000
 
 #define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0)
 #define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1)
 #define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2)
 #define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
 
-#define HECAP_PHYDWORD_0	0
-#define HECAP_PHYDWORD_1	1
-#define HECAP_PHYDWORD_2	2
+#define HE_PHYCAP_BYTE_0	0
+#define HE_PHYCAP_BYTE_1	1
+#define HE_PHYCAP_BYTE_2	2
+#define HE_PHYCAP_BYTE_3	3
+#define HE_PHYCAP_BYTE_4	4
 
-#define HECAP_PHY_SU_BFER		BIT(31)
+#define HECAP_PHY_SU_BFER		BIT(7)
 #define HECAP_PHY_SU_BFEE		BIT(0)
 #define HECAP_PHY_MU_BFER		BIT(1)
-#define HECAP_PHY_UL_MUMIMO		BIT(22)
-#define HECAP_PHY_UL_MUOFDMA		BIT(23)
+#define HECAP_PHY_UL_MUMIMO		BIT(6)
+#define HECAP_PHY_UL_MUOFDMA		BIT(7)
 
 #define HECAP_PHY_SUBFMR_GET(hecap_phy) \
-	FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HECAP_PHYDWORD_0])
+	FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HE_PHYCAP_BYTE_3])
 
 #define HECAP_PHY_SUBFME_GET(hecap_phy) \
-	FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HECAP_PHYDWORD_1])
+	FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HE_PHYCAP_BYTE_4])
 
 #define HECAP_PHY_MUBFMR_GET(hecap_phy) \
-	FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HECAP_PHYDWORD_1])
+	FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HE_PHYCAP_BYTE_4])
 
 #define HECAP_PHY_ULMUMIMO_GET(hecap_phy) \
-	FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HECAP_PHYDWORD_0])
+	FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HE_PHYCAP_BYTE_2])
 
 #define HECAP_PHY_ULOFDMA_GET(hecap_phy) \
-	FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HECAP_PHYDWORD_0])
+	FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HE_PHYCAP_BYTE_2])
 
 #define HE_MODE_SU_TX_BFEE	BIT(0)
 #define HE_MODE_SU_TX_BFER	BIT(1)
@@ -2893,8 +2951,11 @@ struct rx_reorder_queue_remove_params {
 #define HE_DL_MUOFDMA_ENABLE	1
 #define HE_UL_MUOFDMA_ENABLE	1
 #define HE_DL_MUMIMO_ENABLE	1
+#define HE_UL_MUMIMO_ENABLE	1
 #define HE_MU_BFEE_ENABLE	1
 #define HE_SU_BFEE_ENABLE	1
+#define HE_MU_BFER_ENABLE	1
+#define HE_SU_BFER_ENABLE	1
 
 #define HE_VHT_SOUNDING_MODE_ENABLE		1
 #define HE_SU_MU_SOUNDING_MODE_ENABLE		1
@@ -3115,6 +3176,31 @@ struct wlan_ssid {
 	u8 ssid[WLAN_SSID_MAX_LEN];
 };
 
+struct wmi_vdev_ch_power_info {
+	u32 tlv_header;
+	u32 chan_cfreq; /* Channel center frequency (MHz) */
+	/* Unit: dBm, either PSD/EIRP power for this frequency or
+	 * incremental for non-PSD BW
+	 */
+	u32 tx_power;
+} __packed;
+
+struct wmi_vdev_set_tpc_power_cmd {
+	u32 tlv_header;
+	u32 vdev_id;
+	u32 psd_power; /* Value: 0 or 1, is PSD power or not */
+	u32 eirp_power; /* Maximum EIRP power (dBm units), valid only if power is PSD */
+	u32 power_type_6ghz; /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */
+	/* This fixed_param TLV is followed by the below TLVs:
+	 * num_pwr_levels of wmi_vdev_ch_power_info
+	 * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks).
+	 * For non-PSD power, the power values are for 20, 40, and till
+	 * BSS BW power levels.
+	 * The num_pwr_levels will be checked by sw how many elements present
+	 * in the variable-length array.
+	 */
+} __packed;
+
 #define WMI_IE_BITMAP_SIZE             8
 
 /* prefix used by scan requestor ids on the host */
@@ -3509,6 +3595,24 @@ struct wmi_get_pdev_temperature_cmd {
 	u32 pdev_id;
 } __packed;
 
+struct wmi_ftm_seg_hdr {
+	u32 len;
+	u32 msgref;
+	u32 segmentinfo;
+	u32 pdev_id;
+} __packed;
+
+struct wmi_ftm_cmd {
+	u32 tlv_header;
+	struct wmi_ftm_seg_hdr seg_hdr;
+	u8 data[];
+} __packed;
+
+struct wmi_ftm_event_msg {
+	struct wmi_ftm_seg_hdr seg_hdr;
+	u8 data[];
+} __packed;
+
 #define WMI_BEACON_TX_BUFFER_SIZE	512
 
 struct wmi_bcn_tmpl_cmd {
@@ -3681,6 +3785,7 @@ struct wmi_stop_scan_cmd {
 };
 
 struct scan_chan_list_params {
+	struct list_head list;
 	u32 pdev_id;
 	u16 nallchans;
 	struct channel_param ch_param[];
@@ -4039,6 +4144,7 @@ struct wmi_he_rate_set {
 
 #define MAX_REG_RULES 10
 #define REG_ALPHA2_LEN 2
+#define MAX_6GHZ_REG_RULES 5
 
 enum wmi_start_event_param {
 	WMI_VDEV_START_RESP_EVENT = 0,
@@ -4058,6 +4164,7 @@ struct wmi_vdev_start_resp_event {
 	};
 	u32 cfgd_tx_streams;
 	u32 cfgd_rx_streams;
+	s32 max_allowed_tx_power;
 } __packed;
 
 /* VDEV start response status codes */
@@ -4069,16 +4176,6 @@ enum wmi_vdev_start_resp_status_code {
 	WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN = 4,
 };
 
-;
-enum cc_setting_code {
-	REG_SET_CC_STATUS_PASS = 0,
-	REG_CURRENT_ALPHA2_NOT_FOUND = 1,
-	REG_INIT_ALPHA2_NOT_FOUND = 2,
-	REG_SET_CC_CHANGE_NOT_ALLOWED = 3,
-	REG_SET_CC_STATUS_NO_MEMORY = 4,
-	REG_SET_CC_STATUS_FAIL = 5,
-};
-
 /* Regaulatory Rule Flags Passed by FW */
 #define REGULATORY_CHAN_DISABLED     BIT(0)
 #define REGULATORY_CHAN_NO_IR        BIT(1)
@@ -4092,15 +4189,216 @@ enum cc_setting_code {
 #define REGULATORY_CHAN_NO_20MHZ     BIT(11)
 #define REGULATORY_CHAN_NO_10MHZ     BIT(12)
 
-enum {
+enum wmi_reg_chan_list_cmd_type {
+	WMI_REG_CHAN_LIST_CC_ID = 0,
+	WMI_REG_CHAN_LIST_CC_EXT_ID = 1,
+};
+
+enum wmi_reg_cc_setting_code {
 	WMI_REG_SET_CC_STATUS_PASS = 0,
 	WMI_REG_CURRENT_ALPHA2_NOT_FOUND = 1,
 	WMI_REG_INIT_ALPHA2_NOT_FOUND = 2,
 	WMI_REG_SET_CC_CHANGE_NOT_ALLOWED = 3,
 	WMI_REG_SET_CC_STATUS_NO_MEMORY = 4,
 	WMI_REG_SET_CC_STATUS_FAIL = 5,
+
+	/* add new setting code above, update in
+	 * @enum cc_setting_code as well.
+	 * Also handle it in ath11k_wmi_cc_setting_code_to_reg()
+	 */
+};
+
+enum cc_setting_code {
+	REG_SET_CC_STATUS_PASS = 0,
+	REG_CURRENT_ALPHA2_NOT_FOUND = 1,
+	REG_INIT_ALPHA2_NOT_FOUND = 2,
+	REG_SET_CC_CHANGE_NOT_ALLOWED = 3,
+	REG_SET_CC_STATUS_NO_MEMORY = 4,
+	REG_SET_CC_STATUS_FAIL = 5,
+
+	/* add new setting code above, update in
+	 * @enum wmi_reg_cc_setting_code as well.
+	 * Also handle it in ath11k_cc_status_to_str()
+	 */
+};
+
+static inline enum cc_setting_code
+ath11k_wmi_cc_setting_code_to_reg(enum wmi_reg_cc_setting_code status_code)
+{
+	switch (status_code) {
+	case WMI_REG_SET_CC_STATUS_PASS:
+		return REG_SET_CC_STATUS_PASS;
+	case WMI_REG_CURRENT_ALPHA2_NOT_FOUND:
+		return REG_CURRENT_ALPHA2_NOT_FOUND;
+	case WMI_REG_INIT_ALPHA2_NOT_FOUND:
+		return REG_INIT_ALPHA2_NOT_FOUND;
+	case WMI_REG_SET_CC_CHANGE_NOT_ALLOWED:
+		return REG_SET_CC_CHANGE_NOT_ALLOWED;
+	case WMI_REG_SET_CC_STATUS_NO_MEMORY:
+		return REG_SET_CC_STATUS_NO_MEMORY;
+	case WMI_REG_SET_CC_STATUS_FAIL:
+		return REG_SET_CC_STATUS_FAIL;
+	}
+
+	return REG_SET_CC_STATUS_FAIL;
+}
+
+static inline const char *ath11k_cc_status_to_str(enum cc_setting_code code)
+{
+	switch (code) {
+	case REG_SET_CC_STATUS_PASS:
+		return "REG_SET_CC_STATUS_PASS";
+	case REG_CURRENT_ALPHA2_NOT_FOUND:
+		return "REG_CURRENT_ALPHA2_NOT_FOUND";
+	case REG_INIT_ALPHA2_NOT_FOUND:
+		return "REG_INIT_ALPHA2_NOT_FOUND";
+	case REG_SET_CC_CHANGE_NOT_ALLOWED:
+		return "REG_SET_CC_CHANGE_NOT_ALLOWED";
+	case REG_SET_CC_STATUS_NO_MEMORY:
+		return "REG_SET_CC_STATUS_NO_MEMORY";
+	case REG_SET_CC_STATUS_FAIL:
+		return "REG_SET_CC_STATUS_FAIL";
+	}
+
+	return "Unknown CC status";
+}
+
+enum wmi_reg_6ghz_ap_type {
+	WMI_REG_INDOOR_AP = 0,
+	WMI_REG_STANDARD_POWER_AP = 1,
+	WMI_REG_VERY_LOW_POWER_AP = 2,
+
+	/* add AP type above, handle in ath11k_6ghz_ap_type_to_str()
+	 */
+	WMI_REG_CURRENT_MAX_AP_TYPE,
+	WMI_REG_MAX_AP_TYPE = 7,
 };
 
+static inline const char *
+ath11k_6ghz_ap_type_to_str(enum wmi_reg_6ghz_ap_type type)
+{
+	switch (type) {
+	case WMI_REG_INDOOR_AP:
+		return "INDOOR AP";
+	case WMI_REG_STANDARD_POWER_AP:
+		return "STANDARD POWER AP";
+	case WMI_REG_VERY_LOW_POWER_AP:
+		return "VERY LOW POWER AP";
+	case WMI_REG_CURRENT_MAX_AP_TYPE:
+		return "CURRENT_MAX_AP_TYPE";
+	case WMI_REG_MAX_AP_TYPE:
+		return "MAX_AP_TYPE";
+	}
+
+	return "unknown 6 GHz AP type";
+}
+
+enum wmi_reg_6ghz_client_type {
+	WMI_REG_DEFAULT_CLIENT = 0,
+	WMI_REG_SUBORDINATE_CLIENT = 1,
+	WMI_REG_MAX_CLIENT_TYPE = 2,
+
+	/* add client type above, handle it in
+	 * ath11k_6ghz_client_type_to_str()
+	 */
+};
+
+static inline const char *
+ath11k_6ghz_client_type_to_str(enum wmi_reg_6ghz_client_type type)
+{
+	switch (type) {
+	case WMI_REG_DEFAULT_CLIENT:
+		return "DEFAULT CLIENT";
+	case WMI_REG_SUBORDINATE_CLIENT:
+		return "SUBORDINATE CLIENT";
+	case WMI_REG_MAX_CLIENT_TYPE:
+		return "MAX_CLIENT_TYPE";
+	}
+
+	return "unknown 6 GHz client type";
+}
+
+enum reg_subdomains_6ghz {
+	EMPTY_6GHZ = 0x0,
+	FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01,
+	FCC1_CLIENT_SP_6GHZ = 0x02,
+	FCC1_AP_LPI_6GHZ = 0x03,
+	FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ,
+	FCC1_AP_SP_6GHZ = 0x04,
+	ETSI1_LPI_6GHZ = 0x10,
+	ETSI1_VLP_6GHZ = 0x11,
+	ETSI2_LPI_6GHZ = 0x12,
+	ETSI2_VLP_6GHZ = 0x13,
+	APL1_LPI_6GHZ = 0x20,
+	APL1_VLP_6GHZ = 0x21,
+
+	/* add sub-domain above, handle it in
+	 * ath11k_sub_reg_6ghz_to_str()
+	 */
+};
+
+static inline const char *
+ath11k_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz sub_id)
+{
+	switch (sub_id) {
+	case EMPTY_6GHZ:
+		return "N/A";
+	case FCC1_CLIENT_LPI_REGULAR_6GHZ:
+		return "FCC1_CLIENT_LPI_REGULAR_6GHZ";
+	case FCC1_CLIENT_SP_6GHZ:
+		return "FCC1_CLIENT_SP_6GHZ";
+	case FCC1_AP_LPI_6GHZ:
+		return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE";
+	case FCC1_AP_SP_6GHZ:
+		return "FCC1_AP_SP_6GHZ";
+	case ETSI1_LPI_6GHZ:
+		return "ETSI1_LPI_6GHZ";
+	case ETSI1_VLP_6GHZ:
+		return "ETSI1_VLP_6GHZ";
+	case ETSI2_LPI_6GHZ:
+		return "ETSI2_LPI_6GHZ";
+	case ETSI2_VLP_6GHZ:
+		return "ETSI2_VLP_6GHZ";
+	case APL1_LPI_6GHZ:
+		return "APL1_LPI_6GHZ";
+	case APL1_VLP_6GHZ:
+		return "APL1_VLP_6GHZ";
+	}
+
+	return "unknown sub reg id";
+}
+
+enum reg_super_domain_6ghz {
+	FCC1_6GHZ = 0x01,
+	ETSI1_6GHZ = 0x02,
+	ETSI2_6GHZ = 0x03,
+	APL1_6GHZ = 0x04,
+	FCC1_6GHZ_CL = 0x05,
+
+	/* add super domain above, handle it in
+	 * ath11k_super_reg_6ghz_to_str()
+	 */
+};
+
+static inline const char *
+ath11k_super_reg_6ghz_to_str(enum reg_super_domain_6ghz domain_id)
+{
+	switch (domain_id) {
+	case FCC1_6GHZ:
+		return "FCC1_6GHZ";
+	case ETSI1_6GHZ:
+		return "ETSI1_6GHZ";
+	case ETSI2_6GHZ:
+		return "ETSI2_6GHZ";
+	case APL1_6GHZ:
+		return "APL1_6GHZ";
+	case FCC1_6GHZ_CL:
+		return "FCC1_6GHZ_CL";
+	}
+
+	return "unknown domain id";
+}
+
 struct cur_reg_rule {
 	u16 start_freq;
 	u16 end_freq;
@@ -4108,6 +4406,8 @@ struct cur_reg_rule {
 	u8 reg_power;
 	u8 ant_gain;
 	u16 flags;
+	bool psd_flag;
+	s8 psd_eirp;
 };
 
 struct cur_regulatory_info {
@@ -4119,14 +4419,30 @@ struct cur_regulatory_info {
 	u8 alpha2[REG_ALPHA2_LEN + 1];
 	u32 dfs_region;
 	u32 phybitmap;
-	u32 min_bw_2g;
-	u32 max_bw_2g;
-	u32 min_bw_5g;
-	u32 max_bw_5g;
-	u32 num_2g_reg_rules;
-	u32 num_5g_reg_rules;
-	struct cur_reg_rule *reg_rules_2g_ptr;
-	struct cur_reg_rule *reg_rules_5g_ptr;
+	u32 min_bw_2ghz;
+	u32 max_bw_2ghz;
+	u32 min_bw_5ghz;
+	u32 max_bw_5ghz;
+	u32 num_2ghz_reg_rules;
+	u32 num_5ghz_reg_rules;
+	struct cur_reg_rule *reg_rules_2ghz_ptr;
+	struct cur_reg_rule *reg_rules_5ghz_ptr;
+	bool is_ext_reg_event;
+	enum wmi_reg_6ghz_client_type client_type;
+	bool rnr_tpe_usable;
+	bool unspecified_ap_usable;
+	u8 domain_code_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+	u8 domain_code_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+	u32 domain_code_6ghz_super_id;
+	u32 min_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+	u32 max_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+	u32 min_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+	u32 max_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+	u32 num_6ghz_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+	u32 num_6ghz_rules_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+	struct cur_reg_rule *reg_rules_6ghz_ap_ptr[WMI_REG_CURRENT_MAX_AP_TYPE];
+	struct cur_reg_rule *reg_rules_6ghz_client_ptr
+		[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
 };
 
 struct wmi_reg_chan_list_cc_event {
@@ -4138,12 +4454,12 @@ struct wmi_reg_chan_list_cc_event {
 	u32 domain_code;
 	u32 dfs_region;
 	u32 phybitmap;
-	u32 min_bw_2g;
-	u32 max_bw_2g;
-	u32 min_bw_5g;
-	u32 max_bw_5g;
-	u32 num_2g_reg_rules;
-	u32 num_5g_reg_rules;
+	u32 min_bw_2ghz;
+	u32 max_bw_2ghz;
+	u32 min_bw_5ghz;
+	u32 max_bw_5ghz;
+	u32 num_2ghz_reg_rules;
+	u32 num_5ghz_reg_rules;
 } __packed;
 
 struct wmi_regulatory_rule_struct {
@@ -4153,6 +4469,61 @@ struct wmi_regulatory_rule_struct {
 	u32  flag_info;
 };
 
+#define WMI_REG_CLIENT_MAX 4
+
+struct wmi_reg_chan_list_cc_ext_event {
+	u32 status_code;
+	u32 phy_id;
+	u32 alpha2;
+	u32 num_phy;
+	u32 country_id;
+	u32 domain_code;
+	u32 dfs_region;
+	u32 phybitmap;
+	u32 min_bw_2ghz;
+	u32 max_bw_2ghz;
+	u32 min_bw_5ghz;
+	u32 max_bw_5ghz;
+	u32 num_2ghz_reg_rules;
+	u32 num_5ghz_reg_rules;
+	u32 client_type;
+	u32 rnr_tpe_usable;
+	u32 unspecified_ap_usable;
+	u32 domain_code_6ghz_ap_lpi;
+	u32 domain_code_6ghz_ap_sp;
+	u32 domain_code_6ghz_ap_vlp;
+	u32 domain_code_6ghz_client_lpi[WMI_REG_CLIENT_MAX];
+	u32 domain_code_6ghz_client_sp[WMI_REG_CLIENT_MAX];
+	u32 domain_code_6ghz_client_vlp[WMI_REG_CLIENT_MAX];
+	u32 domain_code_6ghz_super_id;
+	u32 min_bw_6ghz_ap_sp;
+	u32 max_bw_6ghz_ap_sp;
+	u32 min_bw_6ghz_ap_lpi;
+	u32 max_bw_6ghz_ap_lpi;
+	u32 min_bw_6ghz_ap_vlp;
+	u32 max_bw_6ghz_ap_vlp;
+	u32 min_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX];
+	u32 max_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX];
+	u32 min_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX];
+	u32 max_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX];
+	u32 min_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX];
+	u32 max_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX];
+	u32 num_6ghz_reg_rules_ap_sp;
+	u32 num_6ghz_reg_rules_ap_lpi;
+	u32 num_6ghz_reg_rules_ap_vlp;
+	u32 num_6ghz_reg_rules_client_sp[WMI_REG_CLIENT_MAX];
+	u32 num_6ghz_reg_rules_client_lpi[WMI_REG_CLIENT_MAX];
+	u32 num_6ghz_reg_rules_client_vlp[WMI_REG_CLIENT_MAX];
+} __packed;
+
+struct wmi_regulatory_ext_rule {
+	u32 tlv_header;
+	u32 freq_info;
+	u32 bw_pwr_info;
+	u32 flag_info;
+	u32 psd_power_info;
+} __packed;
+
 struct wmi_vdev_delete_resp_event {
 	u32 vdev_id;
 } __packed;
@@ -4624,6 +4995,7 @@ struct ath11k_targ_cap {
 };
 
 enum wmi_vdev_type {
+	WMI_VDEV_TYPE_UNSPEC =  0,
 	WMI_VDEV_TYPE_AP      = 1,
 	WMI_VDEV_TYPE_STA     = 2,
 	WMI_VDEV_TYPE_IBSS    = 3,
@@ -5346,6 +5718,16 @@ struct target_resource_config {
 	u32 sched_params;
 	u32 twt_ap_pdev_count;
 	u32 twt_ap_sta_count;
+	u32 max_nlo_ssids;
+	u32 num_packet_filters;
+	u32 num_max_sta_vdevs;
+	u32 max_bssid_indicator;
+	u32 ul_resp_config;
+	u32 msdu_flow_override_config0;
+	u32 msdu_flow_override_config1;
+	u32 flags2;
+	u32 host_service_flags;
+	u8 is_reg_cc_ext_event_supported;
 };
 
 enum wmi_debug_log_param {
@@ -5966,6 +6348,105 @@ enum wmi_sta_keepalive_method {
 #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT	30
 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE	0
 
+#define UNIT_TEST_MAX_NUM_ARGS    8
+
+struct unit_test_cmd {
+	u32 vdev_id;
+	u32 module_id;
+	u32 num_args;
+	u32 args[UNIT_TEST_MAX_NUM_ARGS];
+};
+
+struct wmi_unit_test_cmd_fixed_param {
+	u32 tlv_header;
+	u32 vdev_id;
+	u32 module_id;
+	u32 num_args;
+	u32 diag_token;
+	/**
+	 * TLV (tag length value) parameters follow the wmi_unit_test_cmd_fixed_param
+	 * structure. The TLV's are:
+	 *     u32 args[];
+	 */
+} __packed;
+
+enum wmi_coex_config_type {
+    WMI_COEX_CONFIG_PAGE_P2P_TDM        =  1,
+    WMI_COEX_CONFIG_PAGE_STA_TDM        =  2,
+    WMI_COEX_CONFIG_PAGE_SAP_TDM        =  3,
+    WMI_COEX_CONFIG_DURING_WLAN_CONN    =  4,
+    WMI_COEX_CONFIG_BTC_ENABLE          =  5,
+    WMI_COEX_CONFIG_COEX_DBG            =  6,
+    WMI_COEX_CONFIG_PAGE_P2P_STA_TDM    =  7,
+    WMI_COEX_CONFIG_INQUIRY_P2P_TDM     =  8,
+    WMI_COEX_CONFIG_INQUIRY_STA_TDM     =  9,
+    WMI_COEX_CONFIG_INQUIRY_SAP_TDM     = 10,
+    WMI_COEX_CONFIG_INQUIRY_P2P_STA_TDM = 11,
+    WMI_COEX_CONFIG_TX_POWER            = 12,
+    WMI_COEX_CONFIG_PTA_CONFIG          = 13,
+    WMI_COEX_CONFIG_AP_TDM              = 14,
+    WMI_COEX_CONFIG_WLAN_SCAN_PRIORITY  = 15,
+    WMI_COEX_CONFIG_WLAN_PKT_PRIORITY   = 16,
+    WMI_COEX_CONFIG_PTA_INTERFACE       = 17,
+    WMI_COEX_CONFIG_BTC_DUTYCYCLE       = 18,
+    WMI_COEX_CONFIG_HANDOVER_RSSI       = 19,
+    WMI_COEX_CONFIG_PTA_BT_INFO         = 20,
+    WMI_COEX_CONFIG_SINK_WLAN_TDM       = 21,
+    WMI_COEX_CONFIG_COEX_ENABLE_MCC_TDM = 22,
+    WMI_COEX_CONFIG_LOWRSSI_A2DPOPP_TDM = 23,
+    WMI_COEX_CONFIG_BTC_MODE            = 24,
+    WMI_COEX_CONFIG_ANTENNA_ISOLATION   = 25,
+    WMI_COEX_CONFIG_BT_LOW_RSSI_THRESHOLD = 26,
+    WMI_COEX_CONFIG_BT_INTERFERENCE_LEVEL = 27,
+    WMI_COEX_CONFIG_WLAN_OVER_ZBLOW        = 28,
+    WMI_COEX_CONFIG_WLAN_MGMT_OVER_BT_A2DP = 29,
+    WMI_COEX_CONFIG_WLAN_CONN_OVER_LE      = 30,
+    WMI_COEX_CONFIG_LE_OVER_WLAN_TRAFFIC   = 31,
+    WMI_COEX_CONFIG_THREE_WAY_COEX_RESET   = 32,
+    WMI_COEX_CONFIG_THREE_WAY_DELAY_PARA   = 33,
+    WMI_COEX_CONFIG_THREE_WAY_COEX_START   = 34,
+    WMI_COEX_CONFIG_MPTA_HELPER_ENABLE     = 35,
+    WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_STATE = 36,
+    WMI_COEX_CONFIG_MPTA_HELPER_INT_OCS_PARAMS = 37,
+    WMI_COEX_CONFIG_MPTA_HELPER_MON_OCS_PARAMS   = 38,
+    WMI_COEX_CONFIG_MPTA_HELPER_INT_MON_DURATION = 39,
+    WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_CHANNEL   = 40,
+    WMI_COEX_CONFIG_MPTA_HELPER_WLAN_MUTE_DURATION   = 41,
+    WMI_COEX_CONFIG_BT_SCO_ALLOW_WLAN_2G_SCAN   = 42,
+    WMI_COEX_CONFIG_ENABLE_2ND_HARMONIC_WAR     = 43,
+    WMI_COEX_CONFIG_BTCOEX_SEPARATE_CHAIN_MODE  = 44,
+    WMI_COEX_CONFIG_ENABLE_TPUT_SHAPING = 45,
+    WMI_COEX_CONFIG_ENABLE_TXBF = 46,
+    WMI_COEX_CONFIG_FORCED_ALGO = 47,
+    WMI_COEX_CONFIG_LE_SCAN_POLICY = 48,
+};
+
+struct wmi_coex_config_params {
+	u32 vdev_id;
+	u32 config_type;
+	u32 config_arg1;
+	u32 config_arg2;
+	u32 config_arg3;
+	u32 config_arg4;
+	u32 config_arg5;
+	u32 config_arg6;
+};
+
+struct wmi_coex_config_cmd {
+	u32 tlv_header;
+	u32 vdev_id;
+	u32 config_type;
+	u32 config_arg1;
+	u32 config_arg2;
+	u32 config_arg3;
+	u32 config_arg4;
+	u32 config_arg5;
+	u32 config_arg6;
+} __packed;
+
+#define WMI_COEX_ISOLATION_ARG1_DEFAUT     30
+#define WMI_COEX_BTC_MODE_ARG1_DEFAULT	1
+
 int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
 			u32 cmd_id);
 struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
@@ -6129,6 +6610,8 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
 				 const u8 mac_addr[ETH_ALEN]);
 int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap,
 			     struct ath11k_fw_dbglog *dbglog);
+int ath11k_wmi_set_unit_test(struct ath11k *ar, struct unit_test_cmd *unit_test);
+int ath11k_wmi_send_coex_config(struct ath11k *ar, struct wmi_coex_config_params *param);
 int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
 			      struct wmi_pno_scan_req  *pno_scan);
 int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id);
@@ -6151,4 +6634,11 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar);
 int ath11k_wmi_sta_keepalive(struct ath11k *ar,
 			     const struct wmi_sta_keepalive_arg *arg);
 
+void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
+int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
+				struct cur_regulatory_info *reg_info,
+				enum ieee80211_ap_reg_power power_type);
+int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar,
+				       u32 vdev_id,
+				       struct ath11k_reg_tpc_power_info *param);
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c
index 1dec23b0699c..e94777f217cd 100644
--- a/drivers/net/wireless/ath/ath11k/wow.c
+++ b/drivers/net/wireless/ath/ath11k/wow.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -838,6 +838,7 @@ exit:
 		case ATH11K_STATE_RESTARTING:
 		case ATH11K_STATE_RESTARTED:
 		case ATH11K_STATE_WEDGED:
+		case ATH11K_STATE_TM:
 			ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
 				    ar->state);
 			ret = -EIO;
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 9b77f49b3560..6445528f8756 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -415,12 +415,15 @@ static int check_root_hub_suspended(struct device *dev)
 	return 0;
 }
 
-static int suspend_common(struct device *dev, bool do_wakeup)
+static int suspend_common(struct device *dev, pm_message_t msg)
 {
 	struct pci_dev		*pci_dev = to_pci_dev(dev);
 	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
+	bool			do_wakeup;
 	int			retval;
 
+	do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev);
+
 	/* Root hub suspend should have stopped all downstream traffic,
 	 * and all bus master traffic.  And done so for both the interface
 	 * and the stub usb_device (which we check here).  But maybe it
@@ -447,7 +450,7 @@ static int suspend_common(struct device *dev, bool do_wakeup)
 				(retval == 0 && do_wakeup && hcd->shared_hcd &&
 				 HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
 			if (hcd->driver->pci_resume)
-				hcd->driver->pci_resume(hcd, false);
+				hcd->driver->pci_resume(hcd, msg);
 			retval = -EBUSY;
 		}
 		if (retval)
@@ -470,7 +473,7 @@ static int suspend_common(struct device *dev, bool do_wakeup)
 	return retval;
 }
 
-static int resume_common(struct device *dev, int event)
+static int resume_common(struct device *dev, pm_message_t msg)
 {
 	struct pci_dev		*pci_dev = to_pci_dev(dev);
 	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
@@ -498,12 +501,11 @@ static int resume_common(struct device *dev, int event)
 		 * No locking is needed because PCI controller drivers do not
 		 * get unbound during system resume.
 		 */
-		if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME)
+		if (pci_dev->class == CL_EHCI && msg.event != PM_EVENT_AUTO_RESUME)
 			for_each_companion(pci_dev, hcd,
 					ehci_wait_for_companions);
 
-		retval = hcd->driver->pci_resume(hcd,
-				event == PM_EVENT_RESTORE);
+		retval = hcd->driver->pci_resume(hcd, msg);
 		if (retval) {
 			dev_err(dev, "PCI post-resume error %d!\n", retval);
 			usb_hc_died(hcd);
@@ -516,7 +518,7 @@ static int resume_common(struct device *dev, int event)
 
 static int hcd_pci_suspend(struct device *dev)
 {
-	return suspend_common(dev, device_may_wakeup(dev));
+	return suspend_common(dev, PMSG_SUSPEND);
 }
 
 static int hcd_pci_suspend_noirq(struct device *dev)
@@ -566,12 +568,12 @@ static int hcd_pci_resume_noirq(struct device *dev)
 
 static int hcd_pci_resume(struct device *dev)
 {
-	return resume_common(dev, PM_EVENT_RESUME);
+	return resume_common(dev, PMSG_RESUME);
 }
 
 static int hcd_pci_restore(struct device *dev)
 {
-	return resume_common(dev, PM_EVENT_RESTORE);
+	return resume_common(dev, PMSG_RESTORE);
 }
 
 #else
@@ -588,7 +590,7 @@ static int hcd_pci_runtime_suspend(struct device *dev)
 {
 	int	retval;
 
-	retval = suspend_common(dev, true);
+	retval = suspend_common(dev, PMSG_AUTO_SUSPEND);
 	if (retval == 0)
 		powermac_set_asic(to_pci_dev(dev), 0);
 	dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
@@ -600,7 +602,7 @@ static int hcd_pci_runtime_resume(struct device *dev)
 	int	retval;
 
 	powermac_set_asic(to_pci_dev(dev), 1);
-	retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
+	retval = resume_common(dev, PMSG_AUTO_RESUME);
 	dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
 	return retval;
 }
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 17f8b6ea0c35..4370a2744852 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -354,10 +354,11 @@ done:
  * Also they depend on separate root hub suspend/resume.
  */
 
-static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+static int ehci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
 	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);
+	bool			hibernated = (msg.event == PM_EVENT_RESTORE);
 
 	if (ehci_resume(hcd, hibernated) != 0)
 		(void) ehci_pci_reinit(ehci, pdev);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index d7b4f40f9ff4..900ea0d368e0 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -301,6 +301,12 @@ static struct pci_driver ohci_pci_driver = {
 #endif
 };
 
+#ifdef CONFIG_PM
+static int ohci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
+{
+	return ohci_resume(hcd, msg.event == PM_EVENT_RESTORE);
+}
+#endif
 static int __init ohci_pci_init(void)
 {
 	if (usb_disabled())
@@ -311,7 +317,7 @@ static int __init ohci_pci_init(void)
 #ifdef	CONFIG_PM
 	/* Entries for the PCI suspend/resume callbacks are special */
 	ohci_pci_hc_driver.pci_suspend = ohci_suspend;
-	ohci_pci_hc_driver.pci_resume = ohci_resume;
+	ohci_pci_hc_driver.pci_resume = ohci_pci_resume;
 #endif
 
 	return pci_register_driver(&ohci_pci_driver);
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 7bd2fddde770..5edf6a08cf82 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -169,7 +169,7 @@ static void uhci_shutdown(struct pci_dev *pdev)
 
 #ifdef CONFIG_PM
 
-static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated);
+static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t state);
 
 static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 {
@@ -204,14 +204,15 @@ done_okay:
 
 	/* Check for race with a wakeup request */
 	if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
-		uhci_pci_resume(hcd, false);
+		uhci_pci_resume(hcd, PMSG_SUSPEND);
 		rc = -EBUSY;
 	}
 	return rc;
 }
 
-static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
 {
+	bool hibernated = (msg.event == PM_EVENT_RESTORE);
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
 	dev_dbg(uhci_dev(uhci), "%s\n", __func__);
diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c
index 08369857686e..91ce97821de5 100644
--- a/drivers/usb/host/xhci-histb.c
+++ b/drivers/usb/host/xhci-histb.c
@@ -367,7 +367,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev)
 	if (!device_may_wakeup(dev))
 		xhci_histb_host_enable(histb);
 
-	return xhci_resume(xhci, 0);
+	return xhci_resume(xhci, PMSG_RESUME);
 }
 
 static const struct dev_pm_ops xhci_histb_pm_ops = {
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 2aed88c28ef6..959213bed6ef 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -646,7 +646,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 	return ret;
 }
 
-static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
 {
 	struct xhci_hcd		*xhci = hcd_to_xhci(hcd);
 	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);
@@ -681,7 +681,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 	if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
 		xhci_pme_quirk(hcd);
 
-	retval = xhci_resume(xhci, hibernated);
+	retval = xhci_resume(xhci, msg);
 	return retval;
 }
 
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 5fb55bf19493..820b77cd7eac 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -464,7 +464,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	ret = xhci_resume(xhci, 0);
+	ret = xhci_resume(xhci, PMSG_RESUME);
 	if (ret)
 		return ret;
 
@@ -493,7 +493,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
 	struct usb_hcd  *hcd = dev_get_drvdata(dev);
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
-	return xhci_resume(xhci, 0);
+	return xhci_resume(xhci, PMSG_AUTO_RESUME);
 }
 
 static const struct dev_pm_ops xhci_plat_pm_ops = {
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 51eabc5e8770..bac9a447fffd 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2085,7 +2085,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
 	if (wakeup)
 		tegra_xhci_disable_phy_sleepwalk(tegra);
 
-	err = xhci_resume(xhci, 0);
+	err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME);
 	if (err < 0) {
 		dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
 		goto disable_phy;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index c02ad4f76bb3..e72a04f6153b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1124,8 +1124,9 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
  * This is called when the machine transition from S3/S4 mode.
  *
  */
-int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
+int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
 {
+	bool			hibernated = (msg.event == PM_EVENT_RESTORE);
 	u32			command, temp = 0;
 	struct usb_hcd		*hcd = xhci_to_hcd(xhci);
 	int			retval = 0;
@@ -1282,7 +1283,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 		 * the first wake signalling failed, give it that chance.
 		 */
 		pending_portevent = xhci_pending_portevent(xhci);
-		if (!pending_portevent) {
+		if (!pending_portevent && msg.event == PM_EVENT_AUTO_RESUME) {
 			msleep(120);
 			pending_portevent = xhci_pending_portevent(xhci);
 		}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 1354310cb37b..13f2d8e6a6d2 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2133,7 +2133,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
 int xhci_ext_cap_init(struct xhci_hcd *xhci);
 
 int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
-int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
+int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg);
 
 irqreturn_t xhci_irq(struct usb_hcd *hcd);
 irqreturn_t xhci_msi_irq(int irq, void *hcd);