diff --git drivers/bluetooth/Kconfig drivers/bluetooth/Kconfig
index e30707405455..744a3ca8678c 100644
--- drivers/bluetooth/Kconfig
+++ drivers/bluetooth/Kconfig
@@ -347,6 +347,41 @@ config BT_HCIVHCI
 	  Say Y here to compile support for virtual HCI devices into the
 	  kernel or say M to compile it as module (hci_vhci).
 
+config BT_TI
+	tristate "TI Bluetooth driver support"
+	help
+	  The core driver to support TI Bluetooth devices.
+
+	  Say Y here to compile TI Bluetooth driver
+	  into the kernel or say M to compile it as module.
+
+config BT_TI_SDIO
+	tristate "TI BT-over-SDIO driver"
+	depends on BT_TI && MMC
+	select FW_LOADER
+	select WANT_DEV_COREDUMP
+	help
+	  The driver for TI Bluetooth chipsets with SDIO interface.
+
+	  This driver is required if you want to use TI Bluetooth
+	  devices with SDIO interface.
+	  chipsets are supported.
+
+	  Say Y here to compile support for TI BT-over-SDIO driver
+	  into the kernel or say M to compile it as module.
+
+config BT_TI_UART
+	tristate "TI BT-over-UART driver"
+	depends on SERIAL_DEV_BUS
+	help
+	  Bluetooth HCI UART driver for CC33XX TI devices.
+	  This driver is required if you want to use CC33XX device with
+	  UART interface.
+	  Currently does not require BT_TI core.
+
+	  Say Y here to compile support for CC33XX UART devices into the
+	  kernel or say M to compile it as module (btuart).
+
 config BT_MRVL
 	tristate "Marvell Bluetooth driver support"
 	help
diff --git drivers/bluetooth/Makefile drivers/bluetooth/Makefile
index 3321a8aea4a0..d552dc5b11ec 100644
--- drivers/bluetooth/Makefile
+++ drivers/bluetooth/Makefile
@@ -17,6 +17,9 @@ obj-$(CONFIG_BT_HCIBTSDIO)	+= btsdio.o
 
 obj-$(CONFIG_BT_INTEL)		+= btintel.o
 obj-$(CONFIG_BT_ATH3K)		+= ath3k.o
+obj-$(CONFIG_BT_TI)			+= btti.o
+obj-$(CONFIG_BT_TI_SDIO)	+= btti_sdio.o
+obj-$(CONFIG_BT_TI_UART)	+= btti_uart.o
 obj-$(CONFIG_BT_MRVL)		+= btmrvl.o
 obj-$(CONFIG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
 obj-$(CONFIG_BT_MTKSDIO)	+= btmtksdio.o
@@ -33,6 +36,9 @@ obj-$(CONFIG_BT_HCIUART_NOKIA)	+= hci_nokia.o
 
 obj-$(CONFIG_BT_HCIRSI)		+= btrsi.o
 
+btti-y			:= btti_main.o
+btti-$(CONFIG_DEBUG_FS)	+= btti_debugfs.o
+
 btmrvl-y			:= btmrvl_main.o
 btmrvl-$(CONFIG_DEBUG_FS)	+= btmrvl_debugfs.o
 
diff --git drivers/bluetooth/btti_debugfs.c drivers/bluetooth/btti_debugfs.c
new file mode 100644
index 000000000000..4ccc324e5ded
--- /dev/null
+++ drivers/bluetooth/btti_debugfs.c
@@ -0,0 +1,156 @@
+/*
+ * This file is part of TI BLE over SDIO
+ *
+ * Copyright (C) 2022 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btti_drv.h"
+
+struct btti_debugfs_dir {
+	struct dentry *config_dir;
+	struct dentry *status_dir;
+};
+
+int btti_debugfs_if_prepare_command(u8 cmd_type,\
+		struct btti_private *private_data);
+
+
+#define DEBUGFS_FORMAT_BUFFER_SIZE 256
+
+int btti_format_buffer(char __user *userbuf, size_t count,
+			 loff_t *ppos, char *fmt, ...)
+{
+	va_list args;
+	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
+	int res;
+
+	va_start(args, fmt);
+	res = vscnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+
+//ble_enable
+static ssize_t ble_enable_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btti_private *private_data = file->private_data;
+	char buf[16];
+	int ret;
+	BT_INFO("[bt sdio] ble enable read");
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", \
+			private_data->hci_adapter->ble_enable);
+
+	return btti_format_buffer(userbuf, count,  ppos, "%d\n", \
+			private_data->hci_adapter->ble_enable);
+}
+
+static ssize_t ble_enable_write(struct file *file,
+				const char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct btti_private *private_data = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
+	BT_INFO("[bt sdio] ble enable write: %ld", value);
+
+	if(!private_data || !private_data->hci_adapter){
+		BT_ERR("[bt sdio] driver is not ready");
+	}
+
+	if (private_data->hci_adapter->ble_enable) {
+		BT_WARN("[bt sdio] ble_enable is already %d",\
+				private_data->hci_adapter->ble_enable);
+		return -EINVAL;
+	}
+
+	if (value != 1) {
+		BT_WARN("illegal value in ble_enable "\
+				"(only value allowed is is 1)");
+		BT_WARN("ble_enable cant be disabled after being enabled.");
+		return -EINVAL;
+	}
+
+	ret = btti_debugfs_if_prepare_command((u8)CMD_TYPE_BLE_ENABLE,\
+			private_data);
+
+	return count;
+}
+
+
+
+//ble_enable
+
+static const struct file_operations ble_enable_ops = {
+	.read = ble_enable_read,
+	.write = ble_enable_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+
+void btti_debugfs_init(struct hci_dev *hdev)
+{
+	struct btti_private *private_data = hci_get_drvdata(hdev);
+	struct btti_debugfs_dir *dbg;
+	struct dentry *root_cfg_dir;
+
+	if (!hdev->debugfs)
+		return;
+
+	dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+	private_data->debugfs_dir_vals = dbg;
+
+	if (!dbg) {
+		BT_ERR("Can not allocate memory for btti_debugfs_dir.");
+		return;
+	}
+
+	dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
+	root_cfg_dir = dbg->config_dir;
+
+	debugfs_create_file("ble_enable", 0744, root_cfg_dir,  private_data,\
+			&ble_enable_ops);
+
+	//dbg->status_dir = debugfs_create_dir("cc3xx_status", hdev->debugfs);
+}
+
+void btti_debugfs_remove(struct hci_dev *hdev)
+{
+	struct btti_private *private_data = hci_get_drvdata(hdev);
+	struct btti_debugfs_dir *dbgfs = private_data->debugfs_dir_vals;
+
+	if (!dbgfs)
+		return;
+
+	if(dbgfs->config_dir)
+	{
+		debugfs_remove_recursive(dbgfs->config_dir);
+	}
+	if(dbgfs->status_dir)
+	{
+		debugfs_remove_recursive(dbgfs->status_dir);
+	}
+
+	kfree(dbgfs);
+}
diff --git drivers/bluetooth/btti_drv.h drivers/bluetooth/btti_drv.h
new file mode 100644
index 000000000000..78fdf7f7f7b9
--- /dev/null
+++ drivers/bluetooth/btti_drv.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of TI BLE over SDIO
+ *
+ * Copyright (C) 2022 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <net/bluetooth/bluetooth.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_irq.h>
+
+
+#define BT_SDIO_HEADER_LEN			4
+#define BT_SDIO_UPLD_SIZE			(251+BT_SDIO_HEADER_LEN)
+
+#define CMD_TYPE_BLE_ENABLE         1
+
+
+
+struct btti_worker_thread {
+	struct task_struct *task;
+	wait_queue_head_t wait_queue;
+	void *private_data;
+};
+
+struct btti_device {
+	void *sdiodev; //see btti_sdio_dev
+	struct hci_dev *hcidev;
+	u8 dev_type;
+};
+
+struct btti_hci_adapter {
+	u32 num_of_interrupt;
+	struct sk_buff_head tx_queue;
+	u8 ble_enable;
+	u8 enable_autosuspend;
+	bool is_suspended;
+	bool is_suspending;
+};
+
+struct btti_private {
+	struct btti_device btti_dev;
+	struct btti_hci_adapter *hci_adapter;
+	struct btti_worker_thread work_thread;
+	int (*card_tx_packet_funcp)\
+			(struct btti_private *private_data,struct sk_buff *skb);
+	int (*card_power_up_firmware_funcp)(struct btti_private *private_data);
+	int (*card_power_dn_firmware_funcp)(struct btti_private *private_data);
+	int (*card_process_rx_funcp)(struct btti_private *private_data);
+	spinlock_t irq_cnt_lock;		/* spinlock used by driver */
+#ifdef CONFIG_DEBUG_FS
+	void *debugfs_dir_vals;
+#endif
+	bool sdio_dev_removed;
+};
+
+/* Prototype of global function */
+int btti_hci_register_hdev(struct btti_private *private_data);
+struct btti_private *btti_hci_add_sdio_dev(void *sdiodev);
+int btti_hci_remove_sdio_dev(struct btti_private *private_data);
+void btti_hci_irq_handler(struct btti_private *private_data);
+
+#ifdef CONFIG_DEBUG_FS
+void btti_debugfs_init(struct hci_dev *hdev);
+void btti_debugfs_remove(struct hci_dev *hdev);
+#endif
diff --git drivers/bluetooth/btti_main.c drivers/bluetooth/btti_main.c
new file mode 100644
index 000000000000..6857d8b90df0
--- /dev/null
+++ drivers/bluetooth/btti_main.c
@@ -0,0 +1,482 @@
+/*
+ * This file is part of TI BLE over SDIO
+ *
+ * Copyright (C) 2022 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "btti_drv.h"
+#include "btti_sdio.h"
+
+#define VERSION "ti_1.0"
+
+#define BTTI_SDIO_AUTOSUSPEND_DELAY	8000
+
+static int btti_hci_is_ble_enabled(struct btti_private *private_data);
+
+
+/*
+ * This function is called by the interrupt handler.
+ * It wakes up the worker thread.to make the RX reception.
+ */
+void btti_hci_irq_handler(struct btti_private *private_data)
+{
+	ulong flags;
+
+	spin_lock_irqsave(&private_data->irq_cnt_lock, flags);
+	private_data->hci_adapter->num_of_interrupt++;
+    spin_unlock_irqrestore(&private_data->irq_cnt_lock, flags);
+
+    //  wakeup to btti_service_work_thread
+	wake_up_interruptible(&private_data->work_thread.wait_queue);
+}
+EXPORT_SYMBOL_GPL(btti_hci_irq_handler);
+
+
+int btti_debugfs_if_prepare_command(u8 cmd_type,\
+		struct btti_private *private_data)
+{
+	int ret = 0;
+	switch(cmd_type){
+		case CMD_TYPE_BLE_ENABLE:
+			BT_INFO("[bt sdio hci] "\
+					"btti_debugfs_if_prepare_command "\
+					" CMD_TYPE_BLE_ENABLE ");
+			ret = btti_hci_is_ble_enabled(private_data);
+		break;
+		default:
+			break;
+	}
+	return ret;
+}
+
+static int btti_hci_tx_pkt(struct btti_private *private_data,\
+		struct sk_buff *skb)
+{
+	int ret = 0;
+
+	if (!skb || !skb->data)
+		return -EINVAL;
+
+	if (!skb->len || ((skb->len + BT_SDIO_HEADER_LEN) > BT_SDIO_UPLD_SIZE))
+	{
+		BT_ERR("[bt sdio hci] TX Error: Bad skb length %d : %d",
+						skb->len, BT_SDIO_UPLD_SIZE);
+		return -EINVAL;
+	}
+
+	skb_push(skb, BT_SDIO_HEADER_LEN);
+
+	/* header type: byte[3]
+	 * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor
+	 * header length: byte[2][1][0]
+	 */
+
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = hci_skb_pkt_type(skb);
+
+
+	BT_DBG("[bt sdio hci] TX buff 0x: %*ph", skb->len, skb->data);
+
+
+	if (private_data->card_tx_packet_funcp)
+		ret = private_data->card_tx_packet_funcp(private_data,\
+				skb);//btti_sdio_tx_packet
+
+	return ret;
+}
+
+static void btti_hci_init_hci_adapter(struct btti_private *private_data)
+{
+
+	skb_queue_head_init(&private_data->hci_adapter->tx_queue);
+	private_data->hci_adapter->enable_autosuspend = true;
+}
+
+static void btti_hci_free_hci_adapter(struct btti_private *private_data)
+{
+	skb_queue_purge(&private_data->hci_adapter->tx_queue);
+	kfree(private_data->hci_adapter);
+
+	private_data->hci_adapter = NULL;
+}
+
+
+
+
+//it is on purpose without lock ,
+//worse case card_ble_verify_if_ble_enable_funcp will be called more than once
+static int btti_hci_is_ble_enabled(struct btti_private *private_data)
+{
+        //if ble_enabled -- return 0
+	return( private_data->hci_adapter->ble_enable);
+}
+
+
+static int btti_hci_if_open(struct hci_dev *hdev)
+{
+	BT_DBG("[bt sdio hci] btti_hci_if_open");
+	return 0;
+}
+
+static int btti_hci_if_close(struct hci_dev *hdev)
+{
+	struct btti_private *private_data = hci_get_drvdata(hdev);
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	BT_DBG("[bt sdio hci] btti_hci_if_close");
+
+	pm_runtime_disable(&sdiodev->func->dev);
+	skb_queue_purge(&private_data->hci_adapter->tx_queue);
+
+	return 0;
+}
+
+static int btti_hci_if_flush(struct hci_dev *hdev)
+{
+	struct btti_private *private_data = hci_get_drvdata(hdev);
+	BT_DBG("[bt sdio hci] btti_hci_if_flush");
+
+	skb_queue_purge(&private_data->hci_adapter->tx_queue);
+
+	return 0;
+}
+static int btti_hci_if_tx_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct btti_private *private_data = hci_get_drvdata(hdev);
+
+	BT_INFO("[bt sdio hci] TX from HCI received ,type=%d,"\
+			" opcode: 0x%x len=%d ble_enable=%d",
+			hci_skb_pkt_type(skb), hci_skb_opcode(skb),\
+			skb->len,private_data->hci_adapter->ble_enable);
+
+	if (private_data->hci_adapter->is_suspending\
+			|| private_data->hci_adapter->is_suspended) {
+		BT_ERR("[bt sdio hci] %s:"\
+				" Device is suspending or suspended", __func__);
+		goto fail;
+	}
+
+	//check of ble is enabled
+	if(!btti_hci_is_ble_enabled(private_data)){
+		BT_INFO("[bt sdio hci] ble is not enabled");
+		goto fail;
+	}
+
+
+	switch (hci_skb_pkt_type(skb)) {
+	case HCI_COMMAND_PKT:
+		if(hdev){
+			hdev->stat.cmd_tx++;
+		}
+		break;
+
+	case HCI_ACLDATA_PKT:
+		if(hdev){
+			hdev->stat.acl_tx++;
+		}
+		break;
+
+	case HCI_SCODATA_PKT:
+		BT_WARN("[bt sdio hci] HCI_SCODATA_PKT not supported");
+		if(hdev){
+			hdev->stat.sco_tx++;
+		}
+		break;
+
+	default:
+		BT_ERR("[bt sdio hci] unknown packet type :%d",\
+				hci_skb_pkt_type(skb));
+		return -EILSEQ;
+
+	}
+
+
+	skb_queue_tail(&private_data->hci_adapter->tx_queue, skb);
+
+	if (!private_data->hci_adapter->is_suspended){
+		wake_up_interruptible(&private_data->work_thread.wait_queue);
+	}else{
+		skb_dequeue_tail(&private_data->hci_adapter->tx_queue);
+		BT_INFO("[bt sdio hci] Device is suspending");
+		goto fail;
+	}
+
+	return 0;
+fail:
+    //in case of error HCI frees the skb
+	return -EIO;
+}
+
+
+
+static int btti_hci_if_setup(struct hci_dev *hdev)
+{
+	struct btti_private *private_data = hci_get_drvdata(hdev);
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+
+	BT_INFO("[bt sdio hci] btti_hci_if_setup");
+	pm_runtime_set_autosuspend_delay(&sdiodev->func->dev,
+			BTTI_SDIO_AUTOSUSPEND_DELAY);
+	pm_runtime_use_autosuspend(&sdiodev->func->dev);
+
+	pm_runtime_set_active(&sdiodev->func->dev);
+
+	/* Default forbid runtime auto suspend
+	 */
+	pm_runtime_forbid(&sdiodev->func->dev);
+	pm_runtime_enable(&sdiodev->func->dev);
+
+
+	//auto suspend is allowed
+	if (private_data->hci_adapter\
+			&& private_data->hci_adapter->enable_autosuspend){
+		BT_DBG("[bt sdio hci] btti_hci_if_setup , enable auto suspend");
+		pm_runtime_allow(&sdiodev->func->dev);
+	}
+	return 0;
+}
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ * work_thread.wait_queue
+ */
+static int btti_service_work_thread(void *data)
+{
+	struct btti_worker_thread *thread = data;
+	struct btti_private *private_data = thread->private_data;
+	struct btti_hci_adapter *hci_adapter = private_data->hci_adapter;
+	wait_queue_entry_t wait;
+	struct sk_buff *skb;
+	ulong flags;
+
+	BT_INFO("[bt sdio hci] work thread is started");
+
+	init_waitqueue_entry(&wait, current);
+
+	for (;;) {
+		add_wait_queue(&thread->wait_queue, &wait);
+
+		if (kthread_should_stop() || private_data->sdio_dev_removed) {
+			BT_DBG("[bt sdio hci]work_thread: "\
+					" thread is need to stopped");
+			break;
+		}
+
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		//if no RX and queue is empty or ble is not enabled
+		if ((!private_data->hci_adapter->ble_enable) ||
+				((!hci_adapter->num_of_interrupt) &&\
+						skb_queue_empty\
+						(&hci_adapter->tx_queue))) {
+			BT_INFO("[bt sdio hci] work thread is sleeping...");
+			schedule();
+		}
+
+		set_current_state(TASK_RUNNING);
+
+		remove_wait_queue(&thread->wait_queue, &wait);
+
+		BT_DBG("[bt sdio hci] work thread woke up");
+
+		if (kthread_should_stop() || private_data->sdio_dev_removed) {
+			BT_INFO("[bt sdio hci] work_thread:"\
+					" break from main thread");
+			break;
+		}
+
+		//handle the RX, process the interrupt function
+		spin_lock_irqsave(&private_data->irq_cnt_lock, flags);
+		if (hci_adapter->num_of_interrupt) {
+			hci_adapter->num_of_interrupt = 0;
+			spin_unlock_irqrestore(&private_data->irq_cnt_lock,\
+					flags);
+			if(private_data->card_process_rx_funcp){
+				private_data->card_process_rx_funcp\
+				(private_data);//call to btti_sdio_process_rx
+			}
+		} else {
+			spin_unlock_irqrestore(&private_data->irq_cnt_lock,\
+					flags);
+		}
+
+		if (private_data->hci_adapter->is_suspended)
+		{
+			BT_INFO("[bt sdio hci] work thread not available,"\
+					" is_suspended:%d",
+					private_data->hci_adapter->is_suspended);
+			continue;
+		}
+		//handle the TX
+		skb = skb_dequeue(&hci_adapter->tx_queue);
+		if (skb) {
+			if (btti_hci_tx_pkt(private_data, skb)){
+				//handle tx packet
+				BT_ERR("[bt sdio hci] TX , error send packet");
+			}
+		}
+	}
+
+	return 0;
+}
+
+int btti_hci_register_hdev(struct btti_private *private_data)
+
+{
+	struct hci_dev *hdev = NULL;
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	int ret;
+	BT_INFO("[bt sdio hci] btti_hci_register_hdev");
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("[bt sdio hci] Can not allocate HCI device");
+		goto err_hdev;
+	}
+
+	private_data->btti_dev.hcidev = hdev;
+	hci_set_drvdata(hdev, private_data);
+
+	hdev->bus   = HCI_SDIO;
+	hdev->open  = btti_hci_if_open;
+	hdev->close = btti_hci_if_close;
+	hdev->flush = btti_hci_if_flush;
+	hdev->send  = btti_hci_if_tx_frame;
+	hdev->setup = btti_hci_if_setup;
+	SET_HCIDEV_DEV(hdev, &sdiodev->func->dev);
+
+	set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+
+	hdev->dev_type = HCI_PRIMARY;
+
+	ret = hci_register_dev(hdev);
+	if (ret < 0) {
+		BT_ERR("[bt sdio hci] Can not register HCI device");
+		goto err_hci_register_dev;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	btti_debugfs_init(hdev);
+#endif
+
+	return 0;
+
+err_hci_register_dev:
+	hci_free_dev(hdev);
+
+err_hdev:
+	/* Stop the thread servicing the interrupts */
+	kthread_stop(private_data->work_thread.task);
+
+	btti_hci_free_hci_adapter(private_data);
+	kfree(private_data);
+
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(btti_hci_register_hdev);
+
+struct btti_private *btti_hci_add_sdio_dev(void *sdiodev)
+{
+	struct btti_private *private_data;
+	BT_INFO("[bt sdio hci] btti_hci_add_sdio_dev");
+
+
+	private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+	if (!private_data) {
+		BT_ERR("[bt sdio hci] Can not allocate private_data");
+		goto err_priv;
+	}
+
+	private_data->hci_adapter = \
+			kzalloc(sizeof(*private_data->hci_adapter), GFP_KERNEL);
+	if (!private_data->hci_adapter) {
+		BT_ERR("[bt sdio hci] Allocate buffer"\
+				" for btti_hci_adapter failed!");
+		goto err_hci_adapter;
+	}
+
+	btti_hci_init_hci_adapter(private_data);
+
+	BT_INFO("[bt sdio hci] Starting work thread...");
+	private_data->work_thread.private_data = private_data;
+	spin_lock_init(&private_data->irq_cnt_lock);
+
+	init_waitqueue_head(&private_data->work_thread.wait_queue);
+	private_data->work_thread.task = kthread_run(btti_service_work_thread,
+				&private_data->work_thread,\
+					"btti_main_service");
+	if (IS_ERR(private_data->work_thread.task))
+		goto err_thread;
+
+	private_data->btti_dev.sdiodev = sdiodev;
+	return private_data;
+
+err_thread:
+	btti_hci_free_hci_adapter(private_data);
+
+err_hci_adapter:
+	kfree(private_data);
+
+err_priv:
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(btti_hci_add_sdio_dev);
+
+int btti_hci_remove_sdio_dev(struct btti_private *private_data)
+{
+	struct hci_dev *hdev;
+
+	BT_INFO("[bt sdio hci] remove sdio dev");
+
+	hdev = private_data->btti_dev.hcidev;
+
+	if(private_data->work_thread.task)
+		kthread_stop(private_data->work_thread.task);
+
+
+	if(hdev)
+	{
+		BT_INFO("[bt sdio hci] unregister hci");
+#ifdef CONFIG_DEBUG_FS
+		btti_debugfs_remove(hdev);
+#endif
+		hci_unregister_dev(hdev);
+		hci_free_dev(hdev);
+	}
+
+	private_data->btti_dev.hcidev = NULL;
+
+	btti_hci_free_hci_adapter(private_data);
+
+	kfree(private_data);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btti_hci_remove_sdio_dev);
+
+MODULE_AUTHOR("Texas Instruments.");
+MODULE_DESCRIPTION("Texas Instruments Bluetooth driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git drivers/bluetooth/btti_sdio.c drivers/bluetooth/btti_sdio.c
new file mode 100644
index 000000000000..4f8fda6209b9
--- /dev/null
+++ drivers/bluetooth/btti_sdio.c
@@ -0,0 +1,1023 @@
+/*
+ * This file is part of TI BLE over SDIO
+ *
+ * Copyright (C) 2022 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
+#include <linux/devcoredump.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btti_drv.h"
+#include "btti_sdio.h"
+
+#define VERSION "ti_1.0"
+
+#define SDIO_DEVICE_ID_TI_CC33XX	0x4077
+
+
+static void btti_sdio_if_remove(struct sdio_func *func);
+static int __maybe_unused btti_sdio_unregister_dev(struct sdio_func *func);
+static int btti_sdio_rx_packet(struct btti_private *private_dataate_data);
+static int btti_sdio_tx_packet(struct btti_private *private_data,
+		struct sk_buff *skb);
+static void btti_sdio_irq_handler(struct sdio_func *func);
+static int btti_sdio_power_up_fw(struct btti_private *private_data);
+static int btti_handle_rx_vendor_event(struct btti_private *private_data,
+		struct sk_buff *skb );
+static void btti_acknldg_packet(struct btti_sdio_dev *sdiodev, u8 Ack);
+
+
+
+static const struct of_device_id btti_sdio_of_match_table[] = {
+	{ .compatible = "ti,cc33xxbt" },
+	{ }
+};
+
+static int btti_sdio_if_probe_of(struct device *dev)
+{
+
+	if (!dev || !dev->of_node ||
+	    !of_match_node(btti_sdio_of_match_table, dev->of_node)) {
+			BT_ERR("[bt sdio] sdio device tree data "\
+					" not available\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static const struct btti_sdio_dev_reg_map btti_reg_map_cc33xx = {
+	/* fun0 ,ELP Wakeup Reg address, func0 not used*/
+	.sdio_wup_ble = 0x40,
+	/*fun1, set 0x1, to enable interrupts, 0 to disable */
+	.sdio_enable_int = 0x14,
+	/* fun1,read and write to the card are at 0x0 address */
+	.sdio_rt_data = 0x0,
+	/*  fun1,write 1 to clear rx interrupt after reception*/
+	.sdio_cl_int = 0x13,
+	 /*fun1, bt mode status, bit 1 means ack mode,
+	  *  currently the code just read it */
+	.bt_mode_status = 0x20,
+	/* fun1 read packet control, write 1 is set ack for RX,
+	 *  write 0 for nack */
+	.sdio_pc_rrt = 0x10,
+};
+
+static const struct btti_sdio_device btti_sdio_cc33xx_bt = {
+	.reg_map		= &btti_reg_map_cc33xx,
+};
+
+static const struct sdio_device_id btti_sdio_ids[] = {
+    { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_CC33XX) ,.driver_data\
+		    = (unsigned long)&btti_sdio_cc33xx_bt },
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btti_sdio_ids);
+
+static int btti_sdio_enable_func_interrupt(struct btti_sdio_dev *sdiodev,
+								u8 mask)
+{
+	int ret;
+
+	sdio_writeb(sdiodev->func, mask, sdiodev->reg_map->sdio_enable_int,\
+			&ret);
+	if (ret) {
+		BT_ERR("[bt sdio] Unable to enable the host interrupt!");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int btti_sdio_disable_func_interrupt(struct btti_sdio_dev *sdiodev,
+								u8 mask)
+{
+	u8 sdio_enable_int_mask;
+	int ret;
+
+	sdio_enable_int_mask = sdio_readb(sdiodev->func,\
+			sdiodev->reg_map->sdio_enable_int, &ret);
+	if (ret)
+		return -EIO;
+
+	sdio_enable_int_mask &= ~mask;
+
+	sdio_writeb(sdiodev->func, sdio_enable_int_mask,\
+			sdiodev->reg_map->sdio_enable_int, &ret);
+	if (ret < 0) {
+		BT_ERR("[bt sdio] Unable to disable the host interrupt!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+
+//Read Rx, called from btti_service_work_thread
+static int btti_sdio_process_rx(struct btti_private *private_data)
+{
+	ulong flags;
+	u8 intrpt_occur;
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+
+	spin_lock_irqsave(&private_data->irq_cnt_lock, flags);
+	intrpt_occur = sdiodev->Intrpt_triggered;
+	sdiodev->Intrpt_triggered = 0;
+	spin_unlock_irqrestore(&private_data->irq_cnt_lock, flags);
+
+	sdio_claim_host(sdiodev->func);
+	if (intrpt_occur)
+	{
+		btti_sdio_rx_packet(private_data);//read Rx,
+	}
+	else{
+		btti_acknldg_packet(sdiodev,1);//nack
+	}
+
+	sdio_release_host(sdiodev->func);
+
+	return 0;
+}
+
+static int btti_sdio_clr_irq(struct btti_sdio_dev *sdiodev)
+{
+	int ret;
+
+	BT_DBG("[bt sdio] clear int_status");
+
+	sdio_writeb(sdiodev->func, 1 ,sdiodev->reg_map->sdio_cl_int, &ret);
+	if (ret) {
+		BT_ERR("[bt sdio] clear int status failed: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+
+static int btti_sdio_register_dev(struct btti_sdio_dev *sdiodev)
+{
+	struct sdio_func *func;
+	u8 reg;
+	int ret;
+
+	if (!sdiodev || !sdiodev->func) {
+		BT_ERR("[bt sdio] Error: sdiodev or function is NULL!");
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	func = sdiodev->func;
+
+	sdio_claim_host(func);
+
+	func->enable_timeout = 0;
+
+	ret = sdio_enable_func(func);
+	if (ret) {
+		//this is ok, since the ble is not up yet so SDIO_CCCR_IOEx
+		//is not set on this sage
+		BT_DBG("[bt sdio] sdio_enable_func: %d failed: ret=%d",\
+				func->num, ret );
+	}
+	else{
+		BT_INFO("[bt sdio] sdio_enable_func: %d success", func->num );
+	}
+
+	//set block size
+	ret = sdio_set_block_size(sdiodev->func, SDIO_BLOCK_SIZE);
+	if (ret) {
+		BT_ERR("[bt sdio] cannot set SDIO block size");
+		ret = -EIO;
+		goto unreg_device;
+	}
+
+	//clear interrupt, write to clear
+	ret = btti_sdio_clr_irq(sdiodev);
+
+	//read status --not in use
+	reg = sdio_readb(func, sdiodev->reg_map->bt_mode_status, &ret);
+	if (ret < 0) {
+		BT_ERR("[bt sdio] Failed to read bt_mode_status ");
+	   ret = -EIO;
+	   goto unreg_device;
+	}
+	sdiodev->bt_mode_status = ret;
+
+	BT_DBG("[bt sdio] SDIO FUNC%d IO port: 0x%x BT MODE: 0x%x",\
+			func->num, sdiodev->reg_map->sdio_rt_data,\
+			sdiodev->bt_mode_status);
+
+	sdio_set_drvdata(func, sdiodev);
+
+	sdio_release_host(func);
+
+	return 0;
+
+unreg_device:
+		sdio_release_host(func);
+		btti_sdio_unregister_dev(func);
+		return ret;
+failed:
+		return ret;
+
+}
+
+static int btti_sdio_unregister_dev(struct sdio_func *func)
+{
+	BT_INFO("[bt sdio] btti_sdio_unregister_dev ");
+
+	if (func) {
+		sdio_claim_host(func);
+		BT_DBG("[bt sdio] sdio_release_irq");
+		sdio_release_irq(func);
+		sdio_disable_func(func);
+		sdio_set_drvdata(func, NULL);
+		sdio_release_host(func);
+	}
+
+	return 0;
+}
+
+static int btti_sdio_enable_int(struct btti_sdio_dev *sdiodev)
+{
+	int ret;
+
+	if (!sdiodev || !sdiodev->func)
+		return -EINVAL;
+
+	sdio_claim_host(sdiodev->func);
+
+	ret = btti_sdio_enable_func_interrupt(sdiodev, INTRPT_ENABLE_MASk);
+
+	sdio_release_host(sdiodev->func);
+
+	return ret;
+}
+
+static int btti_sdio_disable_int(struct btti_sdio_dev *sdiodev)
+{
+	int ret;
+
+	if (!sdiodev || !sdiodev->func)
+		return -EINVAL;
+
+	sdio_claim_host(sdiodev->func);
+
+	ret = btti_sdio_disable_func_interrupt(sdiodev, INTRPT_ENABLE_MASk);
+
+	sdio_release_host(sdiodev->func);
+
+	return ret;
+}
+
+
+
+static void btti_sdio_dump_regs(struct btti_private *private_data)
+{
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	int ret = 0;
+	unsigned int reg, reg_start, reg_end;
+	char buf[256], *ptr;
+	u8 loop, func, data;
+	int MAX_LOOP = 2;
+
+	btti_sdio_power_up_fw(private_data);
+	sdio_claim_host(sdiodev->func);
+
+	for (loop = 0; loop < MAX_LOOP; loop++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+
+		if (loop == 0) {
+			/* Read the registers of SDIO function0 */
+			func = loop;
+			reg_start = 0;
+			reg_end = 9;
+		} else {
+			func = 2;
+			reg_start = 0;
+			reg_end = 0x09;
+		}
+
+		ptr += sprintf(ptr, "[bt sdio] SDIO Func%d (%#x-%#x): ",
+			       func, reg_start, reg_end);
+		for (reg = reg_start; reg <= reg_end; reg++) {
+			if (func == 0)
+				data = sdio_f0_readb(sdiodev->func, reg, &ret);
+			else
+				data = sdio_readb(sdiodev->func, reg, &ret);
+
+			if (!ret) {
+				ptr += sprintf(ptr, "%02x ", data);
+			} else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+		}
+
+		BT_INFO("%s", buf);
+	}
+
+	sdio_release_host(sdiodev->func);
+}
+
+/* This function dump sdio register and memory data */
+static void btti_sdio_coredump(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct btti_sdio_dev *sdiodev;
+	struct btti_private *private_data;
+
+	sdiodev = sdio_get_drvdata(func);
+	if(sdiodev)
+	{
+		private_data = sdiodev->private_data;
+		if(!private_data){
+			BT_ERR("[bt sdio] private_data is not allocated");
+			return;
+		}
+	}else {
+		BT_ERR("[bt sdio] no sdiodev");
+		return;
+	}
+
+	/* dump sdio register first */
+	btti_sdio_dump_regs(private_data);
+}
+static int btti_sdio_power_up_fw(struct btti_private *private_data)
+{
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	int ret = 0;
+
+	BT_DBG("[bt sdio] power up FW");
+
+	if (!sdiodev || !sdiodev->func) {
+		BT_ERR("[bt sdio] sdiodev or function is NULL!");
+		return -EINVAL;
+	}
+
+	sdio_claim_host(sdiodev->func);
+
+	// BLE firmware remains awake , force Lx to remain awake;
+	sdio_f0_writeb(sdiodev->func, HOST_POWER_UP_MASK,\
+			sdiodev->reg_map->sdio_wup_ble, &ret);
+	if (ret) {
+		BT_ERR("[bt sdio] Unable to power up ble firmware!");
+		ret = -EIO;
+	}
+
+	sdio_release_host(sdiodev->func);
+
+	BT_INFO("[bt sdio] wake up firmware");
+
+	return ret;
+}
+
+static int btti_sdio_power_dn_fw(struct btti_private *private_data)
+{
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	int ret = 0;
+
+	BT_DBG("[bt sdio] power up FW");
+
+	if (!sdiodev || !sdiodev->func) {
+		BT_ERR("[bt sdio] sdiodev or function is NULL!");
+		return -EINVAL;
+	}
+
+	sdio_claim_host(sdiodev->func);
+
+	//  Lx Core can go to sleep (from host perspective)
+	sdio_f0_writeb(sdiodev->func, ~HOST_POWER_UP_MASK,\
+			sdiodev->reg_map->sdio_wup_ble, &ret);
+	if (ret) {
+		BT_ERR("[bt sdio] Unable to power down ble firmware!");
+		ret = -EIO;
+	}
+
+	sdio_release_host(sdiodev->func);
+
+	BT_INFO("[bt sdio] wake up firmware");
+
+	return ret;
+}
+static void btti_sdio_irq_handler(struct sdio_func *func)
+{
+	struct btti_private *private_data;
+	struct btti_sdio_dev *sdiodev;
+	ulong flags;
+	int ret;
+
+	BT_INFO("[bt sdio] RX btti_sdio_irq_handler received");
+
+	sdiodev = sdio_get_drvdata(func);
+	if (!sdiodev || !sdiodev->private_data) {
+		BT_ERR("[bt sdio] RX btti_hci_irq_handler(%d) "\
+				"%p sdiodev or private_data is NULL,"\
+				" sdiodev=%p",
+		       func->num, func, sdiodev);
+		return;
+	}
+
+	private_data = sdiodev->private_data;
+
+	if (private_data->sdio_dev_removed)	{
+		BT_ERR("[bt sdio] RX sdio_dev_removed");
+		return;
+	}
+
+	sdio_claim_host(func);
+
+	//clear interrupt
+	ret = btti_sdio_clr_irq(sdiodev);
+
+	sdio_release_host(func);
+
+	if (ret){
+		BT_ERR("[bt sdio] RX btti_sdio_irq_handler:failed to clear");
+		return;
+	}
+
+	spin_lock_irqsave(&private_data->irq_cnt_lock, flags);
+	sdiodev->Intrpt_triggered = 1;// signal that interrupt happened
+	spin_unlock_irqrestore(&private_data->irq_cnt_lock, flags);
+
+
+	btti_hci_irq_handler(private_data);
+}
+
+
+//rx
+static int btti_sdio_rx_packet(struct btti_private *private_data)
+{
+	u32 packet_len = 0;
+	int ret;
+	u32 data_read_size;
+	struct sk_buff *skb = NULL;
+	u32 packet_type;
+
+	struct hci_dev *hdev = private_data->btti_dev.hcidev;
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	u8 *sdio_header = sdiodev->sdio_header;
+
+	if (!sdiodev || !sdiodev->func) {
+		BT_ERR("[bt sdio] RX sdiodev or function is NULL!");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	//Read the length of data to be transferred
+	ret = sdio_readsb(sdiodev->func, sdio_header, \
+			sdiodev->reg_map->sdio_rt_data, SDIO_HEADER_LEN);
+	if (ret < 0) {
+		BT_ERR("[bt sdio] RX read rx_len failed");
+		ret = -EIO;
+		goto exit;
+	}
+
+	packet_len = \
+		sdio_header[0] | (sdio_header[1] << 8) | (sdio_header[2] << 16);
+	packet_type = sdio_header[3];
+
+	BT_INFO("[bt sdio] RX packet_len:%d packet_type:%d "\
+			"packet header hex: %*ph", packet_len,packet_type,\
+			SDIO_HEADER_LEN, sdio_header);
+
+	if ((packet_len <= SDIO_HEADER_LEN)\
+			|| (packet_len > HCI_MAX_FRAME_SIZE)) {
+		BT_ERR("[bt sdio] RX packet length: %d not HCI"\
+				" valid length", packet_len);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+
+	if (packet_len > (TIDRV_SIZE_OF_CMD_BUFFER+SDIO_HEADER_LEN)) {
+		BT_WARN("[bt sdio] RX packet length: %d not supported",\
+				packet_len);
+	}
+
+	data_read_size = roundup((packet_len - SDIO_HEADER_LEN),\
+			BTSDIO_RX_ALIGN);
+
+
+	skb = bt_skb_alloc(data_read_size, GFP_KERNEL);
+	if (!skb) {
+		/* Out of memory. Prepare a read retry and just
+		 * return with the expectation that the next time
+		 * we're called we'll have more memory.
+		 */
+		BT_ERR("[bt sdio] No free skb");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	skb_put(skb, packet_len-SDIO_HEADER_LEN);
+
+	ret = sdio_readsb(sdiodev->func, skb->data,\
+			sdiodev->reg_map->sdio_rt_data, data_read_size);
+	if (ret < 0) {
+		BT_ERR("[bt sdio] RX sdio_readsb failed: %d, size read:%d",\
+				ret, data_read_size);
+		ret = -EIO;
+		goto exit;
+	}
+	BT_INFO("[bt sdio] RX packet , packet data(without header) hex: %*ph",\
+			data_read_size, skb->data);
+
+	switch (packet_type) {
+	case HCI_ACLDATA_PKT:
+	case HCI_SCODATA_PKT:
+	case HCI_EVENT_PKT:
+		if(hdev != NULL){
+			btti_acknldg_packet(sdiodev, 0);//ack
+			hdev->stat.byte_rx += data_read_size;
+			hci_skb_pkt_type(skb) = packet_type;
+
+			ret= hci_recv_frame(hdev, skb);
+			if (ret < 0){
+				BT_ERR("[bt sdio] RX hci_recv_frame"
+						" failed :%d ", ret);
+			}
+			ret=0;
+		}else
+		{
+			ret = -EPERM;
+		}
+		//no need to free the skb in here, it is freed at hci_recv_frame
+		break;
+
+	case HCI_VENDOR_PKT:
+		BT_INFO("[bt sdio] vendor packet received");
+		if (!(ret=btti_handle_rx_vendor_event(private_data, skb)))
+		{
+			//if hdev was just created read it
+			hdev = private_data->btti_dev.hcidev;
+			if(hdev != NULL)
+			{
+				btti_acknldg_packet(sdiodev, 0);//ack
+				BT_INFO("[bt sdio] Hdev was created");
+				hdev->stat.byte_rx += data_read_size;
+				hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+				ret= hci_recv_frame(hdev, skb);
+				if (ret < 0){
+					BT_ERR("[bt sdio] vendor RX "
+							"hci_recv_frame "
+							"failed :%d ", ret);
+				}
+				//no need to free the skb in here,
+				//it is freed at hci_recv_frame
+				ret=0;
+			}else{
+				ret=-EPERM;
+			}
+		}
+		break;
+
+
+	default:
+		BT_ERR("[bt sdio] RX Unknown packet packet_type:%d",\
+				packet_type);
+		ret = -ENOENT;
+		break;
+	}
+
+exit:
+    //send nack
+	if (ret) {
+		if(hdev){
+			hdev->stat.err_rx++;
+		}
+		kfree_skb(skb);
+		skb = NULL;
+		btti_acknldg_packet(sdiodev, 1);//nack
+	}
+	return ret;
+}
+
+static void btti_acknldg_packet(struct btti_sdio_dev *sdiodev, u8 Ack)
+{
+	int retACK = 0;
+
+	//ack the packet, HCI will release the skb
+	BT_DBG("[bt sdio] RX sdio_writesb: send ACK ");
+	sdio_writeb(sdiodev->func, Ack,\
+			sdiodev->reg_map->sdio_pc_rrt, &retACK);
+	if (retACK && (Ack==0)) {
+		BT_ERR("[bt sdio] RX sdio_writesb:"\
+				" send ACK failed: %d", retACK);
+	}
+	if (retACK && (Ack==1)) {
+		BT_ERR("[bt sdio] RX sdio_writesb:"\
+				" send NACK failed: %d", retACK);
+	}
+}
+
+
+
+static int btti_handle_rx_vendor_event(struct btti_private *private_data,
+		struct sk_buff *skb )
+{
+	int ret = 0;
+	struct btti_vendor_event* vendor_event;
+	vendor_event = (struct btti_vendor_event *) skb->data;
+	switch (vendor_event->event_opcode) {
+		case BTTI_BLE_FIRMWARE_UP:
+			BT_INFO("[bt sdio] vendor packet- ble is up");
+			private_data->hci_adapter->ble_enable = 1;
+			if (btti_hci_register_hdev(private_data)) {
+				BT_ERR("[bt sdio] Register hdev failed!");
+				ret = -ENODEV;
+			}
+			BT_INFO("[bt sdio] registered to HCI");
+
+			break;
+		default:
+			BT_ERR("[bt sdio] unsupported rx vendor event code:"\
+					" %d", vendor_event->event_code);
+			ret = -EINVAL;
+			break;
+	}
+
+	return ret;
+
+}
+//tx
+//packet_len, the length includes the header.
+static int btti_sdio_tx_packet(struct btti_private *private_data,
+		struct sk_buff *skb)
+{
+	struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev;
+	int ret = 0;
+	int i = 0;
+	void *tmpbuf = NULL;
+	u32 data_send_size = 0;
+	u32 packet_len;
+	char* payload;
+	bool alignment_required;
+
+
+	BT_DBG("[bt sdio] TX btti_sdio_tx_packet");
+
+	if (!sdiodev || !sdiodev->func) {
+		BT_ERR("[bt sdio] sdiodev or function is NULL!");
+		return -EINVAL;
+	}
+
+	packet_len = skb->len;
+	payload = skb->data;
+	data_send_size = roundup(packet_len, BTSDIO_TX_ALIGN);
+	alignment_required = (data_send_size != packet_len);
+
+	if(alignment_required) 
+	{
+		tmpbuf = kzalloc(data_send_size, GFP_KERNEL);
+		if (!tmpbuf){
+			BT_ERR("[bt sdio] TX allocation failed");
+			return -ENOMEM;
+		}
+		memcpy(tmpbuf, payload, data_send_size);
+	}
+	else
+	{
+		tmpbuf = payload;
+	}
+
+	pm_runtime_get_sync(&sdiodev->func->dev);
+
+	sdio_claim_host(sdiodev->func);
+	do {
+		/* Transfer data to device */
+		ret = sdio_writesb(sdiodev->func,\
+				sdiodev->reg_map->sdio_rt_data, tmpbuf,\
+				data_send_size);
+		if (ret < 0) {
+			i++;
+			BT_ERR("[bt sdio] TX  i=%d writesb failed: %d", i, ret);
+			BT_ERR("[bt sdio] TX data_send_size: %d hex: %*ph",\
+					data_send_size, data_send_size,tmpbuf);
+			ret = -EIO;
+			if (i > MAX_SDIO_TX_RETRY)
+				goto exit;
+		}
+		BT_INFO("[bt sdio] TX to SDIO sdiodev done : %*ph ",\
+				data_send_size, tmpbuf);
+
+	} while (ret);
+
+exit:
+	sdio_release_host(sdiodev->func);
+
+	if(alignment_required){
+		kfree(tmpbuf);
+	}
+
+	pm_runtime_mark_last_busy(&sdiodev->func->dev);
+	pm_runtime_put_autosuspend(&sdiodev->func->dev);
+
+	if(ret){
+		private_data->btti_dev.hcidev->stat.err_tx++;
+	}
+	else{
+		private_data->btti_dev.hcidev->stat.byte_tx\
+			+= data_send_size;
+	}
+	kfree_skb(skb);
+	return ret;
+}
+
+
+static int btti_sdio_if_probe(struct sdio_func *func,
+					const struct sdio_device_id *id)
+{
+	int ret = 0;
+	struct btti_private *private_data = NULL;
+	struct btti_sdio_dev *sdiodev = NULL;
+
+	BT_INFO("[bt sdio] PROBE vendor=0x%x, device=0x%x,"\
+			" class=%d, fn=%d 0x%lx",
+			id->vendor, id->device, id->class, func->num,\
+			( unsigned long)func);
+
+	/* We are only able to handle the wlan function */
+	if (func->num != 0x01)
+	{
+		BT_DBG("[bt sdio] PROBE incorrect function number!");
+		ret = -ENODEV;
+		return ret;
+	}
+
+	/* Device tree node parsing */
+	if(btti_sdio_if_probe_of(&func->dev)){
+		ret = -ENODEV;
+		return ret;
+	}
+
+	sdiodev = devm_kzalloc(&func->dev, sizeof(*sdiodev), GFP_KERNEL);
+	if (!sdiodev){
+		ret = -ENODEV;
+		return ret;
+	}
+
+	sdiodev->func = func;
+	sdiodev->private_data = NULL;
+
+	if (id->driver_data) {
+		struct btti_sdio_device *data = (void *) id->driver_data;
+		sdiodev->reg_map = data->reg_map;
+	}
+
+	if (btti_sdio_register_dev(sdiodev) < 0) {
+		BT_ERR("[bt sdio] PROBE Failed to register BT device!");
+		ret = -ENODEV;
+		goto remove_device;
+	}
+	//enable interrupts
+	btti_sdio_enable_int(sdiodev);
+
+	private_data = btti_hci_add_sdio_dev(sdiodev);
+	if (!private_data) {
+		BT_ERR("[bt sdio] PROBE Initializing sdiodev failed!");
+		ret = -ENODEV;
+		goto remove_device;
+	}
+
+	sdiodev->private_data = private_data;
+
+	/* Initialize the interface specific function pointers */
+	private_data->card_tx_packet_funcp = btti_sdio_tx_packet;
+	private_data->card_power_up_firmware_funcp = btti_sdio_power_up_fw;
+	private_data->card_power_dn_firmware_funcp = btti_sdio_power_dn_fw;
+	private_data->card_process_rx_funcp = btti_sdio_process_rx;
+
+
+	/* pm_runtime_enable would be done after the firmware is being
+	 * downloaded because the core layer probably already enables
+	 * runtime PM for this func such as the case host->caps &
+	 * MMC_CAP_POWER_OFF_CARD.
+	 */
+	if (pm_runtime_enabled(&sdiodev->func->dev))
+		pm_runtime_disable(&sdiodev->func->dev);
+
+	/* As explaination in drivers/mmc/core/sdio_bus.c tells us:
+	 * Unbound SDIO functions are always suspended.
+	 * During probe, the function is set active and the usage count
+	 * is incremented.  If the driver supports runtime PM,
+	 * it should call pm_runtime_put_noidle() in its probe routine and
+	 * pm_runtime_get_noresume() in its remove routine.
+	 *
+	 * So, put a pm_runtime_put_noidle here !
+	 */
+	pm_runtime_put_noidle(&sdiodev->func->dev);
+
+	BT_DBG("[bt sdio] PROBE sdio_claim_irq");
+	sdio_claim_host(func);
+	ret = sdio_claim_irq(func, btti_sdio_irq_handler);
+	sdio_release_host(func);
+
+	if (ret) {
+		BT_ERR("[bt sdio] PROBE sdio_claim_irq failed: ret=%d", ret);
+		ret = -EIO;
+		goto remove_device;
+	}
+
+
+	BT_INFO("[bt sdio] TI cc33xx BLE-over-SDIO driver is up and running!");
+
+	return 0;
+
+/*disable_sdio_dev_int:
+	btti_sdio_disable_int(sdiodev);
+	btti_sdio_unregister_dev(func);*/
+remove_device:
+	btti_sdio_if_remove(func);
+	return ret;
+}
+
+static void btti_sdio_if_remove(struct sdio_func *func)
+{
+	struct btti_sdio_dev *sdiodev;
+	BT_INFO("[bt sdio if] sdio remove");
+
+	if (func != NULL) {
+		sdiodev = sdio_get_drvdata(func);
+		if (sdiodev != NULL) {
+			BT_INFO("[bt sdio] disable interrupt");
+			btti_sdio_disable_int(sdiodev);
+			if(sdiodev->private_data != NULL){
+				sdiodev->private_data->sdio_dev_removed = true;
+				btti_hci_remove_sdio_dev(sdiodev->private_data);
+			}
+			else {
+				BT_ERR("[bt sdio] private_data was "\
+						"not allocated");
+			}
+		}
+		btti_sdio_unregister_dev(func);
+	}
+	/* Be consistent the state in btti_sdio_if_probe */
+	pm_runtime_get_noresume(&func->dev);
+
+}
+
+static int btti_sdio_if_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct btti_sdio_dev *sdiodev;
+	struct btti_private *private_data;
+	mmc_pm_flag_t pm_flags;
+	struct hci_dev *hcidev;
+
+	BT_INFO("[bt sdio] suspend");
+	if (func) {
+		pm_flags = sdio_get_host_pm_caps(func);
+		BT_INFO("[bt sdio] %s: suspend: PM flags = 0x%x",\
+				sdio_func_id(func),
+		       pm_flags);
+		if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+			BT_ERR("[bt sdio] %s: cannot remain"\
+					" alive while suspended",
+			       sdio_func_id(func));
+			return -ENOSYS;
+		}
+		//sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+		sdiodev = sdio_get_drvdata(func);
+		if (!sdiodev || !sdiodev->private_data) {
+			BT_ERR("[bt sdio] sdiodev or private_data"\
+					" structure is not valid");
+			return 0;
+		}
+	} else {
+		BT_ERR("[bt sdio] sdio_func is not specified");
+		return 0;
+	}
+
+	private_data = sdiodev->private_data;
+	private_data->hci_adapter->is_suspending = true;
+	hcidev = private_data->btti_dev.hcidev;
+	BT_DBG("[bt sdio] %s: SDIO suspend", hcidev->name);
+	hci_suspend_dev(hcidev);
+	private_data->hci_adapter->is_suspending = false;
+	private_data->hci_adapter->is_suspended = true;
+
+	//if(private_data->card_power_dn_firmware_funcp){
+	//	private_data->card_power_dn_firmware_funcp(private_data);
+	//}
+
+	BT_DBG("[bt sdio] suspend end");
+	return 0;
+}
+
+static int btti_sdio_if_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct btti_sdio_dev *sdiodev;
+	struct btti_private *private_data;
+	mmc_pm_flag_t pm_flags;
+	struct hci_dev *hcidev;
+
+	BT_INFO("[bt sdio] resume");
+
+	if (func) {
+		pm_flags = sdio_get_host_pm_caps(func);
+		BT_INFO("[bt sdio] %s: resume: PM flags = 0x%x",\
+				sdio_func_id(func),
+		       pm_flags);
+		sdiodev = sdio_get_drvdata(func);
+		if (!sdiodev || !sdiodev->private_data) {
+			BT_ERR("[bt sdio] sdiodev or private_data"\
+					" structure is not valid");
+			return 0;
+		}
+	} else {
+		BT_ERR("[bt sdio] sdio_func is not specified");
+		return 0;
+	}
+	private_data = sdiodev->private_data;
+	if(!private_data)
+	{
+		BT_ERR("[bt sdio] private_data is not allocated");
+		return 0;
+	}
+
+	if (!private_data->hci_adapter->is_suspended) {
+		BT_WARN("[bt sdio] device already resumed");
+		return 0;
+	}
+
+	//if(private_data->card_power_up_firmware_funcp){
+	//	private_data->card_power_up_firmware_funcp(private_data);
+	//}
+	hcidev = private_data->btti_dev.hcidev;
+	private_data->hci_adapter->is_suspended = false;
+	BT_INFO("[bt sdio] %s: SDIO resume", hcidev->name);
+	hci_resume_dev(hcidev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops btti_sdio_pm_ops = {
+	.suspend	= btti_sdio_if_suspend,
+	.resume		= btti_sdio_if_resume,
+};
+
+static struct sdio_driver bt_ti_sdio = {
+	.name		= "btti_cc33xx_bt_sdio",
+	.id_table	= btti_sdio_ids,
+	.probe		= btti_sdio_if_probe,
+	.remove		= btti_sdio_if_remove,
+	.drv = {
+		.owner = THIS_MODULE,
+		.coredump = btti_sdio_coredump,
+		.pm = &btti_sdio_pm_ops,
+	}
+};
+
+static int __init btti_sdio_init_module(void)
+{
+	BT_INFO("[bt sdio] BLE SDIO init module");
+	if (sdio_register_driver(&bt_ti_sdio) != 0) {
+		BT_ERR("[bt sdio] SDIO Driver Registration Failed");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit btti_sdio_exit_module(void)
+{
+	BT_INFO("[bt sdio] BLE SDIO exit module");
+	sdio_unregister_driver(&bt_ti_sdio);
+}
+
+module_init(btti_sdio_init_module);
+module_exit(btti_sdio_exit_module);
+
+MODULE_AUTHOR("Texas Instruments.");
+MODULE_DESCRIPTION("Texas Instruments BT-over-SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git drivers/bluetooth/btti_sdio.h drivers/bluetooth/btti_sdio.h
new file mode 100644
index 000000000000..1fa7441d3573
--- /dev/null
+++ drivers/bluetooth/btti_sdio.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of TI BLE over SDIO
+ *
+ * Copyright (C) 2022 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+#define SDIO_HEADER_LEN			4
+
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/* define SD block size for data Tx/Rx */
+#define SDIO_BLOCK_SIZE			128
+
+#define TIDRV_BT_RX_PACKET_BUFFER_SIZE 	(HCI_MAX_FRAME_SIZE)
+#define TIDRV_SIZE_OF_CMD_BUFFER         (251)
+
+#define MAX_RX_PACKET_SIZE	(((min_t (int, TIDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			TIDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
+			* SDIO_BLOCK_SIZE)
+
+
+/* Max retry number of CMD53 write */
+#define MAX_SDIO_TX_RETRY		2
+
+/* register bitmasks */
+#define HOST_POWER_UP_MASK			1 // 1 to power up BLE SDIO firmware wake up bit */
+
+#define INTRPT_ENABLE_MASk		    1 //interrupt set
+
+struct btti_sdio_dev_reg_map {
+	u8 sdio_wup_ble;
+	u8 sdio_enable_int;
+	u8 sdio_rt_data;
+	u8 sdio_cl_int;
+	u8 bt_mode_status;
+	u8 sdio_pc_rrt;//ack interrupt
+};
+
+struct btti_sdio_dev {
+	struct sdio_func *func;
+	const struct btti_sdio_dev_reg_map *reg_map;
+	u8 bt_mode_status;/* the status read from 0x20 address */
+	struct btti_private *private_data;
+	u8 sdio_header[SDIO_HEADER_LEN];
+	bool Intrpt_triggered;
+};
+
+struct btti_sdio_device {
+	const struct btti_sdio_dev_reg_map *reg_map;
+};
+
+struct btti_vendor_event {
+	u8  event_code;//0xFF
+	u8  total_length;//0x2
+	u16 event_opcode;// EOGF  = 1  ; ESG = 0 ;  CMD
+	u8  param_00[2];//Data
+} __packed;
+
+//BTTI_BLE_FIRMWARE_UP: EOGF  = 1  ; ESG = 0 ;  CMD= 42 => opcode  = 0x042A
+#define BTTI_BLE_FIRMWARE_UP 0x2A04 //on the packet it is 0x042A
+
+// CC33xx HW FIFOs must be accessed with 32 bit alignment
+#define BTSDIO_RX_ALIGN		    4
+#define BTSDIO_TX_ALIGN		    4
+
+
+/* Macros for Data Alignment : size */
+#define ALIGN_SZ(p, a)	\
+	(((p) + ((a) - 1)) & ~((a) - 1))
diff --git drivers/bluetooth/btti_uart.c drivers/bluetooth/btti_uart.c
new file mode 100644
index 000000000000..62e52ac5fad9
--- /dev/null
+++ drivers/bluetooth/btti_uart.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  TI Bluetooth HCI UART driver
+ *  Copyright (C) 2023 Texas Instruments
+ * 
+ *  Acknowledgements:
+ *  This file is based on btuart.c, which was written by Marcel Holtmann.
+ */
+
+#define DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/serdev.h>
+#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/firmware.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "h4_recv.h"
+
+#define VERSION "0.84"
+
+struct btti_uart_vnd {
+	const struct h4_recv_pkt *recv_pkts;
+	int recv_pkts_cnt;
+	unsigned int manufacturer;
+};
+
+enum state{
+	STATE_PROBING = 0,
+	STATE_HW_OFF,
+	STATE_HW_ON,
+	STATE_HW_READY,
+	STATE_REMOVED
+};
+
+static const char *sm_state_to_string(enum state state)
+{
+	switch (state){
+	case STATE_PROBING:
+		return "STATE_PROBING";
+	case STATE_HW_OFF: 
+		return "STATE_HW_OFF";
+	case STATE_HW_ON: 
+		return "STATE_HW_ON";
+	case STATE_HW_READY:
+		return "STATE_HW_READY";
+	case STATE_REMOVED:
+		return "STATE_REMOVED";
+	}
+
+	return "Illegal state value";
+};
+
+enum sm_event{
+	EVENT_PROBE_DONE,
+	EVENT_REMOVE,
+	EVENT_REGULATOR_ENABLE,
+	EVENT_REGULATOR_DISABLE,
+	EVENT_HCI_WAKEUP_FRAME_RECEIVED,
+};
+
+static const char *event_to_string(enum sm_event event)
+{
+	switch (event){
+	case EVENT_PROBE_DONE:
+		return "EVENT_PROBE_DONE";
+	case EVENT_REMOVE: 
+		return "EVENT_REMOVE";
+	case EVENT_REGULATOR_ENABLE: 
+		return "EVENT_REGULATOR_ENABLE";
+	case EVENT_REGULATOR_DISABLE:
+		return "EVENT_REGULATOR_DISABLE";
+	case EVENT_HCI_WAKEUP_FRAME_RECEIVED: 
+		return "EVENT_HCI_WAKEUP_FRAME_RECEIVED";
+	}
+
+	return "Illegal event value";
+};
+
+struct btti_uart_dev {
+	struct hci_dev *hdev;
+	struct serdev_device *serdev;
+	struct regulator *reg;
+	struct notifier_block nb;
+
+	struct work_struct tx_work;
+	struct sk_buff_head txq;
+
+	struct work_struct btti_uart_sm_work;
+	struct llist_head sm_event_list;
+	enum state sm_state;
+
+	struct sk_buff *rx_skb;
+
+	const struct btti_uart_vnd *vnd;
+
+	struct gpio_desc	*host_wakeup;
+
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pins_runtime;
+	struct pinctrl_state *pins_sleep;
+};
+
+struct event_node{
+	struct llist_node node;
+	enum sm_event event;
+};
+
+static int serial_open(struct btti_uart_dev *bdev)
+{
+	struct serdev_device *serdev = bdev->serdev;
+	bool disable_flow_control = false;
+	u32 max_speed = 3000000;
+	int ret=0;
+
+	ret = serdev_device_open(serdev);
+	if (ret){
+		dev_err(&serdev->dev, "Cannot open serial port (%d)", ret);
+		return ret;
+	}
+
+	of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
+	disable_flow_control = of_property_read_bool(serdev->dev.of_node, "disable-flow-control");
+
+	serdev_device_set_baudrate(serdev, max_speed);
+	if (disable_flow_control)
+		serdev_device_set_flow_control(serdev, false);
+
+	if (bdev->host_wakeup)
+		pm_runtime_enable(&serdev->dev);
+
+	return ret;
+}
+
+static void serial_close(struct btti_uart_dev *bdev)
+{
+	struct serdev_device *serdev = bdev->serdev;
+
+	if (bdev->host_wakeup)
+		pm_runtime_disable(&serdev->dev);
+
+	serdev_device_close(serdev);
+}
+
+static int btti_uart_open(struct hci_dev *hdev)
+{
+	return 0;
+}
+
+static int btti_uart_close(struct hci_dev *hdev)
+{
+	return 0;
+}
+
+static int btti_uart_flush(struct hci_dev *hdev)
+{
+	struct btti_uart_dev *bdev = hci_get_drvdata(hdev);
+
+	/* Flush any pending characters */
+	serdev_device_write_flush(bdev->serdev);
+	skb_queue_purge(&bdev->txq);
+
+	cancel_work_sync(&bdev->tx_work);
+
+	kfree_skb(bdev->rx_skb);
+	bdev->rx_skb = NULL;
+
+	return 0;
+}
+
+static int btti_uart_setup(struct hci_dev *hdev)
+{
+	return 0;
+}
+
+static int btti_uart_tx_wakeup(struct btti_uart_dev *bdev)
+{
+	schedule_work(&bdev->tx_work);
+	return 0;
+}
+
+static int btti_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct btti_uart_dev *bdev = hci_get_drvdata(hdev);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+	skb_queue_tail(&bdev->txq, skb);
+
+	btti_uart_tx_wakeup(bdev);
+	return 0;
+}
+
+static int btti_uart_register_hci_device(struct btti_uart_dev *bdev)
+{
+	struct serdev_device *serdev = bdev->serdev;
+	struct hci_dev *hdev;
+	int ret;
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		dev_err(&serdev->dev, "Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	hci_set_drvdata(hdev, bdev);
+	hdev->bus = HCI_UART;
+	hdev->open  = btti_uart_open;
+	hdev->close = btti_uart_close;
+	hdev->flush = btti_uart_flush;
+	hdev->setup = btti_uart_setup;
+	hdev->send  = btti_uart_send_frame;
+	SET_HCIDEV_DEV(hdev, &serdev->dev);
+
+	ret = hci_register_dev(hdev);
+	if (ret){
+		dev_err(&serdev->dev, "Can't register HCI device (%d)", ret);
+		hci_free_dev(hdev);
+		hdev = NULL;
+	}
+
+	bdev->hdev = hdev;
+	return ret;
+}
+
+static void btti_uart_unregister_device(struct btti_uart_dev *bdev)
+{
+	struct hci_dev *hdev = bdev->hdev;
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+	
+	bdev->hdev = NULL;
+}
+
+static void btti_uart_tx_work(struct work_struct *work)
+{
+	struct btti_uart_dev *bdev = container_of(work, struct btti_uart_dev,
+					          tx_work);
+	struct serdev_device *serdev = bdev->serdev;
+	struct hci_dev *hdev = bdev->hdev;
+
+	while (1) {
+		struct sk_buff *skb = skb_dequeue(&bdev->txq);
+		int len;
+
+		if (!skb)
+			break;
+
+		len = serdev_device_write_buf(serdev, skb->data,
+					      skb->len);
+		hdev->stat.byte_tx += len;
+
+		skb_pull(skb, len);
+		if (skb->len > 0) {
+			skb_queue_head(&bdev->txq, skb);
+			break;
+		}
+
+		switch (hci_skb_pkt_type(skb)) {
+		case HCI_COMMAND_PKT:
+			hdev->stat.cmd_tx++;
+			break;
+		case HCI_ACLDATA_PKT:
+			hdev->stat.acl_tx++;
+			break;
+		case HCI_SCODATA_PKT:
+			hdev->stat.sco_tx++;
+			break;
+		}
+
+		kfree_skb(skb);
+	}
+}
+
+static void unexpected_event(struct serdev_device *serdev, 
+			     enum state state, enum sm_event event)
+{
+	dev_err(&serdev->dev, "Unexpected event %s at state %s",
+		event_to_string(event), sm_state_to_string(state));
+	WARN_ON(1);
+}
+
+static void btti_uart_sm_post_event(struct btti_uart_dev *bdev, enum sm_event event)
+{
+	struct event_node *event_node;
+
+	event_node = kzalloc(sizeof(*event_node), GFP_KERNEL);
+	if (unlikely(!event_node)){
+		dev_err(&bdev->serdev->dev, "Event allocation failure");
+		return;
+	}
+
+	event_node->event = event;
+
+	llist_add(&event_node->node, &bdev->sm_event_list);
+	schedule_work(&bdev->btti_uart_sm_work);
+}
+
+static void sm_process(struct btti_uart_dev *bdev, enum sm_event event)
+{
+	struct serdev_device *serdev = bdev->serdev;
+	enum state next_state = bdev->sm_state;
+
+	switch (bdev->sm_state){
+	case STATE_PROBING:
+		switch (event){
+		case EVENT_PROBE_DONE:
+			if (regulator_is_enabled(bdev->reg))
+				btti_uart_sm_post_event(bdev, EVENT_REGULATOR_ENABLE);
+
+			next_state = STATE_HW_OFF;
+			break;
+
+		default:
+			unexpected_event(serdev, bdev->sm_state, event);
+		}
+		break;
+
+	case STATE_HW_OFF:
+		switch(event){
+		case EVENT_REGULATOR_ENABLE:
+			if (0 == serial_open(bdev))
+				next_state = STATE_HW_ON;
+			else
+				dev_err(&serdev->dev, "Could not open serial port");
+			break;
+
+		case EVENT_REMOVE:
+			next_state = STATE_REMOVED;
+			break;
+
+		case EVENT_REGULATOR_DISABLE:
+			break;
+
+		default:
+			unexpected_event(serdev, bdev->sm_state, event);
+		}
+		break;
+
+	case STATE_HW_ON:
+		switch(event){
+		case EVENT_REMOVE:
+		case EVENT_REGULATOR_DISABLE:
+			serial_close(bdev);
+			next_state = (event == EVENT_REMOVE) ? 
+						STATE_REMOVED: STATE_HW_OFF;
+			break;
+
+		case EVENT_HCI_WAKEUP_FRAME_RECEIVED:
+			if (0 == btti_uart_register_hci_device(bdev))
+				next_state = STATE_HW_READY;
+			break;
+
+		case EVENT_REGULATOR_ENABLE:
+			break;
+
+		default:
+			unexpected_event(serdev, bdev->sm_state, event);
+		}
+		break;
+
+	case STATE_HW_READY:
+		switch(event){
+		case EVENT_REMOVE:
+		case EVENT_REGULATOR_DISABLE:
+			btti_uart_unregister_device(bdev);
+			serial_close(bdev);
+			next_state = (event == EVENT_REMOVE) ? 
+						STATE_REMOVED: STATE_HW_OFF;
+			break;
+
+		case EVENT_REGULATOR_ENABLE:
+			break;
+
+		default:
+			unexpected_event(serdev, bdev->sm_state, event);
+		}
+		break;
+
+	case STATE_REMOVED:
+		switch(event){
+		case EVENT_REGULATOR_DISABLE:
+		case EVENT_REGULATOR_ENABLE:
+		case EVENT_HCI_WAKEUP_FRAME_RECEIVED:
+			break;
+
+		default:
+			unexpected_event(serdev, bdev->sm_state, event);
+		}
+		break;
+	}
+
+	dev_dbg(&serdev->dev, 
+		"SM: Got %s, moving from %s to %s",
+		event_to_string(event), sm_state_to_string(bdev->sm_state), 
+		sm_state_to_string(next_state));
+
+	bdev->sm_state = next_state;
+}
+
+inline static struct llist_node* get_event_list(struct btti_uart_dev *bdev)
+{
+	struct llist_node* node;
+
+	node = llist_del_all(&bdev->sm_event_list);
+	if (!node)
+		return NULL;
+	
+	return llist_reverse_order(node);
+}
+
+static void btti_uart_sm_work(struct work_struct *work)
+{
+	struct btti_uart_dev *bdev;
+	struct event_node *event_node, *tmp;
+	struct llist_node *event_list;
+
+	bdev = container_of(work, struct btti_uart_dev, btti_uart_sm_work);
+	event_list = get_event_list(bdev);
+
+	llist_for_each_entry_safe(event_node, tmp, event_list, node){
+		sm_process(bdev, event_node->event);
+		kfree(event_node);
+	}
+}
+
+static int btti_uart_wakeup_event_match(struct serdev_device *serdev, 
+					const u8 *data, size_t count)
+{
+	struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev);
+	const u8 hw_wakeup_evt[] = {HCI_VENDOR_PKT, 0xff, 0x02, 0x04, 0x2a, 0x00, 0x00};
+
+	if (count < sizeof hw_wakeup_evt)
+		return 0; // Reject data until all bytes have been received
+
+	if (count > sizeof hw_wakeup_evt)
+		goto error;
+
+	if (0 != memcmp(&hw_wakeup_evt, data, sizeof hw_wakeup_evt))
+		goto error;
+
+	btti_uart_sm_post_event(bdev, EVENT_HCI_WAKEUP_FRAME_RECEIVED);
+	return count;
+
+error:
+	dev_err(&serdev->dev, "Unexpected wakeup pattern");
+	print_hex_dump(KERN_DEBUG, "Pattern:", 
+		       DUMP_PREFIX_NONE, 16, 1, data, count, true);
+	return count;
+}
+
+static int btti_uart_receive_buf(struct serdev_device *serdev, const u8 *data,
+			      size_t count)
+{
+	struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev);
+	const struct btti_uart_vnd *vnd = bdev->vnd;
+
+	if (unlikely(!bdev->hdev)){
+		// Accept only wakeup event until driver is registered with HCI 
+		return btti_uart_wakeup_event_match(serdev, data, count);
+	}
+
+	bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, data, count,
+				   vnd->recv_pkts, vnd->recv_pkts_cnt);
+	if (IS_ERR(bdev->rx_skb)) {
+		int err = PTR_ERR(bdev->rx_skb);
+		dev_err(&serdev->dev, "Frame reassembly failed (%d)", err);
+		bdev->rx_skb = NULL;
+		print_hex_dump( KERN_DEBUG, "Frame:", DUMP_PREFIX_NONE,
+				16, 1, data, count, true);
+		return 0;
+	}
+
+	bdev->hdev->stat.byte_rx += count;
+
+	return count;
+}
+
+static void btti_uart_write_wakeup(struct serdev_device *serdev)
+{
+	struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev);
+
+	btti_uart_tx_wakeup(bdev);
+}
+
+static const struct serdev_device_ops btti_uart_client_ops = {
+	.receive_buf = btti_uart_receive_buf,
+	.write_wakeup = btti_uart_write_wakeup,
+};
+
+static const struct h4_recv_pkt default_recv_pkts[] = {
+	{ H4_RECV_ACL,      .recv = hci_recv_frame },
+	{ H4_RECV_SCO,      .recv = hci_recv_frame },
+	{ H4_RECV_EVENT,    .recv = hci_recv_frame },
+};
+
+static const struct btti_uart_vnd default_vnd = {
+	.recv_pkts	= default_recv_pkts,
+	.recv_pkts_cnt	= ARRAY_SIZE(default_recv_pkts),
+};
+
+static const struct btti_uart_vnd ti_vnd = {
+	.recv_pkts	= default_recv_pkts,
+	.recv_pkts_cnt	= ARRAY_SIZE(default_recv_pkts),
+	.manufacturer	= 13,
+};
+
+static int btti_uart_regulator_event(struct notifier_block *nb,
+				     unsigned long event, void *data)
+{
+	struct btti_uart_dev *bdev = container_of(nb, struct btti_uart_dev, nb);
+
+	if (event & REGULATOR_EVENT_DISABLE) {
+		btti_uart_sm_post_event(bdev, EVENT_REGULATOR_DISABLE);
+	}
+
+	if (event & REGULATOR_EVENT_ENABLE) {
+		btti_uart_sm_post_event(bdev, EVENT_REGULATOR_ENABLE);
+	}
+
+	return NOTIFY_OK;
+}
+
+static irqreturn_t host_wake_irq(int irq, void *data)
+{
+	struct btti_uart_dev *bdev = data;
+	struct serdev_device *serdev = bdev->serdev;
+
+	dev_info(&serdev->dev, "CC33xx wake IRQ");
+
+	pm_wakeup_event(&serdev->dev, 0);
+	pm_system_wakeup();
+
+	return IRQ_HANDLED;
+}
+
+static void btti_uart_host_wake_init(struct serdev_device *serdev)
+{
+	struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev);
+	int ret;
+
+	if (!bdev->pinctrl)
+		goto out_err;
+
+	bdev->pins_sleep = pinctrl_lookup_state(bdev->pinctrl, "sleep");
+	if (IS_ERR(bdev->pins_sleep))
+		bdev->pins_sleep = NULL;
+
+	bdev->host_wakeup = devm_gpiod_get_optional(&serdev->dev, "host-wakeup", GPIOD_IN);
+	if (IS_ERR(bdev->host_wakeup))
+		goto out_err;
+
+	if (device_init_wakeup(&serdev->dev, true) != 0)
+		goto out_err;
+
+	ret = devm_request_irq(&serdev->dev, gpiod_to_irq(bdev->host_wakeup), 
+		host_wake_irq, IRQF_TRIGGER_RISING, "btti_host_wake", bdev);
+	if (ret)
+		goto out_err;
+
+	ret = enable_irq_wake(gpiod_to_irq(bdev->host_wakeup));
+	if (ret < 0)
+		goto out_err_disable_wake; 
+	
+	disable_irq(gpiod_to_irq(bdev->host_wakeup));
+	
+	dev_info(&serdev->dev, "Host wakeup enabled");
+	return;
+
+
+out_err_disable_wake:
+	device_init_wakeup(&serdev->dev, false);
+out_err:
+	bdev->host_wakeup = NULL;
+	bdev->pins_sleep = NULL;
+	dev_info(&serdev->dev, "Host wakeup NOT enabled");
+	return;
+}
+
+static int btti_uart_probe(struct serdev_device *serdev)
+{
+	struct btti_uart_dev *bdev;
+	int ret;
+
+	bdev = devm_kzalloc(&serdev->dev, sizeof(*bdev), GFP_KERNEL);
+	if (!bdev)
+		return -ENOMEM;
+
+	/* Request the vendor specific data and callbacks */
+	bdev->vnd = device_get_match_data(&serdev->dev);
+	if (!bdev->vnd)
+		bdev->vnd = &default_vnd;
+
+	bdev->serdev = serdev;
+	serdev_device_set_drvdata(serdev, bdev);
+
+	/* Using the optional get regulator API as normal get returns a dummy 
+	if the regulator is not found. */
+	bdev->reg = devm_regulator_get_optional(&serdev->dev, "cc33xx");
+	if (PTR_ERR(bdev->reg) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (IS_ERR(bdev->reg)) {
+		dev_err(&serdev->dev, "can't get regulator");
+		return PTR_ERR(bdev->reg);
+	}
+
+	bdev->pinctrl = devm_pinctrl_get(&serdev->dev);
+	if (!IS_ERR(bdev->pinctrl)){
+		bdev->pins_runtime = pinctrl_lookup_state(bdev->pinctrl, "default");
+		if (IS_ERR(bdev->pins_runtime)){
+			dev_err(&serdev->dev, "can't lookup default pin state");
+			return PTR_ERR(bdev->pins_runtime);
+		}
+	} else {
+		bdev->pinctrl = NULL;
+		bdev->pins_runtime = NULL;
+	}
+
+	btti_uart_host_wake_init(serdev);
+
+	if (bdev->pins_runtime)
+		pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime);
+	
+	bdev->nb.notifier_call = btti_uart_regulator_event;
+
+	ret = regulator_register_notifier(bdev->reg, &bdev->nb);
+	if (ret != 0) {
+		dev_err(&serdev->dev, 
+			"Failed to register regulator notifier (%d)", ret);
+		return ret;
+	}
+
+	serdev_device_set_client_ops(serdev, &btti_uart_client_ops);
+
+	INIT_WORK(&bdev->tx_work, btti_uart_tx_work);
+	skb_queue_head_init(&bdev->txq);
+
+	INIT_WORK(&bdev->btti_uart_sm_work, btti_uart_sm_work);
+	init_llist_head(&bdev->sm_event_list);
+
+	btti_uart_sm_post_event(bdev, EVENT_PROBE_DONE);
+
+	return 0;
+}
+
+static void btti_uart_remove(struct serdev_device *serdev)
+{
+	struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev);
+
+	if (bdev->host_wakeup){
+		device_init_wakeup(&serdev->dev, false);
+		disable_irq_wake(gpiod_to_irq(bdev->host_wakeup));
+	}
+
+	regulator_unregister_notifier(bdev->reg, &bdev->nb);
+	btti_uart_sm_post_event(bdev, EVENT_REMOVE);
+	flush_work(&bdev->btti_uart_sm_work);
+}
+
+static int btti_suspend_device(struct device *dev)
+{
+	struct btti_uart_dev *bdev = dev_get_drvdata(dev);
+	struct serdev_device *serdev = bdev->serdev;
+	int ret;
+
+	if (bdev->pins_sleep){
+		ret = pinctrl_select_state(bdev->pinctrl, bdev->pins_sleep);
+		if (ret < 0)
+			goto out_err;
+	}
+
+	enable_irq(gpiod_to_irq(bdev->host_wakeup));
+
+	ret = serdev_device_set_rts(serdev, false);
+		if (ret < 0)
+			goto out_err;
+
+	return 0;
+
+out_err:
+	if (bdev->pins_runtime)
+		pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime);
+
+	serdev_device_set_rts(serdev, true);
+
+	return ret; 
+}
+
+static int btti_resume_device(struct device *dev)
+{
+	struct btti_uart_dev *bdev = dev_get_drvdata(dev);
+	struct serdev_device *serdev = bdev->serdev;
+	int ret;
+
+	disable_irq_nosync(gpiod_to_irq(bdev->host_wakeup));
+
+	if (bdev->pins_runtime){
+		ret = pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime);
+		if (ret < 0)
+			return ret;
+	}
+
+	return serdev_device_set_rts(serdev, true);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id btti_uart_of_match_table[] = {
+	{ .compatible = "ti,cc33xx-bt", .data = &ti_vnd },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, btti_uart_of_match_table);
+#endif
+
+static const struct dev_pm_ops btti_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(btti_suspend_device, btti_resume_device)
+};
+
+static struct serdev_device_driver btti_uart_driver = {
+	.probe = btti_uart_probe,
+	.remove = btti_uart_remove,
+	.driver = {
+		.name = "btti",
+		.of_match_table = of_match_ptr(btti_uart_of_match_table),
+		.pm = &btti_pm_ops
+	},
+};
+
+module_serdev_device_driver(btti_uart_driver);
+
+MODULE_DESCRIPTION("TI Bluetooth UART driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git drivers/net/wireless/ti/Kconfig drivers/net/wireless/ti/Kconfig
index 7c0b17a76fe2..3765e38401bd 100644
--- drivers/net/wireless/ti/Kconfig
+++ drivers/net/wireless/ti/Kconfig
@@ -14,6 +14,7 @@ if WLAN_VENDOR_TI
 source "drivers/net/wireless/ti/wl1251/Kconfig"
 source "drivers/net/wireless/ti/wl12xx/Kconfig"
 source "drivers/net/wireless/ti/wl18xx/Kconfig"
+source "drivers/net/wireless/ti/cc33xx/Kconfig"
 
 # keep last for automatic dependencies
 source "drivers/net/wireless/ti/wlcore/Kconfig"
diff --git drivers/net/wireless/ti/Makefile drivers/net/wireless/ti/Makefile
index 0530dd744275..e42b05e9b2b0 100644
--- drivers/net/wireless/ti/Makefile
+++ drivers/net/wireless/ti/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_WLCORE)			+= wlcore/
 obj-$(CONFIG_WL12XX)			+= wl12xx/
 obj-$(CONFIG_WL1251)			+= wl1251/
 obj-$(CONFIG_WL18XX)			+= wl18xx/
+obj-$(CONFIG_CC33XX)			+= cc33xx/
 
 # small builtin driver bit
 obj-$(CONFIG_WILINK_PLATFORM_DATA)	+= wilink_platform_data.o
diff --git drivers/net/wireless/ti/cc33xx/Kconfig drivers/net/wireless/ti/cc33xx/Kconfig
new file mode 100644
index 000000000000..2cca2fd32562
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config CC33XX
+	tristate "TI CC33XX support"
+	depends on MAC80211
+	select FW_LOADER
+	help
+	  This module contains the main code for TI CC33XX WLAN chips. It abstracts
+	  hardware-specific differences among different chipset families.
+	  Each chipset family needs to implement its own lower-level module
+	  that will depend on this module for the common code.
+
+	  If you choose to build a module, it will be called cc33xx. Say N if
+	  unsure.
+
+config CC33XX_SPI
+	tristate "TI CC33XX SPI support"
+	depends on CC33XX && SPI_MASTER && OF
+	select CRC7
+	help
+	  This module adds support for the SPI interface of adapters using
+	  TI CC33XX chipsets.  Select this if your platform is using
+	  the SPI bus.
+
+	  If you choose to build a module, it'll be called cc33xx_spi.
+	  Say N if unsure.
+
+config CC33XX_SDIO
+	tristate "TI CC33XX SDIO support"
+	depends on CC33XX && MMC
+	help
+	  This module adds support for the SDIO interface of adapters using
+	  TI CC33XX WLAN chipsets.  Select this if your platform is using
+	  the SDIO bus.
+
+	  If you choose to build a module, it'll be called cc33xx_sdio.
+	  Say N if unsure.
diff --git drivers/net/wireless/ti/cc33xx/Makefile drivers/net/wireless/ti/cc33xx/Makefile
new file mode 100644
index 000000000000..56015821b799
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cc33xx-objs		= main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
+			  boot.o init.o debugfs.o scan.o sysfs.o
+
+cc33xx_spi-objs 	= spi.o
+cc33xx_sdio-objs	= sdio.o
+
+cc33xx-$(CONFIG_NL80211_TESTMODE)	+= testmode.o
+obj-$(CONFIG_CC33XX)				+= cc33xx.o
+obj-$(CONFIG_CC33XX_SPI)			+= cc33xx_spi.o
+obj-$(CONFIG_CC33XX_SDIO)			+= cc33xx_sdio.o
diff --git drivers/net/wireless/ti/cc33xx/acx.c drivers/net/wireless/ti/cc33xx/acx.c
new file mode 100644
index 000000000000..3faac5dfb909
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/acx.c
@@ -0,0 +1,2226 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include "acx.h"
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+
+#include "wlcore.h"
+#include "tx.h"
+#include "debug.h"
+#include "cc33xx_80211.h"
+
+
+int cc33xx_acx_dynamic_fw_traces(struct cc33xx *wl)
+{
+	struct acx_dynamic_fw_traces_cfg *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx dynamic fw traces config %d",
+		     wl->dynamic_fw_traces);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->dynamic_fw_traces = cpu_to_le32(wl->dynamic_fw_traces);
+
+	ret = cc33xx_cmd_configure(wl, ACX_DYNAMIC_TRACES_CFG,
+				   acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx config dynamic fw traces failed: %d", ret);
+		goto out;
+	}
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_clear_statistics(struct cc33xx *wl)
+{
+	struct cc33xx_acx_clear_statistics *acx;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx clear statistics");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("failed to clear firmware statistics: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+
+int cc33xx_acx_wake_up_conditions(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				  u8 wake_up_event, u8 listen_interval)
+{
+	struct acx_wake_up_condition *wake_up;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx wake up conditions (wake_up_event %d listen_interval %d)",
+		     wake_up_event, listen_interval);
+
+	wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL);
+	if (!wake_up) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	wake_up->wake_up_event = wake_up_event;
+	wake_up->listen_interval = listen_interval;
+
+	ret = cc33xx_cmd_configure(wl, WAKE_UP_CONDITIONS_CFG,
+				   wake_up, sizeof(*wake_up));
+	if (ret < 0) {
+		cc33xx_warning("could not set wake up conditions: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(wake_up);
+	return ret;
+}
+
+int cc33xx_acx_sleep_auth(struct cc33xx *wl, u8 sleep_auth)
+{
+	struct acx_sleep_auth *auth;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx sleep auth %d", sleep_auth);
+
+	auth = kzalloc(sizeof(*auth), GFP_KERNEL);
+	if (!auth) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	auth->sleep_auth = sleep_auth;
+
+	ret = cc33xx_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth));
+	if (ret < 0) {
+		cc33xx_error("could not configure sleep_auth to %d: %d",
+			     sleep_auth, ret);
+		goto out;
+	}
+
+	wl->sleep_auth = sleep_auth;
+out:
+	kfree(auth);
+	return ret;
+}
+
+int cc33xx_ble_enable(struct cc33xx *wl, u8 ble_enable)
+{
+	struct debug_header *buf;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "ble enable");
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	//cc33xx_cmd_debug
+
+	ret = cc33xx_cmd_debug(wl, BLE_ENABLE ,buf ,sizeof(*buf));
+	if (ret < 0) {
+		cc33xx_error("could not enable ble");
+		goto out;
+	}
+
+	wl->ble_enable = 1;
+out:
+	kfree(buf);
+	return ret;
+}
+
+int cc33xx_acx_tx_power(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			int power)
+{
+	struct acx_tx_power_cfg *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx TX_POWER_CFG %d", power);
+
+	if (power < CC33XX_MIN_TXPWR) {
+		cc33xx_warning("Configured Tx power %d dBm."
+			       " Increasing to minimum %d dBm",
+			       power, CC33XX_MIN_TXPWR);
+		power = CC33XX_MIN_TXPWR;
+	} else if (power > CC33XX_MAX_TXPWR) {
+		cc33xx_warning("Configured Tx power %d dBm is bigger than upper"
+			       " limit: %d dBm. Attenuating to max limit",
+			       power, CC33XX_MAX_TXPWR);
+		power = CC33XX_MAX_TXPWR;
+	}
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->tx_power = power;
+
+	ret = cc33xx_cmd_configure(wl, TX_POWER_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("Configure of tx power failed: %d", ret);
+		goto out;
+	}
+
+	wlvif->power_level = power;
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_feature_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct acx_feature_config *feature;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx feature cfg");
+
+	feature = kzalloc(sizeof(*feature), GFP_KERNEL);
+	if (!feature) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */
+	feature->role_id = wlvif->role_id;
+	feature->data_flow_options = 0;
+	feature->options = 0;
+
+	ret = cc33xx_cmd_configure(wl, ACX_FEATURE_CFG,
+				   feature, sizeof(*feature));
+	if (ret < 0) {
+		cc33xx_error("Couldn't set HW encryption");
+		goto out;
+	}
+
+out:
+	kfree(feature);
+	return ret;
+}
+
+int cc33xx_acx_mem_map(struct cc33xx *wl, struct acx_header *mem_map,
+		       size_t len)
+{
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx mem map");
+
+	ret = cc33xx_cmd_interrogate(wl, MEM_MAP_INTR, mem_map,
+				     sizeof(struct acx_header), len);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int cc33xx_acx_get_fw_versions(struct cc33xx *wl, struct cc33xx_acx_fw_versions *get_fw_versions, size_t len)
+{
+	int ret;
+	cc33xx_debug(DEBUG_ACX, "acx get FW versions");
+
+	ret = cc33xx_cmd_interrogate(wl, GET_FW_VERSIONS_INTR, get_fw_versions,
+				     sizeof(struct cc33xx_acx_fw_versions), len);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+int cc33xx_acx_rx_msdu_life_time(struct cc33xx *wl)
+{
+	struct acx_rx_msdu_lifetime *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx rx msdu life time");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->lifetime = cpu_to_le32(wl->conf.host_conf.rx.rx_msdu_life_time);
+	ret = cc33xx_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME,
+				   acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("failed to set rx msdu life time: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_slot(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		    enum acx_slot_type slot_time)
+{
+	struct acx_slot *slot;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx slot");
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (!slot) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	slot->role_id = wlvif->role_id;
+	slot->slot_time = slot_time;
+	ret = cc33xx_cmd_configure(wl, SLOT_CFG, slot, sizeof(*slot));
+
+	if (ret < 0) {
+		cc33xx_warning("failed to set slot time: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(slot);
+	return ret;
+}
+
+int cc33xx_acx_group_address_tbl(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				 bool enable, void *mc_list, u32 mc_list_len)
+{
+	struct acx_dot11_grp_addr_tbl *acx;
+    int ret;
+
+    acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+    if (!acx) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+	cc33xx_debug(DEBUG_ACX, "acx group address tbl");
+
+    acx->enabled = enable;
+    acx->num_groups = mc_list_len;
+    memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
+
+    ret = cc33xx_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx));
+    if (ret < 0) {
+        cc33xx_warning("failed to set group addr table: %d", ret);
+        goto out;
+    }
+out:
+    kfree(acx);
+    return ret;
+}
+
+int cc33xx_acx_service_period_timeout(struct cc33xx *wl,
+				      struct cc33xx_vif *wlvif)
+{
+	struct acx_rx_timeout *rx_timeout;
+	int ret;
+
+	rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL);
+	if (!rx_timeout) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_ACX, "acx service period timeout");
+
+	rx_timeout->role_id = wlvif->role_id;
+	rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.host_conf.rx.ps_poll_timeout);
+	rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.host_conf.rx.upsd_timeout);
+
+	ret = cc33xx_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT,
+				   rx_timeout, sizeof(*rx_timeout));
+	if (ret < 0) {
+		cc33xx_warning("failed to set service period timeout: %d",
+			       ret);
+		goto out;
+	}
+
+out:
+	kfree(rx_timeout);
+	return ret;
+}
+
+int cc33xx_acx_rts_threshold(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			     u32 rts_threshold)
+{
+	struct acx_rts_threshold *rts;
+	int ret;
+
+	/*
+	 * If the RTS threshold is not configured or out of range, use the
+	 * default value.
+	 */
+	if (rts_threshold > IEEE80211_MAX_RTS_THRESHOLD)
+		rts_threshold = wl->conf.host_conf.rx.rts_threshold;
+
+	cc33xx_debug(DEBUG_ACX, "acx rts threshold: %d", rts_threshold);
+
+	rts = kzalloc(sizeof(*rts), GFP_KERNEL);
+	if (!rts) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	rts->role_id = wlvif->role_id;
+	rts->threshold = cpu_to_le16((u16)rts_threshold);
+
+	ret = cc33xx_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts));
+	if (ret < 0) {
+		cc33xx_warning("failed to set rts threshold: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(rts);
+	return ret;
+}
+
+int cc33xx_acx_dco_itrim_params(struct cc33xx *wl)
+{
+	struct acx_dco_itrim_params *dco;
+	struct conf_itrim_settings *c = &wl->conf.host_conf.itrim;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx dco itrim parameters");
+
+	dco = kzalloc(sizeof(*dco), GFP_KERNEL);
+	if (!dco) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dco->enable = c->enable;
+	dco->timeout = cpu_to_le32(c->timeout);
+
+	ret = cc33xx_cmd_configure(wl, ACX_SET_DCO_ITRIM_PARAMS,
+				   dco, sizeof(*dco));
+	if (ret < 0) {
+		cc33xx_warning("failed to set dco itrim parameters: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(dco);
+	return ret;
+}
+
+int cc33xx_acx_beacon_filter_opt(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				 bool enable_filter)
+{
+	struct acx_beacon_filter_option *beacon_filter = NULL;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx beacon filter opt enable=%d",
+		     enable_filter);
+
+	if (enable_filter &&
+	    wl->conf.host_conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED)
+		goto out;
+
+	beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL);
+	if (!beacon_filter) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	beacon_filter->role_id = wlvif->role_id;
+	beacon_filter->enable = enable_filter;
+
+	/*
+	 * When set to zero, and the filter is enabled, beacons
+	 * without the unicast TIM bit set are dropped.
+	 */
+	beacon_filter->max_num_beacons = 0;
+
+	ret = cc33xx_cmd_configure(wl, ACX_BEACON_FILTER_OPT,
+				   beacon_filter, sizeof(*beacon_filter));
+	if (ret < 0) {
+		cc33xx_warning("failed to set beacon filter opt: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(beacon_filter);
+	return ret;
+}
+
+int cc33xx_acx_beacon_filter_table(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif)
+{
+	struct acx_beacon_filter_ie_table *ie_table;
+	struct conf_bcn_filt_rule bcn_filt_ie[32];
+	struct conf_bcn_filt_rule* p_bcn_filt_ie;
+	int i, idx = 0;
+	int ret;
+	bool vendor_spec = false;
+
+	cc33xx_debug(DEBUG_ACX, "acx beacon filter table");
+
+	ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL);
+	if (!ie_table) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* configure default beacon pass-through rules */
+	ie_table->role_id = wlvif->role_id;
+	ie_table->num_ie = 0;
+	p_bcn_filt_ie =&(wl->conf.host_conf.conn.bcn_filt_ie0);
+	memcpy(bcn_filt_ie,p_bcn_filt_ie,32* sizeof(struct conf_bcn_filt_rule));
+	for (i = 0; i < wl->conf.host_conf.conn.bcn_filt_ie_count; i++) {
+		struct conf_bcn_filt_rule *r = &bcn_filt_ie[i];
+		ie_table->table[idx++] = r->ie;
+		ie_table->table[idx++] = r->rule;
+
+		if (r->ie == WLAN_EID_VENDOR_SPECIFIC) {
+			/* only one vendor specific ie allowed */
+			if (vendor_spec)
+				continue;
+
+			/* for vendor specific rules configure the
+			   additional fields */
+			memcpy(&(ie_table->table[idx]), r->oui,
+			       CONF_BCN_IE_OUI_LEN);
+			idx += CONF_BCN_IE_OUI_LEN;
+			ie_table->table[idx++] = r->type;
+			memcpy(&(ie_table->table[idx]), r->version,
+			       CONF_BCN_IE_VER_LEN);
+			idx += CONF_BCN_IE_VER_LEN;
+			vendor_spec = true;
+		}
+
+		ie_table->num_ie++;
+	}
+
+	ret = cc33xx_cmd_configure(wl, ACX_BEACON_FILTER_TABLE,
+				   ie_table, sizeof(*ie_table));
+	if (ret < 0) {
+		cc33xx_warning("failed to set beacon filter table: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(ie_table);
+	return ret;
+}
+
+#define ACX_CONN_MONIT_DISABLE_VALUE  0xffffffff
+
+int cc33xx_acx_conn_monit_params(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				 bool enable)
+{
+	struct acx_conn_monit_params *acx;
+	u32 threshold = ACX_CONN_MONIT_DISABLE_VALUE;
+	u32 timeout = ACX_CONN_MONIT_DISABLE_VALUE;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx connection monitor parameters: %s",
+		     enable ? "enabled" : "disabled");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (enable) {
+		threshold = wl->conf.host_conf.conn.synch_fail_thold;
+		timeout = wl->conf.host_conf.conn.bss_lose_timeout;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->synch_fail_thold = cpu_to_le32(threshold);
+	acx->bss_lose_timeout = cpu_to_le32(timeout);
+
+	ret = cc33xx_cmd_configure(wl, ACX_CONN_MONIT_PARAMS,
+				   acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("failed to set connection monitor "
+			       "parameters: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_cca_threshold(struct cc33xx *wl)
+{
+	struct acx_energy_detection *detection;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx cca threshold");
+
+	detection = kzalloc(sizeof(*detection), GFP_KERNEL);
+	if (!detection) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	detection->rx_cca_threshold = cpu_to_le16(wl->conf.host_conf.rx.rx_cca_threshold);
+	detection->tx_energy_detection = wl->conf.host_conf.tx.tx_energy_detection;
+
+	ret = cc33xx_cmd_configure(wl, ACX_CCA_THRESHOLD,
+				   detection, sizeof(*detection));
+	if (ret < 0)
+		cc33xx_warning("failed to set cca threshold: %d", ret);
+
+out:
+	kfree(detection);
+	return ret;
+}
+
+int cc33xx_acx_bcn_dtim_options(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct acx_beacon_broadcast *bb;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx bcn dtim options");
+
+	bb = kzalloc(sizeof(*bb), GFP_KERNEL);
+	if (!bb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	bb->role_id = wlvif->role_id;
+	bb->beacon_rx_timeout = cpu_to_le16(wl->conf.host_conf.conn.beacon_rx_timeout);
+	bb->broadcast_timeout = cpu_to_le16(wl->conf.host_conf.conn.broadcast_timeout);
+	bb->rx_broadcast_in_ps = wl->conf.host_conf.conn.rx_broadcast_in_ps;
+	bb->ps_poll_threshold = wl->conf.host_conf.conn.ps_poll_threshold;
+
+	ret = cc33xx_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb));
+	if (ret < 0) {
+		cc33xx_warning("failed to set rx config: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(bb);
+	return ret;
+}
+ 
+int cc33xx_assoc_info_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif, struct ieee80211_sta *sta,u16 aid)
+{
+    struct assoc_info_cfg *cfg;
+    int ret;
+
+    cc33xx_debug(DEBUG_ACX, "acx aid");
+
+    cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+    if (!cfg) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    cfg->role_id = wlvif->role_id;
+    cfg->aid = cpu_to_le16(aid);
+    cfg->wmm_enabled = wlvif->wmm_enabled;
+
+    cfg->nontransmitted = wlvif->nontransmitted;
+    cfg->bssid_index = wlvif->bssid_index;
+    cfg->bssid_indicator = wlvif->bssid_indicator;
+	cfg->ht_supported = sta->deflink.ht_cap.ht_supported;
+	cfg->vht_supported = sta->deflink.vht_cap.vht_supported;
+	cfg->has_he = sta->deflink.he_cap.has_he;
+    memcpy(cfg->transmitter_bssid,
+		wlvif->transmitter_bssid, 
+		ETH_ALEN);
+    ret = cc33xx_cmd_configure(wl, ASSOC_INFO_CFG, cfg, sizeof(*cfg));
+    if (ret < 0) {
+        cc33xx_warning("failed to set aid: %d", ret);
+        goto out;
+    }
+
+out:
+    kfree(cfg);
+    return ret;
+}
+
+int cc33xx_acx_aid(struct cc33xx *wl, struct cc33xx_vif *wlvif, u16 aid)
+{
+	struct acx_aid *acx_aid;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx aid");
+
+	acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL);
+	if (!acx_aid) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx_aid->role_id = wlvif->role_id;
+	acx_aid->aid = cpu_to_le16(aid);
+
+	ret = cc33xx_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid));
+	if (ret < 0) {
+		cc33xx_warning("failed to set aid: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx_aid);
+	return ret;
+}
+
+int cc33xx_acx_event_mbox_mask(struct cc33xx *wl, u32 event_mask)
+{
+	struct acx_event_mask *mask;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx event mbox mask");
+
+	mask = kzalloc(sizeof(*mask), GFP_KERNEL);
+	if (!mask) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* high event mask is unused */
+	mask->high_event_mask = cpu_to_le32(0xffffffff);
+	mask->event_mask = cpu_to_le32(event_mask);
+
+	ret = cc33xx_cmd_configure(wl, ACX_EVENT_MBOX_MASK,
+				   mask, sizeof(*mask));
+	if (ret < 0) {
+		cc33xx_warning("failed to set acx_event_mbox_mask: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(mask);
+	return ret;
+}
+
+int cc33xx_acx_set_preamble(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    enum acx_preamble_type preamble)
+{
+	struct acx_preamble *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx_set_preamble");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->preamble = preamble;
+
+	ret = cc33xx_cmd_configure(wl, PREAMBLE_TYPE_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("Setting of preamble failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_cts_protect(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   enum acx_ctsprotect_type ctsprotect)
+{
+	struct acx_ctsprotect *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx_set_ctsprotect");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->ctsprotect = ctsprotect;
+
+	ret = cc33xx_cmd_configure(wl, CTS_PROTECTION_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("Setting of ctsprotect failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_statistics(struct cc33xx *wl, void *stats)
+{
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx statistics");
+
+	ret = cc33xx_cmd_interrogate(wl, ACX_STATISTICS, stats,
+				     sizeof(struct acx_header),
+				     wl->stats.fw_stats_len);
+	if (ret < 0) {
+		cc33xx_warning("acx statistics failed: %d", ret);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int cc33xx_update_ap_rates(struct cc33xx *wl,u8 role_id,u32 basic_rates_set,u32 supported_rates){
+
+	struct ap_rates_class_cfg *cfg;
+	int ret;
+	cc33xx_debug(DEBUG_AP, "Attempting to Update Basic Rates and Supported Rates");
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+
+    if (!cfg) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+	cfg->basic_rates_set = cpu_to_le32(basic_rates_set);
+	cfg->supported_rates = cpu_to_le32(supported_rates);
+	cfg->role_id = role_id;
+	ret = cc33xx_cmd_configure(wl, AP_RATES_CFG, cfg, sizeof(*cfg));
+	if (ret < 0) {
+		cc33xx_warning("Updating AP Rates  failed: %d", ret);
+		goto out;
+	}
+
+out:
+    kfree(cfg);
+    return ret;
+}
+
+int cc33xx_tx_param_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+              u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop, bool acm,
+              u8 ps_scheme, u8 is_mu_edca, u8 mu_edca_aifs, u8 mu_edca_ecw_min_max,
+              u8 mu_edca_timer)
+{
+    struct tx_param_cfg *cfg;
+    int ret = 0;
+
+    cc33xx_debug(DEBUG_ACX, "tx param cfg %d cw_ming %d cw_max %d "
+             "aifs %d txop %d", ac, cw_min, cw_max, aifsn, txop);
+
+    cc33xx_debug(DEBUG_ACX, "tx param cfg ps_scheme %d is_mu_edca %d mu_edca_aifs %d "
+                 "mu_edca_ecw_min_max %d mu_edca_timer %d",
+                 ps_scheme, is_mu_edca, mu_edca_aifs, mu_edca_ecw_min_max, mu_edca_timer);
+
+    cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+
+    if (!cfg) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    cfg->role_id = wlvif->role_id;
+    cfg->ac = ac;
+    cfg->cw_min = cw_min;
+    cfg->cw_max = cpu_to_le16(cw_max);
+    cfg->aifsn = aifsn;
+    cfg->tx_op_limit = cpu_to_le16(txop);
+    cfg->acm = cpu_to_le16(acm);
+
+    cfg->ps_scheme = ps_scheme;
+    cfg->is_mu_edca = is_mu_edca;
+    cfg->mu_edca_aifs = mu_edca_aifs;
+    cfg->mu_edca_ecw_min_max = mu_edca_ecw_min_max;
+    cfg->mu_edca_timer = mu_edca_timer;
+
+    ret = cc33xx_cmd_configure(wl, TX_PARAMS_CFG, cfg, sizeof(*cfg));
+    if (ret < 0) {
+        cc33xx_warning("tx param cfg failed: %d", ret);
+        goto out;
+    }
+
+out:
+    kfree(cfg);
+    return ret;
+}
+
+int cc33xx_acx_ac_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		      u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop)
+{
+	struct acx_ac_cfg *acx;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d "
+		     "aifs %d txop %d", ac, cw_min, cw_max, aifsn, txop);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->ac = ac;
+	acx->cw_min = cw_min;
+	acx->cw_max = cpu_to_le16(cw_max);
+	acx->aifsn = aifsn;
+	acx->tx_op_limit = cpu_to_le16(txop);
+
+	ret = cc33xx_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx ac cfg failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_tid_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       u8 queue_id, u8 channel_type,
+		       u8 tsid, u8 ps_scheme, u8 ack_policy,
+		       u32 apsd_conf0, u32 apsd_conf1)
+{
+	struct acx_tid_config *acx;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx tid config");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->queue_id = queue_id;
+	acx->channel_type = channel_type;
+	acx->tsid = tsid;
+	acx->ps_scheme = ps_scheme;
+	acx->ack_policy = ack_policy;
+	acx->apsd_conf[0] = cpu_to_le32(apsd_conf0);
+	acx->apsd_conf[1] = cpu_to_le32(apsd_conf1);
+
+	ret = cc33xx_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("Setting of tid config failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_frag_threshold(struct cc33xx *wl, u32 frag_threshold)
+{
+	struct acx_frag_threshold *acx;
+	int ret = 0;
+
+	/*
+	 * If the fragmentation is not configured or out of range, use the
+	 * default value.
+	 */
+	if (frag_threshold > IEEE80211_MAX_FRAG_THRESHOLD)
+		frag_threshold = wl->conf.host_conf.tx.frag_threshold;
+
+	cc33xx_debug(DEBUG_ACX, "acx frag threshold: %d", frag_threshold);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->frag_threshold = cpu_to_le16((u16)frag_threshold);
+	ret = cc33xx_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("Setting of frag threshold failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_tx_config_options(struct cc33xx *wl)
+{
+	struct acx_tx_config_options *acx;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx tx config options");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->tx_compl_timeout = cpu_to_le16(wl->conf.host_conf.tx.tx_compl_timeout);
+	acx->tx_compl_threshold = cpu_to_le16(wl->conf.host_conf.tx.tx_compl_threshold);
+	ret = cc33xx_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("Setting of tx options failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_mem_cfg(struct cc33xx *wl)
+{
+	struct cc33xx_acx_config_memory *mem_conf;
+	struct conf_memory_settings *mem;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "cc33xx mem cfg");
+
+	mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL);
+	if (!mem_conf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	mem = &wl->conf.host_conf.mem;
+
+	/* memory config */
+	mem_conf->num_stations = mem->num_stations;
+	mem_conf->rx_mem_block_num = mem->rx_block_num;
+	mem_conf->tx_min_mem_block_num = mem->tx_min_block_num;
+	mem_conf->num_ssid_profiles = mem->ssid_profiles;
+	mem_conf->total_tx_descriptors = cpu_to_le32(wl->num_tx_desc);
+	mem_conf->dyn_mem_enable = mem->dynamic_memory;
+	mem_conf->tx_free_req = mem->min_req_tx_blocks;
+	mem_conf->rx_free_req = mem->min_req_rx_blocks;
+	mem_conf->tx_min = mem->tx_min;
+	mem_conf->fwlog_blocks = wl->conf.host_conf.fwlog.mem_blocks;
+
+	ret = cc33xx_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
+				   sizeof(*mem_conf));
+	if (ret < 0) {
+		cc33xx_warning("cc33xx mem config failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(mem_conf);
+	return ret;
+}
+
+int cc33xx_acx_init_mem_config(struct cc33xx *wl)
+{
+	int ret;
+
+	wl->target_mem_map = kzalloc(sizeof(struct cc33xx_acx_mem_map),
+				     GFP_KERNEL);
+	if (!wl->target_mem_map) {
+		cc33xx_error("couldn't allocate target memory map");
+		return -ENOMEM;
+	}
+
+	/* we now ask for the firmware built memory map */
+	ret = cc33xx_acx_mem_map(wl, (void *)wl->target_mem_map,
+				 sizeof(struct cc33xx_acx_mem_map));
+	if (ret < 0) {
+		cc33xx_error("couldn't retrieve firmware memory map");
+		kfree(wl->target_mem_map);
+		wl->target_mem_map = NULL;
+		return ret;
+	}
+
+	/* initialize TX block book keeping */
+	wl->tx_blocks_available =
+		le32_to_cpu(wl->target_mem_map->num_tx_mem_blocks);
+	cc33xx_debug(DEBUG_TX, "available tx blocks: %d",
+		     wl->tx_blocks_available);
+
+	cc33xx_debug(DEBUG_TX, "available tx descriptor: %d available rx blocks %d",
+		     wl->target_mem_map->num_tx_descriptor,
+		     wl->target_mem_map->num_rx_mem_blocks);
+
+	return 0;
+}
+
+int cc33xx_acx_init_get_fw_versions(struct cc33xx *wl)
+{
+	int ret;
+
+	wl->all_versions.fw_ver = kzalloc(sizeof(struct cc33xx_acx_fw_versions),
+					GFP_KERNEL);
+	if (!wl->all_versions.fw_ver) {
+	 	cc33xx_error("couldn't allocate cc33xx_acx_fw_versions");
+		return -ENOMEM;
+	}	
+
+	ret = cc33xx_acx_get_fw_versions(wl, (void *)wl->all_versions.fw_ver,
+				sizeof(struct cc33xx_acx_fw_versions));
+	if (ret < 0) {
+		cc33xx_error("couldn't retrieve firmware versions");
+		kfree(wl->all_versions.fw_ver);
+		wl->all_versions.fw_ver = NULL;		
+		return ret;
+	}
+
+	return 0;
+}
+
+int cc33xx_acx_init_rx_interrupt(struct cc33xx *wl)
+{
+	struct cc33xx_acx_rx_config_opt *rx_conf;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "cc33xx rx interrupt config");
+
+	rx_conf = kzalloc(sizeof(*rx_conf), GFP_KERNEL);
+	if (!rx_conf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	rx_conf->threshold = cpu_to_le16(wl->conf.host_conf.rx.irq_pkt_threshold);
+	rx_conf->timeout = cpu_to_le16(wl->conf.host_conf.rx.irq_timeout);
+	rx_conf->mblk_threshold = cpu_to_le16(wl->conf.host_conf.rx.irq_blk_threshold);
+	rx_conf->queue_type = wl->conf.host_conf.rx.queue_type;
+
+	ret = cc33xx_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf,
+				   sizeof(*rx_conf));
+	if (ret < 0) {
+		cc33xx_warning("cc33xx rx config opt failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(rx_conf);
+	return ret;
+}
+
+int cc33xx_acx_bet_enable(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  bool enable)
+{
+	struct cc33xx_acx_bet_enable *acx = NULL;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx bet enable");
+
+	if (enable && wl->conf.host_conf.conn.bet_enable == CONF_BET_MODE_DISABLE)
+		goto out;
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE;
+	acx->max_consecutive = wl->conf.host_conf.conn.bet_max_consecutive;
+
+	ret = cc33xx_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx bet enable failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33x_acx_arp_ip_filter(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			     u8 enable, __be32 address)
+{
+	struct cc33xx_acx_arp_filter *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->version = ACX_IPV4_VERSION;
+	acx->enable = enable;
+
+	if (enable)
+		memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE);
+
+	ret = cc33xx_cmd_configure(wl, ACX_ARP_IP_FILTER,
+				   acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("failed to set arp ip filter: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_pm_config(struct cc33xx *wl)
+{
+	struct cc33xx_acx_pm_config *acx = NULL;
+	struct  conf_pm_config_settings *c = &wl->conf.host_conf.pm_config;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx pm config");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->host_clk_settling_time = cpu_to_le32(c->host_clk_settling_time);
+	acx->host_fast_wakeup_support = c->host_fast_wakeup_support;
+
+	ret = cc33xx_cmd_configure(wl, ACX_PM_CONFIG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx pm config failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_rssi_snr_trigger(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				bool enable, s16 thold, u8 hyst)
+{
+	struct cc33xx_acx_rssi_snr_trigger *acx = NULL;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx rssi snr trigger");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	wlvif->last_rssi_event = -1;
+
+	acx->role_id = wlvif->role_id;
+	acx->pacing = cpu_to_le16(wl->conf.host_conf.roam_trigger.trigger_pacing);
+	acx->metric = CC33XX_ACX_TRIG_METRIC_RSSI_BEACON;
+	acx->type = CC33XX_ACX_TRIG_TYPE_EDGE;
+	if (enable)
+		acx->enable = CC33XX_ACX_TRIG_ENABLE;
+	else
+		acx->enable = CC33XX_ACX_TRIG_DISABLE;
+
+	acx->index = CC33XX_ACX_TRIG_IDX_RSSI;
+	acx->dir = CC33XX_ACX_TRIG_DIR_BIDIR;
+	acx->threshold = cpu_to_le16(thold);
+	acx->hysteresis = hyst;
+
+	ret = cc33xx_cmd_configure(wl, ACX_RSSI_SNR_TRIGGER, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx rssi snr trigger setting failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_rssi_snr_avg_weights(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_acx_rssi_snr_avg_weights *acx = NULL;
+	struct conf_roam_trigger_settings *c = &wl->conf.host_conf.roam_trigger;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx rssi snr avg weights");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->rssi_beacon = c->avg_weight_rssi_beacon;
+	acx->rssi_data = c->avg_weight_rssi_data;
+	acx->snr_beacon = c->avg_weight_snr_beacon;
+	acx->snr_data = c->avg_weight_snr_data;
+
+	ret = cc33xx_cmd_configure(wl, ACX_RSSI_SNR_WEIGHTS, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx rssi snr trigger weights failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_set_ht_capabilities(struct cc33xx *wl,
+				    struct ieee80211_sta_ht_cap *ht_cap,
+				    bool allow_ht_operation, u8 hlid)
+{
+	struct cc33xx_acx_ht_capabilities *acx;
+	int ret = 0;
+	u32 ht_capabilites = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx ht capabilities setting "
+		     "sta supp: %d sta cap: %d", ht_cap->ht_supported,
+		     ht_cap->cap);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (allow_ht_operation && ht_cap->ht_supported) {
+		/* no need to translate capabilities - use the spec values */
+		ht_capabilites = ht_cap->cap;
+
+		/*
+		 * this bit is not employed by the spec but only by FW to
+		 * indicate peer HT support
+		 */
+		ht_capabilites |= CC33XX_HT_CAP_HT_OPERATION;
+
+		/* get data from A-MPDU parameters field */
+		acx->ampdu_max_length = ht_cap->ampdu_factor;
+		acx->ampdu_min_spacing = ht_cap->ampdu_density;
+	}
+
+	acx->hlid = hlid;
+	acx->ht_capabilites = cpu_to_le32(ht_capabilites);
+
+	ret = cc33xx_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx ht capabilities setting failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+
+int cc33xx_acx_set_ht_information(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif,
+				   u16 ht_operation_mode,
+				   u32 he_oper_params, u16 he_oper_nss_set)
+{
+	struct cc33xx_acx_ht_information *acx;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx ht information setting");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+	acx->ht_protection =
+		(u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION);
+	acx->rifs_mode = 0;
+	acx->gf_protection =
+		!!(ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+
+	acx->dual_cts_protection = 0;
+	
+    	cc33xx_debug(DEBUG_ACX, "HE operation: 0x%xm mcs: 0x%x",he_oper_params, he_oper_nss_set);
+
+	acx->he_operation = cpu_to_le32(he_oper_params);
+	acx->bss_basic_mcs_set = cpu_to_le16(he_oper_nss_set);
+	acx->qos_info_more_data_ack_bit = 0; // TODO
+	ret = cc33xx_cmd_configure(wl, BSS_OPERATION_CFG, acx, sizeof(*acx));
+
+	if (ret < 0) {
+		cc33xx_warning("acx ht information setting failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+/* Configure BA session initiator/receiver parameters setting in the FW. */
+int cc33xx_acx_set_ba_initiator_policy(struct cc33xx *wl,
+				       struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_acx_ba_initiator_policy *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx ba initiator policy");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* set for the current role */
+	acx->role_id = wlvif->role_id;
+	acx->tid_bitmap = wl->conf.host_conf.ht.tx_ba_tid_bitmap;
+	acx->win_size = wl->conf.host_conf.ht.tx_ba_win_size;
+	acx->inactivity_timeout = cpu_to_le16(wl->conf.host_conf.ht.inactivity_timeout);
+
+	ret = cc33xx_cmd_configure(wl,
+				   ACX_BA_SESSION_INIT_POLICY,
+				   acx,
+				   sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx ba initiator policy failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+/* setup BA session receiver setting in the FW. */
+int cc33xx_acx_set_ba_receiver_session(struct cc33xx *wl, u8 tid_index,
+				       u16 ssn, bool enable, u8 peer_hlid,
+				       u8 win_size)
+{
+	struct cc33xx_acx_ba_receiver_setup *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx ba receiver session setting");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->hlid = peer_hlid;
+	acx->tid = tid_index;
+	acx->enable = enable;
+	acx->win_size =	win_size;
+	acx->ssn = cpu_to_le16(ssn);
+
+	ret = wlcore_cmd_configure_failsafe(wl, BA_SESSION_RX_SETUP_CFG, acx,
+					    sizeof(*acx),
+					    BIT(CMD_STATUS_NO_RX_BA_SESSION));
+	if (ret < 0) {
+		cc33xx_warning("acx ba receiver session failed: %d", ret);
+		goto out;
+	}
+
+	/* sometimes we can't start the session */
+	if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
+		cc33xx_warning("no fw rx ba on tid %d", tid_index);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = 0;
+out:
+	kfree(acx);
+	return ret;
+}
+
+
+int cc33xx_acx_static_calibration_configure(struct cc33xx *wl,
+                        		    struct calibration_file_header *file_header,
+					    u8 *calibration_entry_ptr,
+					    bool valid_data)
+{
+	struct cc33xx_acx_static_calibration_cfg *acx;
+	size_t size;
+	struct calibration_header *calibration_header;
+	struct calibration_header_fw *fw_calibration_header;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx static calibration configuration");
+
+	if (valid_data) {
+		calibration_header = (struct calibration_header *)calibration_entry_ptr;
+
+		/* Extract the part of header that goes to FW */
+		fw_calibration_header = &(calibration_header->cal_header_fw);
+
+		size = ALIGN(sizeof(struct cc33xx_acx_static_calibration_cfg) 
+				 + fw_calibration_header->length, 4);
+	} else {
+		size = sizeof(struct cc33xx_acx_static_calibration_cfg);
+	}
+
+	acx = kzalloc(size, GFP_KERNEL);
+	if (!acx) {
+		cc33xx_warning("acx static calibration configuration "
+				"process failed due to memory allocation "
+				"failure");
+		return -ENOMEM;
+	}
+	acx->valid_data = valid_data;
+
+	if (!valid_data) {
+		cc33xx_debug(DEBUG_ACX, "Sending empty calibration data to FW");
+		goto out;
+	}
+
+    	acx->file_version = file_header->file_version;
+	acx->payload_struct_version = file_header->payload_struct_version;
+	
+	/* copy header & payload */
+	memcpy(&(acx->calibration_header), fw_calibration_header,
+	       sizeof(acx->calibration_header) + fw_calibration_header->length);
+
+	cc33xx_debug(DEBUG_ACX, "acx calibration payload length: %d",
+			acx->calibration_header.length);
+
+out:
+	ret = cc33xx_cmd_configure(wl,
+				   STATIC_CALIBRATION_CFG,
+				   acx,
+				   size);
+
+	if (ret < 0)
+		cc33xx_warning("acx command sending failed: %d", ret);
+	
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_tsf_info(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			u64 *mactime)
+{
+	struct cc33xx_acx_fw_tsf_information *tsf_info;
+	int ret;
+
+	tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL);
+	if (!tsf_info) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	tsf_info->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info,
+				sizeof(struct acx_header), sizeof(*tsf_info));
+	if (ret < 0) {
+		cc33xx_warning("acx tsf info interrogate failed");
+		goto out;
+	}
+
+	*mactime = le32_to_cpu(tsf_info->current_tsf_low) |
+		((u64) le32_to_cpu(tsf_info->current_tsf_high) << 32);
+
+out:
+	kfree(tsf_info);
+	return ret;
+}
+
+int cc33xx_acx_ps_rx_streaming(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			       bool enable)
+{
+	struct cc33xx_acx_ps_rx_streaming *rx_streaming;
+	u32 conf_queues, enable_queues;
+	int i, ret = 0;
+
+	cc33xx_debug(DEBUG_ACX, "acx ps rx streaming");
+
+	rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL);
+	if (!rx_streaming) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	conf_queues = wl->conf.host_conf.rx_streaming.queues;
+	if (enable)
+		enable_queues = conf_queues;
+	else
+		enable_queues = 0;
+
+	for (i = 0; i < 8; i++) {
+		/*
+		 * Skip non-changed queues, to avoid redundant acxs.
+		 * this check assumes conf.rx_streaming.queues can't
+		 * be changed while rx_streaming is enabled.
+		 */
+		if (!(conf_queues & BIT(i)))
+			continue;
+
+		rx_streaming->role_id = wlvif->role_id;
+		rx_streaming->tid = i;
+		rx_streaming->enable = enable_queues & BIT(i);
+		rx_streaming->period = wl->conf.host_conf.rx_streaming.interval;
+		rx_streaming->timeout = wl->conf.host_conf.rx_streaming.interval;
+
+		ret = cc33xx_cmd_configure(wl, ACX_PS_RX_STREAMING,
+					   rx_streaming,
+					   sizeof(*rx_streaming));
+		if (ret < 0) {
+			cc33xx_warning("acx ps rx streaming failed: %d", ret);
+			goto out;
+		}
+	}
+out:
+	kfree(rx_streaming);
+	return ret;
+}
+
+int cc33xx_acx_ap_max_tx_retry(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_acx_ap_max_tx_retry *acx = NULL;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx ap max tx retry");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx)
+		return -ENOMEM;
+
+	acx->role_id = wlvif->role_id;
+	acx->max_tx_retry = cpu_to_le16(wl->conf.host_conf.tx.max_tx_retries);
+
+	ret = cc33xx_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx ap max tx retry failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_config_ps(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_acx_config_ps *config_ps;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx config ps");
+
+	config_ps = kzalloc(sizeof(*config_ps), GFP_KERNEL);
+	if (!config_ps) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	config_ps->exit_retries = wl->conf.host_conf.conn.psm_exit_retries;
+	config_ps->enter_retries = wl->conf.host_conf.conn.psm_entry_retries;
+	config_ps->null_data_rate = cpu_to_le32(wlvif->basic_rate);
+
+	ret = cc33xx_cmd_configure(wl, ACX_CONFIG_PS, config_ps,
+				   sizeof(*config_ps));
+
+	if (ret < 0) {
+		cc33xx_warning("acx config ps failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(config_ps);
+	return ret;
+}
+
+int cc33xx_acx_set_inconnection_sta(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif, u8 *addr)
+{
+	struct cc33xx_acx_inconnection_sta *acx = NULL;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx)
+		return -ENOMEM;
+
+	memcpy(acx->addr, addr, ETH_ALEN);
+	acx->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST,
+				   acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx set inconnaction sta failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_set_rate_mgmt_params(struct cc33xx *wl)
+{
+	struct cc33xx_acx_set_rate_mgmt_params *acx = NULL;
+	struct conf_rate_policy_settings *conf = &wl->conf.host_conf.rate;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx set rate mgmt params");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx)
+		return -ENOMEM;
+
+	acx->index = ACX_RATE_MGMT_ALL_PARAMS;
+	acx->rate_retry_score = cpu_to_le16(conf->rate_retry_score);
+	acx->per_add = cpu_to_le16(conf->per_add);
+	acx->per_th1 = cpu_to_le16(conf->per_th1);
+	acx->per_th2 = cpu_to_le16(conf->per_th2);
+	acx->max_per = cpu_to_le16(conf->max_per);
+	acx->inverse_curiosity_factor = conf->inverse_curiosity_factor;
+	acx->tx_fail_low_th = conf->tx_fail_low_th;
+	acx->tx_fail_high_th = conf->tx_fail_high_th;
+	acx->per_alpha_shift = conf->per_alpha_shift;
+	acx->per_add_shift = conf->per_add_shift;
+	acx->per_beta1_shift = conf->per_beta1_shift;
+	acx->per_beta2_shift = conf->per_beta2_shift;
+	acx->rate_check_up = conf->rate_check_up;
+	acx->rate_check_down = conf->rate_check_down;
+	memcpy(acx->rate_retry_policy, conf->rate_retry_policy,
+	       sizeof(acx->rate_retry_policy));
+
+	ret = cc33xx_cmd_configure(wl, ACX_SET_RATE_MGMT_PARAMS,
+				   acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx set rate mgmt params failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_config_hangover(struct cc33xx *wl)
+{
+	struct cc33xx_acx_config_hangover *acx;
+	struct conf_hangover_settings *conf = &wl->conf.host_conf.hangover;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx config hangover");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->recover_time = cpu_to_le32(conf->recover_time);
+	acx->hangover_period = conf->hangover_period;
+	acx->dynamic_mode = conf->dynamic_mode;
+	acx->early_termination_mode = conf->early_termination_mode;
+	acx->max_period = conf->max_period;
+	acx->min_period = conf->min_period;
+	acx->increase_delta = conf->increase_delta;
+	acx->decrease_delta = conf->decrease_delta;
+	acx->quiet_time = conf->quiet_time;
+	acx->increase_time = conf->increase_time;
+	acx->window_size = conf->window_size;
+
+	ret = cc33xx_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx,
+				   sizeof(*acx));
+
+	if (ret < 0) {
+		cc33xx_warning("acx config hangover failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+
+}
+
+int wlcore_acx_average_rssi(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    s8 *avg_rssi)
+{
+	struct acx_roaming_stats *acx;
+	int ret = 0;
+	cc33xx_debug(DEBUG_ACX, "acx roaming statistics");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_interrogate(wl, RSSI_INTR,
+				     acx, sizeof(*acx), sizeof(*acx));
+	if (ret	< 0) {
+		cc33xx_warning("acx roaming statistics failed: %d", ret);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	*avg_rssi = acx->rssi_beacon;
+	
+out:
+	kfree(acx);
+	return ret;
+}
+
+#ifdef CONFIG_PM
+/* Set the global behaviour of RX filters - On/Off + default action */
+int cc33xx_acx_default_rx_filter_enable(struct cc33xx *wl, bool enable,
+					enum rx_filter_action action)
+{
+	struct acx_default_rx_filter *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx default rx filter en: %d act: %d",
+		     enable, action);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx)
+		return -ENOMEM;
+
+	acx->enable = enable;
+	acx->default_action = action;
+
+	ret = cc33xx_cmd_configure(wl, ACX_ENABLE_RX_DATA_FILTER, acx,
+				   sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx default rx filter enable failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+/* Configure or disable a specific RX filter pattern */
+int cc33xx_acx_set_rx_filter(struct cc33xx *wl, u8 index, bool enable,
+			     struct cc33xx_rx_filter *filter)
+{
+	struct acx_rx_filter_cfg *acx;
+	int fields_size = 0;
+	int acx_size;
+	int ret;
+
+	WARN_ON(enable && !filter);
+	WARN_ON(index >= CC33XX_MAX_RX_FILTERS);
+
+	cc33xx_debug(DEBUG_ACX,
+		     "acx set rx filter idx: %d enable: %d filter: %p",
+		     index, enable, filter);
+
+	if (enable) {
+		fields_size = cc33xx_rx_filter_get_fields_size(filter);
+
+		cc33xx_debug(DEBUG_ACX, "act: %d num_fields: %d field_size: %d",
+		      filter->action, filter->num_fields, fields_size);
+	}
+
+	acx_size = ALIGN(sizeof(*acx) + fields_size, 4);
+	acx = kzalloc(acx_size, GFP_KERNEL);
+
+	if (!acx)
+		return -ENOMEM;
+
+	acx->enable = enable;
+	acx->index = index;
+
+	if (enable) {
+		acx->num_fields = filter->num_fields;
+		acx->action = filter->action;
+		cc33xx_rx_filter_flatten_fields(filter, acx->fields);
+	}
+
+	cc33xx_dump(DEBUG_ACX, "RX_FILTER: ", acx, acx_size);
+
+	ret = cc33xx_cmd_configure(wl, ACX_SET_RX_DATA_FILTER, acx, acx_size);
+	if (ret < 0) {
+		cc33xx_warning("setting rx filter failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+#endif /* CONFIG_PM */
+
+
+
+int cc33xx_acx_host_if_cfg_bitmap(struct cc33xx *wl, u32 host_cfg_bitmap,
+				  u32 sdio_blk_size, u32 extra_mem_blks,
+				  u32 len_field_size)
+{
+	struct cc33xx_acx_host_config_bitmap *bitmap_conf;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d",
+		     host_cfg_bitmap, sdio_blk_size, extra_mem_blks,
+		     len_field_size);
+
+	bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
+	if (!bitmap_conf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap);
+	bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size);
+	bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks);
+	bitmap_conf->length_field_size = cpu_to_le32(len_field_size);
+
+	ret = cc33xx_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP,
+				   bitmap_conf, sizeof(*bitmap_conf));
+	if (ret < 0) {
+		cc33xx_warning("cc33xx bitmap config opt failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(bitmap_conf);
+
+	return ret;
+}
+
+int cc33xx_acx_set_checksum_state(struct cc33xx *wl)
+{
+	struct cc33xx_acx_checksum_state *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx checksum state");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
+
+	ret = cc33xx_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("failed to set Tx checksum state: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+
+int cc33xx_acx_peer_ht_operation_mode(struct cc33xx *wl, u8 hlid, bool wide)
+{
+	struct wlcore_peer_ht_operation_mode *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d",
+		     hlid, wide);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->hlid = hlid;
+	acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ;
+
+	ret = cc33xx_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
+				   sizeof(*acx));
+
+	if (ret < 0) {
+		cc33xx_warning("acx peer ht operation mode failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+
+}
+
+/*
+ * this command is basically the same as cc33xx_acx_ht_capabilities,
+ * with the addition of supported rates. they should be unified in
+ * the next fw api change
+ */
+int cc33xx_acx_set_peer_cap(struct cc33xx *wl,
+			    struct ieee80211_sta_ht_cap *ht_cap,
+			    struct ieee80211_sta_he_cap *he_cap,
+			    struct cc33xx_vif *wlvif,
+			    bool allow_ht_operation,
+			    u32 rate_set, u8 hlid)
+{
+	struct wlcore_acx_peer_cap *acx;
+	int ret = 0;
+	u32 ht_capabilites = 0;
+
+	cc33xx_debug(DEBUG_ACX,
+		     "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x",
+		     ht_cap->ht_supported, ht_cap->cap, rate_set);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (allow_ht_operation && ht_cap->ht_supported) {
+		/* no need to translate capabilities - use the spec values */
+		ht_capabilites = ht_cap->cap;
+
+		/*
+		 * this bit is not employed by the spec but only by FW to
+		 * indicate peer HT support
+		 */
+		ht_capabilites |= CC33XX_HT_CAP_HT_OPERATION;
+
+		/* get data from A-MPDU parameters field */
+		acx->ampdu_max_length = ht_cap->ampdu_factor;
+		acx->ampdu_min_spacing = ht_cap->ampdu_density;
+	}
+
+	acx->ht_capabilites = cpu_to_le32(ht_capabilites);
+	acx->supported_rates = cpu_to_le32(rate_set);
+
+	acx->role_id = wlvif->role_id;
+	acx->has_he = he_cap->has_he;
+	memcpy(acx->mac_cap_info, he_cap->he_cap_elem.mac_cap_info, 6);
+	acx->nominal_packet_padding = (he_cap->he_cap_elem.phy_cap_info[8] & NOMINAL_PACKET_PADDING);
+	/* Max DCM constelation for RX - bits [4:3] in PHY capabilities byte 3 */
+	acx->dcm_max_constelation = (he_cap->he_cap_elem.phy_cap_info[3] & IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK) >> 3;
+	acx->er_upper_supported = ((he_cap->he_cap_elem.phy_cap_info[6] & IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE) != 0) ? 1 : 0;
+    ret = cc33xx_cmd_configure(wl, PEER_CAP_CFG, acx, sizeof(*acx));
+
+	if (ret < 0) {
+		cc33xx_warning("acx ht capabilities setting failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+/*
+ * When the host is suspended, we don't want to get any fast-link/PSM
+ * notifications
+ */
+int cc33xx_acx_interrupt_notify_config(struct cc33xx *wl,
+				       bool action)
+{
+	struct cc33xx_acx_interrupt_notify *acx;
+	int ret = 0;
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->enable = cpu_to_le32(action);
+	ret = cc33xx_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx interrupt notify setting failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+/*
+ * When the host is suspended, we can configure the FW to disable RX BA
+ * notifications.
+ */
+int cc33xx_acx_rx_ba_filter(struct cc33xx *wl, bool action)
+{
+	struct cc33xx_acx_rx_ba_filter *acx;
+	int ret = 0;
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->enable = cpu_to_le32(action);
+	ret = cc33xx_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx rx ba activity filter setting failed: %d",
+			       ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_ap_sleep(struct cc33xx *wl)
+{
+	struct acx_ap_sleep_cfg *acx;
+	struct conf_ap_sleep_settings *conf = &wl->conf.host_conf.ap_sleep;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx config ap sleep");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->idle_duty_cycle = conf->idle_duty_cycle;
+	acx->connected_duty_cycle = conf->connected_duty_cycle;
+	acx->max_stations_thresh = conf->max_stations_thresh;
+	acx->idle_conn_thresh = conf->idle_conn_thresh;
+
+	ret = cc33xx_cmd_configure(wl, ACX_AP_SLEEP_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx config ap-sleep failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_set_antenna_select(struct cc33xx *wl, u8 selection)
+{
+	struct acx_antenna_select *acx;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx setting antenna to %d", selection);
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->selection = selection;
+
+	ret = cc33xx_cmd_configure(wl, SET_ANTENNA_SELECT_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		cc33xx_warning("acx setting antenna failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int cc33xx_acx_set_tsf(struct cc33xx *wl, u64 tsf_val)
+{
+	struct debug_set_tsf *set_tsf_cmd;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx set tsf. new tsf value: %llx", tsf_val);
+
+	set_tsf_cmd = kzalloc(sizeof(*set_tsf_cmd), GFP_KERNEL);
+	if (!set_tsf_cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	set_tsf_cmd->tsf_val = cpu_to_le64(tsf_val);
+
+	ret = cc33xx_cmd_debug(wl, SET_TSF, set_tsf_cmd, sizeof(*set_tsf_cmd));
+	if (ret < 0) {
+		cc33xx_error("acx set tsf failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(set_tsf_cmd);
+	return ret;
+}
+
+int cc33xx_acx_trigger_fw_assert(struct cc33xx *wl)
+{
+	struct debug_header *buf;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx trigger firmware assert");
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_debug(wl, TRIGGER_FW_ASSERT, buf, sizeof(*buf));
+	if (ret < 0) {
+		cc33xx_error("failed to trigger firmware assert");
+		goto out;
+	}
+
+out:
+	kfree(buf);
+	return ret;
+}
+
+int cc33xx_acx_burst_mode_cfg(struct cc33xx *wl, u8 burst_disable)
+{
+	struct debug_burst_mode_cfg *burst_mode_cfg;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "acx burst mode cfg. burst_disable = %d", burst_disable);
+
+	burst_mode_cfg = kzalloc(sizeof(*burst_mode_cfg), GFP_KERNEL);
+	if (!burst_mode_cfg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	burst_mode_cfg->burst_disable = burst_disable;
+
+	ret = cc33xx_cmd_debug(wl, BURST_MODE_CFG, burst_mode_cfg, sizeof(*burst_mode_cfg));
+	if (ret < 0) {
+		cc33xx_warning("acx burst mode cfg failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(burst_mode_cfg);
+	return ret;
+}
diff --git drivers/net/wireless/ti/cc33xx/acx.h drivers/net/wireless/ti/cc33xx/acx.h
new file mode 100644
index 000000000000..fc1ad9a8e158
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/acx.h
@@ -0,0 +1,1638 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __ACX_H__
+#define __ACX_H__
+
+
+#include "wlcore.h"
+#include "cmd.h"
+
+/*************************************************************************
+
+    Host Interrupt Register (WiLink -> Host)
+
+**************************************************************************/
+/* HW Initiated interrupt Watchdog timer expiration */
+#define CC33XX_ACX_INTR_WATCHDOG           BIT(0)
+/* Init sequence is done (masked interrupt, detection through polling only ) */
+#define CC33XX_ACX_INTR_INIT_COMPLETE      BIT(1)
+/* Event was entered to Event MBOX #A*/
+#define CC33XX_ACX_INTR_EVENT_A            BIT(2)
+/* Event was entered to Event MBOX #B*/
+#define CC33XX_ACX_INTR_EVENT_B            BIT(3)
+/* Command processing completion*/
+#define CC33XX_ACX_INTR_CMD_COMPLETE       BIT(4)
+/* Signaling the host on HW wakeup */
+#define CC33XX_ACX_INTR_HW_AVAILABLE       BIT(5)
+/* The MISC bit is used for aggregation of RX, TxComplete and TX rate update */
+#define CC33XX_ACX_INTR_DATA               BIT(6)
+/* Trace message on MBOX #A */
+#define CC33XX_ACX_INTR_TRACE_A            BIT(7)
+/* Trace message on MBOX #B */
+#define CC33XX_ACX_INTR_TRACE_B            BIT(8)
+/* SW FW Initiated interrupt Watchdog timer expiration */
+#define CC33XX_ACX_SW_INTR_WATCHDOG        BIT(9)
+
+#define CC33XX_ACX_INTR_ALL             0xFFFFFFFF
+
+/* all possible interrupts - only appropriate ones will be masked in */
+#define WLCORE_ALL_INTR_MASK		(CC33XX_ACX_INTR_WATCHDOG     | \
+					CC33XX_ACX_INTR_EVENT_A       | \
+					CC33XX_ACX_INTR_EVENT_B       | \
+					CC33XX_ACX_INTR_HW_AVAILABLE  | \
+					CC33XX_ACX_INTR_DATA          | \
+					CC33XX_ACX_SW_INTR_WATCHDOG)
+
+enum {
+	/* Regular PS: simple sending of packets */
+	PS_SCHEME_LEGACY         = 0,
+	/* UPSD: sending a packet triggers a UPSD downstream*/
+	PS_SCHEME_UPSD_TRIGGER   = 1,
+	/* Mixed mode is partially supported: we are not going to sleep, and
+	   triggers (on APSD AC's) are not sent when service period ends with
+	   more_data = 1. */
+	PS_SCHEME_MIXED_MODE     = 2,
+	/* Legacy PSPOLL: a PSPOLL packet will be sent before every data packet
+	   transmission in this queue.*/
+	PS_SCHEME_LEGACY_PSPOLL  = 3,
+	/* Scheduled APSD mode. */
+	PS_SCHEME_SAPSD          = 4,
+	/* No PSPOLL: move to active after first packet. no need to sent
+	   pspoll */
+	PS_SCHEME_NOPSPOLL       = 5,
+	
+	MAX_PS_SCHEME = PS_SCHEME_NOPSPOLL
+};
+
+
+/* Target's information element */
+struct acx_header {
+	struct cc33xx_cmd_header cmd;
+
+	/* acx (or information element) header */
+	__le16 id;
+
+	/* payload length (not including headers */
+	__le16 len;
+} __packed;
+
+struct debug_header {
+	struct cc33xx_cmd_header cmd;
+
+	/* debug (or information element) header */
+	__le16 id;
+
+	/* payload length (not including headers */
+	__le16 len;
+} __packed;
+
+struct acx_error_counter {
+	struct acx_header header;
+
+	/* The number of PLCP errors since the last time this */
+	/* information element was interrogated. This field is */
+	/* automatically cleared when it is interrogated.*/
+	__le32 PLCP_error;
+
+	/* The number of FCS errors since the last time this */
+	/* information element was interrogated. This field is */
+	/* automatically cleared when it is interrogated.*/
+	__le32 FCS_error;
+
+	/* The number of MPDUs without PLCP header errors received*/
+	/* since the last time this information element was interrogated. */
+	/* This field is automatically cleared when it is interrogated.*/
+	__le32 valid_frame;
+
+	/* the number of missed sequence numbers in the squentially */
+	/* values of frames seq numbers */
+	__le32 seq_num_miss;
+} __packed;
+
+enum cc33xx_role {
+	CC33XX_ROLE_STA = 0,
+	CC33XX_ROLE_IBSS,
+	CC33XX_ROLE_AP,
+	CC33XX_ROLE_DEVICE,
+	CC33XX_ROLE_P2P_CL,
+	CC33XX_ROLE_P2P_GO,
+	CC33XX_ROLE_MESH_POINT,
+
+	ROLE_TRANSCEIVER     = 16,
+
+	CC33XX_INVALID_ROLE_TYPE = 0xff
+};
+
+enum cc33xx_psm_mode {
+	/* Active mode */
+	CC33XX_PSM_CAM = 0,
+
+	/* Power save mode */
+	CC33XX_PSM_PS = 1,
+
+	/* Extreme low power */
+	CC33XX_PSM_ELP = 2,
+
+	CC33XX_PSM_MAX = CC33XX_PSM_ELP,
+
+	/* illegal out of band value of PSM mode */
+	CC33XX_PSM_ILLEGAL = 0xff
+};
+
+struct acx_sleep_auth {
+	struct acx_header header;
+
+	/* The sleep level authorization of the device. */
+	/* 0 - Always active*/
+	/* 1 - Power down mode: light / fast sleep*/
+	/* 2 - ELP mode: Deep / Max sleep*/
+	u8  sleep_auth;
+	u8  padding[3];
+} __packed;
+
+enum {
+	HOSTIF_PCI_MASTER_HOST_INDIRECT,
+	HOSTIF_PCI_MASTER_HOST_DIRECT,
+	HOSTIF_SLAVE,
+	HOSTIF_PKT_RING,
+	HOSTIF_DONTCARE = 0xFF
+};
+
+#define DEFAULT_UCAST_PRIORITY          0
+#define DEFAULT_RX_Q_PRIORITY           0
+#define DEFAULT_RXQ_PRIORITY            0 /* low 0 .. 15 high  */
+#define DEFAULT_RXQ_TYPE                0x07    /* All frames, Data/Ctrl/Mgmt */
+#define TRACE_BUFFER_MAX_SIZE           256
+
+#define  DP_RX_PACKET_RING_CHUNK_SIZE 1600
+#define  DP_TX_PACKET_RING_CHUNK_SIZE 1600
+#define  DP_RX_PACKET_RING_CHUNK_NUM 2
+#define  DP_TX_PACKET_RING_CHUNK_NUM 2
+#define  DP_TX_COMPLETE_TIME_OUT 20
+
+#define TX_MSDU_LIFETIME_MIN       0
+#define TX_MSDU_LIFETIME_MAX       3000
+#define TX_MSDU_LIFETIME_DEF       512
+#define RX_MSDU_LIFETIME_MIN       0
+#define RX_MSDU_LIFETIME_MAX       0xFFFFFFFF
+#define RX_MSDU_LIFETIME_DEF       512000
+
+struct acx_rx_msdu_lifetime {
+	struct acx_header header;
+
+	/*
+	 * The maximum amount of time, in TU, before the
+	 * firmware discards the MSDU.
+	 */
+	__le32 lifetime;
+} __packed;
+
+enum acx_slot_type {
+	SLOT_TIME_LONG = 0,
+	SLOT_TIME_SHORT = 1,
+	DEFAULT_SLOT_TIME = SLOT_TIME_SHORT,
+	MAX_SLOT_TIMES = 0xFF
+};
+
+#define STATION_WONE_INDEX 0
+
+struct acx_slot {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 slot_time;
+	u8 reserved[2];
+} __packed;
+
+
+#define ACX_MC_ADDRESS_GROUP_MAX	(20)
+#define ADDRESS_GROUP_MAX_LEN		(ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX)
+
+struct acx_dot11_grp_addr_tbl {
+	struct acx_header header;
+
+	u8 enabled;
+	u8 num_groups;
+	u8 pad[2];
+	u8 mac_table[ADDRESS_GROUP_MAX_LEN];
+} __packed;
+
+struct acx_rx_timeout {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 reserved;
+	__le16 ps_poll_timeout;
+	__le16 upsd_timeout;
+	u8 padding[2];
+} __packed;
+
+struct acx_rts_threshold {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 reserved;
+	__le16 threshold;
+} __packed;
+
+struct acx_beacon_filter_option {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 enable;
+	/*
+	 * The number of beacons without the unicast TIM
+	 * bit set that the firmware buffers before
+	 * signaling the host about ready frames.
+	 * When set to 0 and the filter is enabled, beacons
+	 * without the unicast TIM bit set are dropped.
+	 */
+	u8 max_num_beacons;
+	u8 pad[1];
+} __packed;
+
+/*
+ * ACXBeaconFilterEntry (not 221)
+ * Byte Offset     Size (Bytes)    Definition
+ * ===========     ============    ==========
+ * 0               1               IE identifier
+ * 1               1               Treatment bit mask
+ *
+ * ACXBeaconFilterEntry (221)
+ * Byte Offset     Size (Bytes)    Definition
+ * ===========     ============    ==========
+ * 0               1               IE identifier
+ * 1               1               Treatment bit mask
+ * 2               3               OUI
+ * 5               1               Type
+ * 6               2               Version
+ *
+ *
+ * Treatment bit mask - The information element handling:
+ * bit 0 - The information element is compared and transferred
+ * in case of change.
+ * bit 1 - The information element is transferred to the host
+ * with each appearance or disappearance.
+ * Note that both bits can be set at the same time.
+ */
+#define	BEACON_FILTER_TABLE_MAX_IE_NUM		       (32)
+#define BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM (6)
+#define BEACON_FILTER_TABLE_IE_ENTRY_SIZE	       (2)
+#define BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE (6)
+#define BEACON_FILTER_TABLE_MAX_SIZE ((BEACON_FILTER_TABLE_MAX_IE_NUM * \
+			    BEACON_FILTER_TABLE_IE_ENTRY_SIZE) + \
+			   (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \
+			    BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE))
+
+struct acx_beacon_filter_ie_table {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 num_ie;
+	u8 pad[2];
+	u8 table[BEACON_FILTER_TABLE_MAX_SIZE];
+} __packed;
+
+struct acx_conn_monit_params {
+       struct acx_header header;
+
+	   u8 role_id;
+	   u8 padding[3];
+       __le32 synch_fail_thold; /* number of beacons missed */
+       __le32 bss_lose_timeout; /* number of TU's from synch fail */
+} __packed;
+
+struct acx_bt_wlan_coex {
+	struct acx_header header;
+
+	u8 enable;
+	u8 pad[3];
+} __packed;
+
+
+struct acx_dco_itrim_params {
+	struct acx_header header;
+
+	u8 enable;
+	u8 padding[3];
+	__le32 timeout;
+} __packed;
+
+struct acx_energy_detection {
+	struct acx_header header;
+
+	/* The RX Clear Channel Assessment threshold in the PHY */
+	__le16 rx_cca_threshold;
+	u8 tx_energy_detection;
+	u8 pad;
+} __packed;
+
+struct acx_beacon_broadcast {
+	struct acx_header header;
+
+	u8 role_id;
+	/* Enables receiving of broadcast packets in PS mode */
+	u8 rx_broadcast_in_ps;
+
+	__le16 beacon_rx_timeout;
+	__le16 broadcast_timeout;
+
+	/* Consecutive PS Poll failures before updating the host */
+	u8 ps_poll_threshold;
+	u8 pad[1];
+} __packed;
+
+struct acx_event_mask {
+	struct acx_header header;
+
+	__le32 event_mask;
+	__le32 high_event_mask; /* Unused */
+} __packed;
+
+#define SCAN_PASSIVE		BIT(0)
+#define SCAN_5GHZ_BAND		BIT(1)
+#define SCAN_TRIGGERED		BIT(2)
+#define SCAN_PRIORITY_HIGH	BIT(3)
+
+/* When set, disable HW encryption */
+#define DF_ENCRYPTION_DISABLE      0x01
+#define DF_SNIFF_MODE_ENABLE       0x80
+
+struct acx_feature_config {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 padding[3];
+	__le32 options;
+	__le32 data_flow_options;
+} __packed;
+
+struct acx_tx_power_cfg {
+	struct acx_header header;
+
+	u8 role_id;
+	s8 tx_power;
+	u8 padding[2];
+} __packed;
+
+struct acx_wake_up_condition {
+	struct acx_header header;
+
+	u8 wake_up_event;
+	u8 listen_interval;
+	u8 padding[2];
+} __packed;
+
+
+struct assoc_info_cfg
+{
+	struct acx_header header;
+
+	u8 role_id;
+	__le16 aid;
+	u8 wmm_enabled;
+	u8 nontransmitted;
+	u8 bssid_index;
+	u8 bssid_indicator;
+	u8 transmitter_bssid[ETH_ALEN];
+	u8 ht_supported;
+	u8 vht_supported;
+	u8 has_he;
+	//u8 padding[3]; incase total size of the struct variables isnt a multiple of 32 bit then add padding
+}__packed;
+
+
+struct acx_aid {
+	struct acx_header header;
+
+	/*
+	 * To be set when associated with an AP.
+	 *
+	 */
+	u8 role_id;
+	u8 reserved;
+	__le16 aid;
+} __packed;
+
+enum acx_preamble_type {
+	ACX_PREAMBLE_LONG = 0,
+	ACX_PREAMBLE_SHORT = 1
+};
+
+struct acx_preamble {
+	struct acx_header header;
+
+	/*
+	 * When set, the WiLink transmits the frames with a short preamble and
+	 * when cleared, the WiLink transmits the frames with a long preamble.
+	 */
+	u8 role_id;
+	u8 preamble;
+	u8 padding[2];
+} __packed;
+
+enum acx_ctsprotect_type {
+	CTSPROTECT_DISABLE = 0,
+	CTSPROTECT_ENABLE = 1
+};
+
+struct acx_ctsprotect {
+	struct acx_header header;
+	u8 role_id;
+	u8 ctsprotect;
+	u8 padding[2];
+} __packed;
+
+struct acx_rate_class {
+	__le32 enabled_rates;
+	u8 short_retry_limit;
+	u8 long_retry_limit;
+	u8 aflags;
+	u8 reserved;
+};
+
+struct ap_rates_class_cfg {
+
+	struct acx_header header;
+	u8 role_id;
+	__le32 basic_rates_set;
+	__le32 supported_rates;
+    u8 padding[3];
+}__packed;
+
+struct acx_rate_policy {
+	struct acx_header header;
+
+	__le32 rate_policy_idx;
+	struct acx_rate_class rate_policy;
+} __packed;
+
+struct tx_param_cfg {
+    struct acx_header header;
+
+    u8 role_id;
+    u8 ac;
+    u8 aifsn;
+    u8 cw_min;
+
+    __le16 cw_max;
+    __le16 tx_op_limit;
+
+    __le16 acm;
+
+    u8 ps_scheme;
+
+    u8 is_mu_edca;
+    u8 mu_edca_aifs;
+    u8 mu_edca_ecw_min_max;
+    u8 mu_edca_timer;
+
+    u8 reserved[1];
+
+} __packed;
+
+struct acx_ac_cfg {
+	struct acx_header header;
+	u8 role_id;
+	u8 ac;
+	u8 aifsn;
+	u8 cw_min;
+	__le16 cw_max;
+	__le16 tx_op_limit;
+	u8 ps_scheme;
+} __packed;
+
+struct acx_tid_config {
+	struct acx_header header;
+	u8 role_id;
+	u8 queue_id;
+	u8 channel_type;
+	u8 tsid;
+	u8 ps_scheme;
+	u8 ack_policy;
+	u8 padding[2];
+	__le32 apsd_conf[2];
+} __packed;
+
+struct acx_frag_threshold {
+	struct acx_header header;
+	__le16 frag_threshold;
+	u8 padding[2];
+} __packed;
+
+struct acx_tx_config_options {
+	struct acx_header header;
+	__le16 tx_compl_timeout;     /* msec */
+	__le16 tx_compl_threshold;   /* number of packets */
+} __packed;
+
+struct cc33xx_acx_config_memory {
+	struct acx_header header;
+
+	u8 rx_mem_block_num;
+	u8 tx_min_mem_block_num;
+	u8 num_stations;
+	u8 num_ssid_profiles;
+	__le32 total_tx_descriptors;
+	u8 dyn_mem_enable;
+	u8 tx_free_req;
+	u8 rx_free_req;
+	u8 tx_min;
+	u8 fwlog_blocks;
+	u8 padding[3];
+} __packed;
+
+struct cc33xx_acx_mem_map {
+	struct acx_header header;
+
+	/* Number of blocks FW allocated for TX packets */
+	__le32 num_tx_mem_blocks;
+
+	/* Number of blocks FW allocated for RX packets */
+	__le32 num_rx_mem_blocks;
+
+	/* Number of TX descriptor that allocated. */
+	__le32 num_tx_descriptor;
+
+	__le32 tx_result;
+
+} __packed;
+
+struct cc33xx_acx_fw_versions {
+	struct acx_header header;
+
+	__le16 major_version;
+	__le16 minor_version;
+	__le16 api_version;
+	__le16 build_version;
+
+	u8 phy_version[6];
+	u8 padding[2];
+} __packed;
+
+struct cc33xx_acx_rx_config_opt {
+	struct acx_header header;
+
+	__le16 mblk_threshold;
+	__le16 threshold;
+	__le16 timeout;
+	u8 queue_type;
+	u8 reserved;
+} __packed;
+
+
+struct cc33xx_acx_bet_enable {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 enable;
+	u8 max_consecutive;
+	u8 padding[1];
+} __packed;
+
+#define ACX_IPV4_VERSION 4
+#define ACX_IPV6_VERSION 6
+#define ACX_IPV4_ADDR_SIZE 4
+
+/* bitmap of enabled arp_filter features */
+#define ACX_ARP_FILTER_ARP_FILTERING	BIT(0)
+#define ACX_ARP_FILTER_AUTO_ARP		BIT(1)
+
+struct cc33xx_acx_arp_filter {
+	struct acx_header header;
+	u8 role_id;
+	u8 version;         /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */
+	u8 enable;          /* bitmap of enabled ARP filtering features */
+	u8 padding[1];
+	u8 address[16];     /* The configured device IP address - all ARP
+			       requests directed to this IP address will pass
+			       through. For IPv4, the first four bytes are
+			       used. */
+} __packed;
+
+struct cc33xx_acx_pm_config {
+	struct acx_header header;
+
+	__le32 host_clk_settling_time;
+	u8 host_fast_wakeup_support;
+	u8 padding[3];
+} __packed;
+
+/* TODO: maybe this needs to be moved somewhere else? */
+#define HOST_IF_CFG_RX_FIFO_ENABLE     BIT(0)
+#define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1)
+#define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3)
+#define HOST_IF_CFG_RX_PAD_TO_SDIO_BLK BIT(4)
+#define HOST_IF_CFG_ADD_RX_ALIGNMENT   BIT(6)
+
+enum {
+	CC33XX_ACX_TRIG_TYPE_LEVEL = 0,
+	CC33XX_ACX_TRIG_TYPE_EDGE,
+};
+
+enum {
+	CC33XX_ACX_TRIG_DIR_LOW = 0,
+	CC33XX_ACX_TRIG_DIR_HIGH,
+	CC33XX_ACX_TRIG_DIR_BIDIR,
+};
+
+enum {
+	CC33XX_ACX_TRIG_ENABLE = 1,
+	CC33XX_ACX_TRIG_DISABLE,
+};
+
+enum {
+	CC33XX_ACX_TRIG_METRIC_RSSI_BEACON = 0,
+	CC33XX_ACX_TRIG_METRIC_RSSI_DATA,
+	CC33XX_ACX_TRIG_METRIC_SNR_BEACON,
+	CC33XX_ACX_TRIG_METRIC_SNR_DATA,
+};
+
+enum {
+	CC33XX_ACX_TRIG_IDX_RSSI = 0,
+	CC33XX_ACX_TRIG_COUNT = 8,
+};
+
+struct cc33xx_acx_rssi_snr_trigger {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 metric;
+	u8 type;
+	u8 dir;
+	__le16 threshold;
+	__le16 pacing; /* 0 - 60000 ms */
+	u8 hysteresis;
+	u8 index;
+	u8 enable;
+	u8 padding[1];
+};
+
+struct cc33xx_acx_rssi_snr_avg_weights {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 padding[3];
+	u8 rssi_beacon;
+	u8 rssi_data;
+	u8 snr_beacon;
+	u8 snr_data;
+};
+
+
+/* special capability bit (not employed by the 802.11n spec) */
+#define CC33XX_HT_CAP_HT_OPERATION BIT(16)
+
+/*
+ * ACX_PEER_HT_CAP
+ * Configure HT capabilities - declare the capabilities of the peer
+ * we are connected to.
+ */
+struct cc33xx_acx_ht_capabilities {
+	struct acx_header header;
+
+	/* bitmask of capability bits supported by the peer */
+	__le32 ht_capabilites;
+
+	/* Indicates to which link these capabilities apply. */
+	u8 hlid;
+
+	/*
+	 * This the maximum A-MPDU length supported by the AP. The FW may not
+	 * exceed this length when sending A-MPDUs
+	 */
+	u8 ampdu_max_length;
+
+	/* This is the minimal spacing required when sending A-MPDUs to the AP*/
+	u8 ampdu_min_spacing;
+
+	u8 padding;
+} __packed;
+
+/*
+ * ACX_HT_BSS_OPERATION
+ * Configure HT capabilities - AP rules for behavior in the BSS.
+ */
+struct cc33xx_acx_ht_information {
+	struct acx_header header;
+
+	u8 role_id;
+
+	/* Values: 0 - RIFS not allowed, 1 - RIFS allowed */
+	u8 rifs_mode;
+
+	/* Values: 0 - 3 like in spec */
+	u8 ht_protection;
+
+	/* Values: 0 - GF protection not required, 1 - GF protection required */
+	u8 gf_protection;
+
+	/*
+	 * Values: 0 - Dual CTS protection not required,
+	 *         1 - Dual CTS Protection required
+	 * Note: When this value is set to 1 FW will protect all TXOP with RTS
+	 * frame and will not use CTS-to-self regardless of the value of the
+	 * ACX_CTS_PROTECTION information element
+	 */
+	u8 dual_cts_protection;
+
+	__le32 he_operation;
+	//u8 bss_color_info;
+
+	__le16 bss_basic_mcs_set;
+	u8 qos_info_more_data_ack_bit;
+
+} __packed;
+
+struct cc33xx_acx_ba_initiator_policy {
+	struct acx_header header;
+
+	/* Specifies role Id, Range 0-7, 0xFF means ANY role. */
+	u8 role_id;
+
+	/*
+	 * Per TID setting for allowing TX BA. Set a bit to 1 to allow
+	 * TX BA sessions for the corresponding TID.
+	 */
+	u8 tid_bitmap;
+
+	/* Windows size in number of packets */
+	u8 win_size;
+
+	u8 padding1[1];
+
+	/* As initiator inactivity timeout in time units(TU) of 1024us */
+	__le16 inactivity_timeout;
+
+	u8 padding[2];
+} __packed;
+
+struct cc33xx_acx_ba_receiver_setup {
+	struct acx_header header;
+
+	/* Specifies link id, range 0-31 */
+	u8 hlid;
+
+	u8 tid;
+
+	u8 enable;
+
+	/* Windows size in number of packets */
+	u8 win_size;
+
+	/* BA session starting sequence number.  RANGE 0-FFF */
+	__le16 ssn;
+
+	u8 padding[2];
+} __packed;
+
+struct calibration_header_fw {
+
+	/* chip id to assign each chip with its appropriate data */
+	u8 chip_id[ETH_ALEN];
+	/* payload can be of different length, chosen by user*/
+	__le16 length;
+
+} __packed;
+
+struct calibration_header {
+	/* preamble static pattern */
+	__le16 static_pattern;
+
+	struct calibration_header_fw cal_header_fw;
+
+} __packed;
+
+struct calibration_file_header {
+	u8	file_version;
+	u8	payload_struct_version;
+	__le16 entries_count;
+} __packed;
+
+
+
+
+
+
+
+struct cc33xx_acx_static_calibration_cfg {
+
+	struct acx_header header;
+	
+	bool valid_data;
+	u8 file_version;
+	u8 payload_struct_version;
+	u8 padding;
+	struct calibration_header_fw calibration_header;
+	u8  payload[0];
+	
+} __packed;
+
+struct cc33xx_acx_fw_tsf_information {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 padding1[3];
+	__le32 current_tsf_high;
+	__le32 current_tsf_low;
+	__le32 last_bttt_high;
+	__le32 last_tbtt_low;
+	u8 last_dtim_count;
+	u8 padding2[3];
+} __packed;
+
+struct cc33xx_acx_ps_rx_streaming {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 tid;
+	u8 enable;
+
+	/* interval between triggers (10-100 msec) */
+	u8 period;
+
+	/* timeout before first trigger (0-200 msec) */
+	u8 timeout;
+	u8 padding[3];
+} __packed;
+
+struct cc33xx_acx_ap_max_tx_retry {
+	struct acx_header header;
+
+	u8 role_id;
+	u8 padding_1;
+
+	/*
+	 * the number of frames transmission failures before
+	 * issuing the aging event.
+	 */
+	__le16 max_tx_retry;
+} __packed;
+
+struct cc33xx_acx_config_ps {
+	struct acx_header header;
+
+	u8 exit_retries;
+	u8 enter_retries;
+	u8 padding[2];
+	__le32 null_data_rate;
+} __packed;
+
+struct cc33xx_acx_inconnection_sta {
+	struct acx_header header;
+
+	u8 addr[ETH_ALEN];
+	u8 role_id;
+	u8 padding;
+} __packed;
+
+#define ACX_RATE_MGMT_ALL_PARAMS 0xff
+struct cc33xx_acx_set_rate_mgmt_params {
+	struct acx_header header;
+
+	u8 index; /* 0xff to configure all params */
+	u8 padding1;
+	__le16 rate_retry_score;
+	__le16 per_add;
+	__le16 per_th1;
+	__le16 per_th2;
+	__le16 max_per;
+	u8 inverse_curiosity_factor;
+	u8 tx_fail_low_th;
+	u8 tx_fail_high_th;
+	u8 per_alpha_shift;
+	u8 per_add_shift;
+	u8 per_beta1_shift;
+	u8 per_beta2_shift;
+	u8 rate_check_up;
+	u8 rate_check_down;
+	u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES];
+	u8 padding2[2];
+} __packed;
+
+struct cc33xx_acx_config_hangover {
+	struct acx_header header;
+
+	__le32 recover_time;
+	u8 hangover_period;
+	u8 dynamic_mode;
+	u8 early_termination_mode;
+	u8 max_period;
+	u8 min_period;
+	u8 increase_delta;
+	u8 decrease_delta;
+	u8 quiet_time;
+	u8 increase_time;
+	u8 window_size;
+	u8 padding[2];
+} __packed;
+
+
+struct acx_default_rx_filter {
+	struct acx_header header;
+	u8 enable;
+
+	/* action of type FILTER_XXX */
+	u8 default_action;
+
+	u8 pad[2];
+} __packed;
+
+
+struct acx_rx_filter_cfg {
+	struct acx_header header;
+
+	u8 enable;
+
+	/* 0 - WL1271_MAX_RX_FILTERS-1 */
+	u8 index;
+
+	u8 action;
+
+	u8 num_fields;
+	u8 fields[0];
+} __packed;
+
+struct acx_roaming_stats {
+	struct acx_header header;
+
+	u8	role_id;
+	u8	pad[3];
+	__le32	missed_beacons;
+	u8	snr_data;
+	u8	snr_bacon;
+	s8	rssi_data;
+	s8	rssi_beacon;
+} __packed;
+
+typedef enum {
+	CTS_PROTECTION_CFG = 0,
+	TX_PARAMS_CFG = 1,
+	ASSOC_INFO_CFG = 2,
+	PEER_CAP_CFG = 3,
+	BSS_OPERATION_CFG = 4,
+	SLOT_CFG = 5,
+	PREAMBLE_TYPE_CFG = 6,
+	DOT11_GROUP_ADDRESS_TBL = 7,
+	BA_SESSION_RX_SETUP_CFG = 8,
+	ACX_SLEEP_AUTH = 9,
+	STATIC_CALIBRATION_CFG = 10,
+	AP_RATES_CFG = 11,
+	WAKE_UP_CONDITIONS_CFG = 12,
+	SET_ANTENNA_SELECT_CFG = 13,
+	TX_POWER_CFG = 14,
+	LAST_CFG_VALUE,
+	MAX_DOT11_CFG = LAST_CFG_VALUE,
+
+	MAX_CFG = 0xFFFF /*force enumeration to 16bits*/
+} Cfg_e;
+
+typedef enum {
+	UPLINK_MULTI_USER_CFG,
+	UPLINK_MULTI_USER_DATA_CFG,
+	OPERATION_MODE_CTRL_CFG,
+	UPLINK_POWER_HEADER_CFG,
+	MCS_FIXED_RATE_CFG,
+	GI_LTF_CFG,
+	TRANSMIT_OMI_CFG,
+	TB_ONLY_CFG,
+	BA_SESSION_CFG,
+	FORCE_PS_CFG,
+	RATE_OVERRRIDE_CFG,
+	BLS_CFG,
+	BLE_ENABLE,
+	SET_TSF,
+	RTS_TH_CFG,
+	LINK_ADAPT_CFG,
+	CALIB_BITMAP_CFG,
+    PWR_PARTIAL_MODES_CFG,
+	TRIGGER_FW_ASSERT,
+	BURST_MODE_CFG,
+
+	LAST_DEBUG_VALUE,
+
+	MAX_DEBUG = 0xFFFF /*force enumeration to 16bits*/
+
+} cmdDebug_e;
+
+typedef enum {
+	BEACON_RSSI_INTR,
+	LAST_DEBUG_READ_VALUE,
+
+	MAX_DEBUG_READ = 0xFFFF /*force enumeration to 16bits*/
+} cmdDebugRead_e;
+
+typedef enum {
+	MEM_MAP_INTR = 0,
+	GET_FW_VERSIONS_INTR = 1,
+	RSSI_INTR = 2,
+	GET_ANTENNA_SELECT_INTR = 3,
+	LAST_IE_VALUE,
+	MAX_DOT11_IE = LAST_IE_VALUE,
+
+	MAX_IE = 0xFFFF /*force enumeration to 16bits*/
+} Interrogate_e;
+
+//TODO: RazB - Need to remove this enum when we finish over all the commands
+enum {    
+	ACX_MEM_CFG = LAST_CFG_VALUE	 ,
+	ACX_SLOT                         ,
+	ACX_AC_CFG                       ,
+	ACX_MEM_MAP                      ,
+	ACX_AID                          ,
+	ACX_MEDIUM_USAGE                 ,
+	ACX_STATISTICS                   ,
+	ACX_PWR_CONSUMPTION_STATISTICS   ,
+	ACX_TID_CFG                      ,
+	ACX_PS_RX_STREAMING              ,
+	ACX_BEACON_FILTER_OPT            ,
+	ACX_NOISE_HIST                   ,
+	ACX_HDK_VERSION                  ,
+	ACX_PD_THRESHOLD                 ,
+	ACX_TX_CONFIG_OPT                ,
+	ACX_CCA_THRESHOLD                ,
+	ACX_EVENT_MBOX_MASK              ,
+	ACX_CONN_MONIT_PARAMS            ,
+	ACX_DISABLE_BROADCASTS           ,
+	ACX_BCN_DTIM_OPTIONS             ,
+	ACX_SG_ENABLE                    ,
+	ACX_SG_CFG                       ,
+	ACX_FM_COEX_CFG                  ,
+	ACX_BEACON_FILTER_TABLE          ,
+	ACX_ARP_IP_FILTER                ,
+	ACX_ROAMING_STATISTICS_TBL       ,
+	ACX_RATE_POLICY                  ,
+	ACX_CTS_PROTECTION               ,
+	ACX_PREAMBLE_TYPE                ,
+	ACX_ERROR_CNT                    ,
+	ACX_IBSS_FILTER                  ,
+	ACX_SERVICE_PERIOD_TIMEOUT       ,
+	ACX_TSF_INFO                     ,
+	ACX_CONFIG_PS_WMM                ,
+	ACX_ENABLE_RX_DATA_FILTER        ,
+	ACX_SET_RX_DATA_FILTER           ,
+	ACX_GET_DATA_FILTER_STATISTICS   ,
+	ACX_RX_CONFIG_OPT                ,
+	ACX_FRAG_CFG                     ,
+	ACX_BET_ENABLE                   ,
+	ACX_RSSI_SNR_TRIGGER             ,
+	ACX_RSSI_SNR_WEIGHTS             ,
+	ACX_KEEP_ALIVE_MODE              ,
+	ACX_BA_SESSION_INIT_POLICY       ,
+	ACX_BA_SESSION_RX_SETUP          ,
+	ACX_PEER_HT_CAP                  ,
+	ACX_HT_BSS_OPERATION             ,
+	ACX_COEX_ACTIVITY                ,
+	ACX_BURST_MODE                   ,
+	ACX_SET_RATE_MGMT_PARAMS         ,
+	ACX_GET_RATE_MGMT_PARAMS         ,
+	ACX_SET_RATE_ADAPT_PARAMS        ,
+	ACX_SET_DCO_ITRIM_PARAMS         ,
+	ACX_GEN_FW_CMD                   ,
+	ACX_HOST_IF_CFG_BITMAP           ,
+	ACX_MAX_TX_FAILURE               ,
+	ACX_UPDATE_INCONNECTION_STA_LIST ,
+	DOT11_RX_MSDU_LIFE_TIME          ,
+	DOT11_CUR_TX_PWR                 ,
+	DOT11_RTS_THRESHOLD              ,
+	ACX_PM_CONFIG                    ,
+	ACX_CONFIG_PS                    ,
+	ACX_CONFIG_HANGOVER              ,
+	ACX_FEATURE_CFG                  ,
+	ACX_PROTECTION_CFG               ,
+};
+
+
+
+enum {
+	ACX_NS_IPV6_FILTER		 = 0x0050,
+	ACX_PEER_HT_OPERATION_MODE_CFG	 = 0x0051,
+	ACX_CSUM_CONFIG			 = 0x0052,
+	ACX_SIM_CONFIG			 = 0x0053,
+	ACX_CLEAR_STATISTICS		 = 0x0054,
+	ACX_AUTO_RX_STREAMING		 = 0x0055,
+	ACX_PEER_CAP			 = 0x0056,
+	ACX_INTERRUPT_NOTIFY		 = 0x0057,
+	ACX_RX_BA_FILTER		 = 0x0058,
+	ACX_AP_SLEEP_CFG                 = 0x0059,
+	ACX_DYNAMIC_TRACES_CFG		 = 0x005A,
+	ACX_TIME_SYNC_CFG		 = 0x005B,
+};
+
+/*
+ * ACX_DYNAMIC_TRACES_CFG
+ * configure the FW dynamic traces
+ */
+struct acx_dynamic_fw_traces_cfg {
+	struct acx_header header;
+	__le32 dynamic_fw_traces;
+} __packed;
+
+struct cc33xx_acx_clear_statistics {
+	struct acx_header header;
+};
+
+
+
+/* numbers of bits the length field takes (add 1 for the actual number) */
+#define CC33XX_HOST_IF_LEN_SIZE_FIELD 15
+
+#define CC33XX_ACX_EVENTS_VECTOR	(CC33XX1_ACX_INTR_WATCHDOG	| \
+					 CC33XX_ACX_INTR_INIT_COMPLETE	| \
+					 CC33XX_ACX_INTR_EVENT_A	| \
+					 CC33XX_ACX_INTR_EVENT_B	| \
+					 CC33XX_ACX_INTR_CMD_COMPLETE	| \
+					 CC33XX_ACX_INTR_HW_AVAILABLE	| \
+					 CC33XX_ACX_INTR_DATA		| \
+					 CC33XX_ACX_SW_INTR_WATCHDOG)
+
+#define CC33XX_INTR_MASK		(CC33XX_ACX_INTR_WATCHDOG	| \
+					 CC33XX_ACX_INTR_EVENT_A	| \
+					 CC33XX_ACX_INTR_EVENT_B	| \
+					 CC33XX_ACX_INTR_HW_AVAILABLE	| \
+					 CC33XX_ACX_INTR_DATA		| \
+					 CC33XX_ACX_SW_INTR_WATCHDOG)
+
+struct cc33xx_acx_host_config_bitmap {
+	struct acx_header header;
+
+	__le32 host_cfg_bitmap;
+
+	__le32 host_sdio_block_size;
+
+	/* extra mem blocks per frame in TX. */
+	__le32 extra_mem_blocks;
+
+	/*
+	 * number of bits of the length field in the first TX word
+	 * (up to 15 - for using the entire 16 bits).
+	 */
+	__le32 length_field_size;
+
+} __packed;
+
+enum {
+	CHECKSUM_OFFLOAD_DISABLED = 0,
+	CHECKSUM_OFFLOAD_ENABLED  = 1,
+	CHECKSUM_OFFLOAD_FAKE_RX  = 2,
+	CHECKSUM_OFFLOAD_INVALID  = 0xFF
+};
+
+struct cc33xx_acx_checksum_state {
+	struct acx_header header;
+
+	 /* enum acx_checksum_state */
+	u8 checksum_state;
+	u8 pad[3];
+} __packed;
+
+
+struct cc33xx_acx_error_stats {
+	__le32 error_frame_non_ctrl;
+	__le32 error_frame_ctrl;
+	__le32 error_frame_during_protection;
+	__le32 null_frame_tx_start;
+	__le32 null_frame_cts_start;
+	__le32 bar_retry;
+	__le32 num_frame_cts_nul_flid;
+	__le32 tx_abort_failure;
+	__le32 tx_resume_failure;
+	__le32 rx_cmplt_db_overflow_cnt;
+	__le32 elp_while_rx_exch;
+	__le32 elp_while_tx_exch;
+	__le32 elp_while_tx;
+	__le32 elp_while_nvic_pending;
+	__le32 rx_excessive_frame_len;
+	__le32 burst_mismatch;
+	__le32 tbc_exch_mismatch;
+} __packed;
+
+#define NUM_OF_RATES_INDEXES 30
+struct cc33xx_acx_tx_stats {
+	__le32 tx_prepared_descs;
+	__le32 tx_cmplt;
+	__le32 tx_template_prepared;
+	__le32 tx_data_prepared;
+	__le32 tx_template_programmed;
+	__le32 tx_data_programmed;
+	__le32 tx_burst_programmed;
+	__le32 tx_starts;
+	__le32 tx_stop;
+	__le32 tx_start_templates;
+	__le32 tx_start_int_templates;
+	__le32 tx_start_fw_gen;
+	__le32 tx_start_data;
+	__le32 tx_start_null_frame;
+	__le32 tx_exch;
+	__le32 tx_retry_template;
+	__le32 tx_retry_data;
+	__le32 tx_retry_per_rate[NUM_OF_RATES_INDEXES];
+	__le32 tx_exch_pending;
+	__le32 tx_exch_expiry;
+	__le32 tx_done_template;
+	__le32 tx_done_data;
+	__le32 tx_done_int_template;
+	__le32 tx_cfe1;
+	__le32 tx_cfe2;
+	__le32 frag_called;
+	__le32 frag_mpdu_alloc_failed;
+	__le32 frag_init_called;
+	__le32 frag_in_process_called;
+	__le32 frag_tkip_called;
+	__le32 frag_key_not_found;
+	__le32 frag_need_fragmentation;
+	__le32 frag_bad_mblk_num;
+	__le32 frag_failed;
+	__le32 frag_cache_hit;
+	__le32 frag_cache_miss;
+} __packed;
+
+struct cc33xx_acx_rx_stats {
+	__le32 rx_beacon_early_term;
+	__le32 rx_out_of_mpdu_nodes;
+	__le32 rx_hdr_overflow;
+	__le32 rx_dropped_frame;
+	__le32 rx_done_stage;
+	__le32 rx_done;
+	__le32 rx_defrag;
+	__le32 rx_defrag_end;
+	__le32 rx_cmplt;
+	__le32 rx_pre_complt;
+	__le32 rx_cmplt_task;
+	__le32 rx_phy_hdr;
+	__le32 rx_timeout;
+	__le32 rx_rts_timeout;
+	__le32 rx_timeout_wa;
+	__le32 defrag_called;
+	__le32 defrag_init_called;
+	__le32 defrag_in_process_called;
+	__le32 defrag_tkip_called;
+	__le32 defrag_need_defrag;
+	__le32 defrag_decrypt_failed;
+	__le32 decrypt_key_not_found;
+	__le32 defrag_need_decrypt;
+	__le32 rx_tkip_replays;
+	__le32 rx_xfr;
+} __packed;
+
+struct cc33xx_acx_isr_stats {
+	__le32 irqs;
+} __packed;
+
+#define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10
+
+struct cc33xx_acx_pwr_stats {
+	__le32 missing_bcns_cnt;
+	__le32 rcvd_bcns_cnt;
+	__le32 connection_out_of_sync;
+	__le32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD];
+	__le32 rcvd_awake_bcns_cnt;
+	__le32 sleep_time_count;
+	__le32 sleep_time_avg;
+	__le32 sleep_cycle_avg;
+	__le32 sleep_percent;
+	__le32 ap_sleep_active_conf;
+	__le32 ap_sleep_user_conf;
+	__le32 ap_sleep_counter;
+} __packed;
+
+struct cc33xx_acx_rx_filter_stats {
+	__le32 beacon_filter;
+	__le32 arp_filter;
+	__le32 mc_filter;
+	__le32 dup_filter;
+	__le32 data_filter;
+	__le32 ibss_filter;
+	__le32 protection_filter;
+	__le32 accum_arp_pend_requests;
+	__le32 max_arp_queue_dep;
+} __packed;
+
+struct cc33xx_acx_rx_rate_stats {
+	__le32 rx_frames_per_rates[50];
+} __packed;
+
+#define AGGR_STATS_TX_AGG	16
+#define AGGR_STATS_RX_SIZE_LEN	16
+
+struct cc33xx_acx_aggr_stats {
+	__le32 tx_agg_rate[AGGR_STATS_TX_AGG];
+	__le32 tx_agg_len[AGGR_STATS_TX_AGG];
+	__le32 rx_size[AGGR_STATS_RX_SIZE_LEN];
+} __packed;
+
+#define PIPE_STATS_HW_FIFO	11
+
+struct cc33xx_acx_pipeline_stats {
+	__le32 hs_tx_stat_fifo_int;
+	__le32 hs_rx_stat_fifo_int;
+	__le32 enc_tx_stat_fifo_int;
+	__le32 enc_rx_stat_fifo_int;
+	__le32 rx_complete_stat_fifo_int;
+	__le32 pre_proc_swi;
+	__le32 post_proc_swi;
+	__le32 sec_frag_swi;
+	__le32 pre_to_defrag_swi;
+	__le32 defrag_to_rx_xfer_swi;
+	__le32 dec_packet_in;
+	__le32 dec_packet_in_fifo_full;
+	__le32 dec_packet_out;
+	__le16 pipeline_fifo_full[PIPE_STATS_HW_FIFO];
+	__le16 padding;
+} __packed;
+
+#define DIVERSITY_STATS_NUM_OF_ANT	2
+
+struct cc33xx_acx_diversity_stats {
+	__le32 num_of_packets_per_ant[DIVERSITY_STATS_NUM_OF_ANT];
+	__le32 total_num_of_toggles;
+} __packed;
+
+struct cc33xx_acx_thermal_stats {
+	__le16 irq_thr_low;
+	__le16 irq_thr_high;
+	__le16 tx_stop;
+	__le16 tx_resume;
+	__le16 false_irq;
+	__le16 adc_source_unexpected;
+} __packed;
+
+#define CC33XX_NUM_OF_CALIBRATIONS_ERRORS 18
+struct cc33xx_acx_calib_failure_stats {
+	__le16 fail_count[CC33XX_NUM_OF_CALIBRATIONS_ERRORS];
+	__le32 calib_count;
+} __packed;
+
+struct cc33xx_roaming_stats {
+	s32 rssi_level;
+} __packed;
+
+struct cc33xx_dfs_stats {
+	__le32 num_of_radar_detections;
+} __packed;
+
+struct cc33xx_acx_statistics {
+	struct acx_header header;
+
+	struct cc33xx_acx_error_stats		error;
+	struct cc33xx_acx_tx_stats		tx;
+	struct cc33xx_acx_rx_stats		rx;
+	struct cc33xx_acx_isr_stats		isr;
+	struct cc33xx_acx_pwr_stats		pwr;
+	struct cc33xx_acx_rx_filter_stats	rx_filter;
+	struct cc33xx_acx_rx_rate_stats		rx_rate;
+	struct cc33xx_acx_aggr_stats		aggr_size;
+	struct cc33xx_acx_pipeline_stats	pipeline;
+	struct cc33xx_acx_diversity_stats	diversity;
+	struct cc33xx_acx_thermal_stats		thermal;
+	struct cc33xx_acx_calib_failure_stats	calib;
+	struct cc33xx_roaming_stats		roaming;
+	struct cc33xx_dfs_stats			dfs;
+} __packed;
+
+enum wlcore_bandwidth {
+	WLCORE_BANDWIDTH_20MHZ,
+	WLCORE_BANDWIDTH_40MHZ,
+};
+
+struct wlcore_peer_ht_operation_mode {
+	struct acx_header header;
+
+	u8 hlid;
+	u8 bandwidth; /* enum wlcore_bandwidth */
+	u8 padding[2];
+};
+
+
+/*
+ * ACX_PEER_CAP
+ * this struct is very similar to cc33xx_acx_ht_capabilities, with the
+ * addition of supported rates
+ */
+#define NOMINAL_PACKET_PADDING (0xC0)
+struct wlcore_acx_peer_cap {
+    struct acx_header header;
+
+    u8 role_id;
+
+    /* rates supported by the remote peer */
+    __le32 supported_rates;
+
+    /* bitmask of capability bits supported by the peer */
+    __le32 ht_capabilites;
+    /*
+     * This the maximum A-MPDU length supported by the AP. The FW may not
+     * exceed this length when sending A-MPDUs
+     */
+    u8 ampdu_max_length;
+
+    /* This is the minimal spacing required when sending A-MPDUs to the AP*/
+    u8 ampdu_min_spacing;
+
+    /* HE capabilities */
+    u8 mac_cap_info[8];
+
+    /* Nominal packet padding value, used for determining the packet extension duration */
+    u8 nominal_packet_padding;
+
+    /* HE peer support */
+    bool has_he;
+    
+    /* */
+	u8 dcm_max_constelation;
+
+	/* */
+	u8 er_upper_supported;
+
+    u8 paddin[1];
+} __packed;
+
+/*
+ * ACX_INTERRUPT_NOTIFY
+ * enable/disable fast-link/PSM notification from FW
+ */
+struct cc33xx_acx_interrupt_notify {
+	struct acx_header header;
+	__le32 enable;
+};
+
+/*
+ * ACX_RX_BA_FILTER
+ * enable/disable RX BA filtering in FW
+ */
+struct cc33xx_acx_rx_ba_filter {
+	struct acx_header header;
+	__le32 enable;
+};
+
+struct acx_ap_sleep_cfg {
+	struct acx_header header;
+	/* Duty Cycle (20-80% of staying Awake) for IDLE AP
+	 * (0: disable)
+	 */
+	u8 idle_duty_cycle;
+	/* Duty Cycle (20-80% of staying Awake) for Connected AP
+	 * (0: disable)
+	 */
+	u8 connected_duty_cycle;
+	/* Maximum stations that are allowed to be connected to AP
+	 *  (255: no limit)
+	 */
+	u8 max_stations_thresh;
+	/* Timeout till enabling the Sleep Mechanism after data stops
+	 * [unit: 100 msec]
+	 */
+	u8 idle_conn_thresh;
+} __packed;
+
+struct acx_antenna_select {
+	struct acx_header header;
+
+	u8 selection;
+	u8 padding[3];
+} __packed;
+
+struct debug_set_tsf {
+	struct debug_header header;
+
+	__le64 tsf_val;
+} __packed;
+
+struct debug_burst_mode_cfg {
+	struct debug_header header;
+
+    u8 burst_disable;
+	u8 padding[3];
+} __packed;
+
+
+int cc33xx_acx_wake_up_conditions(struct cc33xx *wl,
+				  struct cc33xx_vif *wlvif,
+				  u8 wake_up_event, u8 listen_interval);
+int cc33xx_acx_sleep_auth(struct cc33xx *wl, u8 sleep_auth);
+int cc33xx_ble_enable(struct cc33xx *wl, u8 ble_enable);
+int cc33xx_acx_tx_power(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			int power);
+int cc33xx_acx_feature_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_acx_mem_map(struct cc33xx *wl,
+		       struct acx_header *mem_map, size_t len);
+int cc33xx_acx_rx_msdu_life_time(struct cc33xx *wl);
+int cc33xx_acx_slot(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		    enum acx_slot_type slot_time);
+int cc33xx_acx_group_address_tbl(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				 bool enable, void *mc_list, u32 mc_list_len);
+int cc33xx_acx_service_period_timeout(struct cc33xx *wl,
+				      struct cc33xx_vif *wlvif);
+int cc33xx_acx_rts_threshold(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			     u32 rts_threshold);
+int cc33xx_acx_dco_itrim_params(struct cc33xx *wl);
+int cc33xx_acx_beacon_filter_opt(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				 bool enable_filter);
+int cc33xx_acx_beacon_filter_table(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif);
+int cc33xx_acx_conn_monit_params(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				 bool enable);
+int cc33xx_acx_cca_threshold(struct cc33xx *wl);
+int cc33xx_acx_bcn_dtim_options(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_assoc_info_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif, struct ieee80211_sta *sta,u16 aid);
+int cc33xx_acx_aid(struct cc33xx *wl, struct cc33xx_vif *wlvif, u16 aid);
+int cc33xx_acx_event_mbox_mask(struct cc33xx *wl, u32 event_mask);
+int cc33xx_acx_set_preamble(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    enum acx_preamble_type preamble);
+int cc33xx_acx_cts_protect(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   enum acx_ctsprotect_type ctsprotect);
+int cc33xx_acx_statistics(struct cc33xx *wl, void *stats);
+int cc33xx_tx_param_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+              u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop, bool acm,
+              u8 ps_scheme, u8 is_mu_edca, u8 mu_edca_aifs, u8 mu_edca_ecw_min_max,
+              u8 mu_edca_timer);
+int cc33xx_update_ap_rates(struct cc33xx *wl,u8 role_id,u32 basic_rates_set,u32 supported_rates);
+int cc33xx_acx_ac_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		      u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop);
+int cc33xx_acx_tid_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       u8 queue_id, u8 channel_type,
+		       u8 tsid, u8 ps_scheme, u8 ack_policy,
+		       u32 apsd_conf0, u32 apsd_conf1);
+int cc33xx_acx_frag_threshold(struct cc33xx *wl, u32 frag_threshold);
+int cc33xx_acx_tx_config_options(struct cc33xx *wl);
+int cc33xx_acx_mem_cfg(struct cc33xx *wl);
+int cc33xx_acx_init_mem_config(struct cc33xx *wl);
+int cc33xx_acx_init_get_fw_versions(struct cc33xx *wl);
+int cc33xx_acx_init_rx_interrupt(struct cc33xx *wl);
+int cc33xx_acx_smart_reflex(struct cc33xx *wl);
+int cc33xx_acx_bet_enable(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  bool enable);
+int cc33x_acx_arp_ip_filter(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			     u8 enable, __be32 address);
+int cc33xx_acx_pm_config(struct cc33xx *wl);
+int cc33xx_acx_rssi_snr_trigger(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				bool enable, s16 thold, u8 hyst);
+int cc33xx_acx_rssi_snr_avg_weights(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif);
+int cc33xx_acx_set_ht_capabilities(struct cc33xx *wl,
+				    struct ieee80211_sta_ht_cap *ht_cap,
+				    bool allow_ht_operation, u8 hlid);
+int cc33xx_acx_set_ht_information(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif,
+				   u16 ht_operation_mode,
+				   u32 he_oper_params, u16 he_oper_nss_set);
+int cc33xx_acx_set_ba_initiator_policy(struct cc33xx *wl,
+				       struct cc33xx_vif *wlvif);
+int cc33xx_acx_set_ba_receiver_session(struct cc33xx *wl, u8 tid_index,
+				       u16 ssn, bool enable, u8 peer_hlid,
+				       u8 win_size);
+int cc33xx_acx_static_calibration_configure(struct cc33xx *wl,
+                        		    struct calibration_file_header *file_header,
+					    u8 *calibration_entry_ptr,
+					    bool valid_data);						
+int cc33xx_acx_tsf_info(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			u64 *mactime);
+int cc33xx_acx_ps_rx_streaming(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			       bool enable);
+int cc33xx_acx_ap_max_tx_retry(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_acx_config_ps(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_acx_set_inconnection_sta(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif, u8 *addr);
+int cc33xx_acx_set_rate_mgmt_params(struct cc33xx *wl);
+int cc33xx_acx_config_hangover(struct cc33xx *wl);
+int wlcore_acx_average_rssi(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    s8 *avg_rssi);
+
+int cc33xx_acx_default_rx_filter_enable(struct cc33xx *wl, bool enable,
+					enum rx_filter_action action);
+int cc33xx_acx_set_rx_filter(struct cc33xx *wl, u8 index, bool enable,
+			     struct cc33xx_rx_filter *filter);
+
+int cc33xx_acx_dynamic_fw_traces(struct cc33xx *wl);
+int cc33xx_acx_clear_statistics(struct cc33xx *wl);
+
+int cc33xx_acx_host_if_cfg_bitmap(struct cc33xx *wl, u32 host_cfg_bitmap,
+				  u32 sdio_blk_size, u32 extra_mem_blks,
+				  u32 len_field_size);
+int cc33xx_acx_set_checksum_state(struct cc33xx *wl);
+int cc33xx_acx_peer_ht_operation_mode(struct cc33xx *wl, u8 hlid, bool wide);
+int cc33xx_acx_set_peer_cap(struct cc33xx *wl,
+			    struct ieee80211_sta_ht_cap *ht_cap,
+			    struct ieee80211_sta_he_cap *he_cap,
+			    struct cc33xx_vif *wlvif,
+			    bool allow_ht_operation,
+			    u32 rate_set, u8 hlid);
+int cc33xx_acx_interrupt_notify_config(struct cc33xx *wl, bool action);
+int cc33xx_acx_rx_ba_filter(struct cc33xx *wl, bool action);
+int cc33xx_acx_ap_sleep(struct cc33xx *wl);
+int cc33xx_acx_set_antenna_select(struct cc33xx *wl, u8 selection);
+int cc33xx_acx_get_fw_versions(struct cc33xx *wl, struct cc33xx_acx_fw_versions *get_fw_versions, size_t len);
+int cc33xx_acx_set_tsf(struct cc33xx *wl, u64 tsf_val);
+int cc33xx_acx_trigger_fw_assert(struct cc33xx *wl);
+int cc33xx_acx_burst_mode_cfg(struct cc33xx *wl, u8 burst_disable);
+
+
+#endif /* __CC33XX_ACX_H__ */
diff --git drivers/net/wireless/ti/cc33xx/boot.c drivers/net/wireless/ti/cc33xx/boot.c
new file mode 100644
index 000000000000..1ccf2d702fd0
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/boot.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/firmware.h>
+
+#include "debug.h"
+#include "acx.h"
+#include "boot.h"
+#include "io.h"
+#include "event.h"
+#include "rx.h"
+#include "tx.h"
+#include "init.h"
+
+
+#define CC33XX_BOOT_TIMEOUT 2000
+
+struct hwinfo_bitmap
+{
+    u32 disable_5g                  : 1u;
+    u32 disable_6g                  : 1u;
+    u32 disable_ble                 : 1u;
+    u32 disable_ble_m0plus          : 1u;
+    u32 disable_m33                 : 1u;
+    u64 udi                         : 64u;
+    u32 pg_version                  : 4u;
+    u32 metal_version               : 4u;
+    u32 boot_rom_version            : 4u;
+    u32 m3_rom_version              : 4u;
+    u32 fuse_rom_structure_version  : 4u;
+    u64 mac_address                 : 48u;
+    u32 device_part_number          : 6u;
+    u32 package_type                : 4u;
+    u32 fw_rollback_protection_1    : 32u;
+    u32 fw_rollback_protection_2    : 32u;
+    u32 fw_rollback_protection_3    : 32u;
+    u32 reserved                    : 13u;
+} /* Aligned with boot code, must not be __packed */;
+
+
+union hw_info
+{
+    struct hwinfo_bitmap	bitmap;
+    u8 				bytes[sizeof (struct hwinfo_bitmap)] ;
+};
+
+
+int wlcore_boot_upload_nvs(struct cc33xx *wl); 
+
+int cc33xx_hw_init(struct cc33xx *wl);
+
+/* Called from threaded irq context */
+void cc33xx_handle_boot_irqs(struct cc33xx *wl, u32 pending_interrupts)
+{
+	if (WARN_ON(!wl->fw_download))
+		return; 
+
+	cc33xx_debug(DEBUG_BOOT, "BOOT IRQs: 0x%x", pending_interrupts);
+
+	atomic_or(pending_interrupts, &wl->fw_download->pending_irqs);
+	complete(&wl->fw_download->wait_on_irq);
+}
+
+int wlcore_boot_run_firmware(struct cc33xx *wl)
+{
+	cc33xx_debug(DEBUG_CC33xx, "FW reset not implemented");
+
+
+	cc33xx_debug(DEBUG_CC33xx, "Skipping wait for init complete");
+
+	return 0;
+}
+
+static u8 * fetch_container(struct cc33xx *wl, const char* container_name, 
+						size_t *container_len)
+{
+	u8 *container_data=NULL;
+	const struct firmware *container;	
+	int ret;
+	
+	ret = request_firmware(&container, container_name, wl->dev);
+
+	if (ret < 0) {
+		cc33xx_error("could not get container %s: (%d)", 
+				container_name, ret);
+		return NULL;
+	}
+
+	if (container->size % 4) {
+		cc33xx_error("container size is not word-aligned: %zu",
+			     container->size);
+		goto out;		
+	}
+
+	
+	*container_len = container->size;
+	container_data = vmalloc(container->size);
+
+	if (!container_data) {
+		cc33xx_error("could not allocate memory for the container");
+		goto out;
+	}
+
+	memcpy(container_data, container->data, container->size);	
+
+out:
+	release_firmware(container);
+	return container_data;
+}
+
+static int cc33xx_set_power_on(struct cc33xx *wl)
+{
+	int ret;
+
+	msleep(CC33XX_PRE_POWER_ON_SLEEP);
+	ret = cc33xx_power_on(wl);
+	if (ret < 0)
+		goto out;
+	msleep(CC33XX_POWER_ON_SLEEP);
+	cc33xx_io_reset(wl);
+	cc33xx_io_init(wl);
+
+out:
+	return ret;
+}
+
+
+static int cc33xx_chip_wakeup(struct cc33xx *wl)
+{
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_BOOT, "Chip wakeup");
+
+	ret = cc33xx_set_power_on(wl);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * For wl127x based devices we could use the default block
+	 * size (512 bytes), but due to a bug in the sdio driver, we
+	 * need to set it explicitly after the chip is powered on.  To
+	 * simplify the code and since the performance impact is
+	 * negligible, we use the same block size for all different
+	 * chip types.
+	 *
+	 * Check if the bus supports blocksize alignment and, if it
+	 * doesn't, make sure we don't have the quirk.
+	 */
+	if (!cc33xx_set_block_size(wl))
+		wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
+
+	/* TODO: make sure the lower driver has set things up correctly */
+
+out:
+	return ret;
+}
+
+static int wait_for_boot_irq(struct cc33xx *wl, 
+				u32 boot_irq_mask, unsigned long timeout)
+{
+	int ret; 
+	u32 pending_irqs;
+	struct cc33xx_fw_download *fw_download;
+
+	fw_download = wl->fw_download;
+
+	ret = wait_for_completion_interruptible_timeout(
+			&fw_download->wait_on_irq, msecs_to_jiffies(timeout));
+
+	// Fetch pending IRQs while clearing them in fw_download
+	pending_irqs = atomic_fetch_and(0, &fw_download->pending_irqs);
+	pending_irqs &= ~HINT_COMMAND_COMPLETE;
+
+	reinit_completion(&fw_download->wait_on_irq);
+
+	if (ret == 0){
+		cc33xx_error("boot IRQ timeout");
+		return -1;
+	}
+	else if (ret < 0){
+		cc33xx_error("boot IRQ completion error %d", ret);		
+		return -2;	
+	}
+
+	if (boot_irq_mask != pending_irqs){
+		cc33xx_error("Unexpected IRQ received @ boot: 0x%x", 
+		pending_irqs);		
+		return -3;
+	}
+
+	return 0;
+}
+
+static int download_container(struct cc33xx *wl, u8 *container, size_t len)
+{
+	int ret;
+	u8 *current_transfer;
+	size_t current_transfer_size;
+	u8 *const container_end = container + len;	
+	size_t max_transfer_size = wl->fw_download->max_transfer_size;
+	bool is_last_transfer;
+
+	current_transfer = container;
+
+	while (current_transfer < container_end){
+		current_transfer_size = container_end - current_transfer;
+		current_transfer_size = 
+			min(current_transfer_size, max_transfer_size);
+
+		is_last_transfer = (current_transfer + current_transfer_size >= container_end);
+
+		ret = cmd_download_container_chunk(
+			wl, current_transfer, current_transfer_size, is_last_transfer);
+
+		current_transfer += current_transfer_size;
+
+		if (ret < 0) {
+			cc33xx_error("Chunk transfer failed");
+			goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int container_download_and_wait(
+	struct cc33xx *wl, 
+	const char* container_name, 
+	const u32 irq_wait_mask)
+{
+	int ret=-1;
+	u8 *container_data;
+	size_t container_len;
+
+	cc33xx_debug(DEBUG_BOOT, 
+		"Downloading %s to device", container_name);
+
+	container_data = fetch_container(wl, container_name, &container_len);
+	if (!container_data)
+		return ret;
+
+	ret = download_container(wl, container_data, container_len);
+	if (ret < 0){
+		cc33xx_error("Transfer error while downloading %s", 
+				container_name);
+		goto out;
+	}
+
+	ret = wait_for_boot_irq(wl, irq_wait_mask, CC33XX_BOOT_TIMEOUT);
+
+	if (ret < 0){
+		cc33xx_error("%s boot signal timeout", container_name);
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_BOOT, "%s loaded successfully", container_name);
+	ret=0;
+
+out:
+	vfree(container_data);
+	return ret;
+}
+
+static int fw_download_alloc(struct cc33xx *wl)
+{
+	if (WARN_ON(wl->fw_download != NULL))
+		return -EFAULT;
+
+	wl->fw_download = kzalloc(sizeof(*wl->fw_download), GFP_KERNEL);
+	if (!wl->fw_download)
+		return -ENOMEM;
+		
+	init_completion(&wl->fw_download->wait_on_irq);
+
+	return 0;
+}
+
+static void fw_download_free(struct cc33xx *wl)
+{
+	if (WARN_ON(wl->fw_download == NULL))
+		return;
+
+	kfree(wl->fw_download);
+	wl->fw_download = NULL;
+}
+
+int get_device_info(struct cc33xx *wl)
+{
+	int ret; 
+	union hw_info hw_info;
+	u64 mac_address;
+
+	ret = cmd_get_device_info(wl, hw_info.bytes, sizeof hw_info.bytes);
+	if (ret < 0)
+		return ret;
+
+	cc33xx_debug(DEBUG_BOOT, "CC33XX device info: "
+		"PG version: %d, Metal version: %d, Boot ROM version: %d "
+		"M3 ROM version: %d, MAC address: 0x%llx, Device part number: %d",
+		hw_info.bitmap.pg_version, hw_info.bitmap.metal_version,
+		hw_info.bitmap.boot_rom_version, hw_info.bitmap.m3_rom_version,
+		(u64) hw_info.bitmap.mac_address, hw_info.bitmap.device_part_number);
+		
+	wl->fw_download->max_transfer_size = 640;
+
+	mac_address = hw_info.bitmap.mac_address;
+
+	wl->fuse_rom_structure_version = hw_info.bitmap.fuse_rom_structure_version;
+	wl->pg_version = hw_info.bitmap.pg_version;
+	wl->device_part_number = hw_info.bitmap.device_part_number;
+	wl->disable_5g = hw_info.bitmap.disable_5g;
+	wl->disable_6g = hw_info.bitmap.disable_6g;
+
+	wl->efuse_mac_address[5] = (u8) (mac_address);
+	wl->efuse_mac_address[4] = (u8) (mac_address >> 8);
+	wl->efuse_mac_address[3] = (u8) (mac_address >> 16);
+	wl->efuse_mac_address[2] = (u8) (mac_address >> 24);
+	wl->efuse_mac_address[1] = (u8) (mac_address >> 32);
+	wl->efuse_mac_address[0] = (u8) (mac_address >> 40);
+
+	return 0;
+}
+
+int cc33xx_init_fw(struct cc33xx *wl)
+{
+	int ret;
+	wl->max_cmd_size = CC33XX_CMD_MAX_SIZE;
+
+	ret = fw_download_alloc(wl);
+	if (ret < 0)
+		return ret;	
+
+	reinit_completion(&wl->fw_download->wait_on_irq);
+
+	ret = cc33xx_chip_wakeup(wl);
+	if (ret < 0)
+		goto power_off;
+
+	wlcore_enable_interrupts(wl);
+
+	ret = wait_for_boot_irq(wl, 
+		HINT_ROM_LOADER_INIT_COMPLETE, 
+		CC33XX_BOOT_TIMEOUT);
+	if (ret < 0)
+		goto disable_irq;
+
+	ret = get_device_info(wl);
+	if (ret < 0)
+		goto disable_irq;
+	
+	ret = container_download_and_wait(wl, 
+		SECOND_LOADER_NAME, 
+		HINT_SECOND_LOADER_INIT_COMPLETE);
+	if (ret < 0)
+		goto disable_irq;
+
+	ret = container_download_and_wait(wl, 
+		FW_NAME, 
+		HINT_FW_WAKEUP_COMPLETE);
+	if (ret < 0)
+		goto disable_irq;
+
+	ret = cc33xx_download_ini_params_and_wait(wl);
+
+	if (ret < 0)
+		goto disable_irq;
+	
+	ret = wait_for_boot_irq(wl, HINT_FW_INIT_COMPLETE , CC33XX_BOOT_TIMEOUT);
+	
+
+	if (ret < 0)
+		goto disable_irq;
+	
+	/* Get static calibration data and send it to FW*/
+	ret = download_static_calibration_data(wl);
+	if (ret < 0)
+		return ret;
+
+	ret = cc33xx_hw_init(wl);
+	if (ret < 0)
+		goto disable_irq;
+
+	/*
+	 * Now we know if 11a is supported (info from the INI File), so disable
+	 * 11a channels if not supported
+	 */
+	wl->enable_11a =wl->conf.core.enable_5ghz;
+
+	cc33xx_debug(DEBUG_MAC80211, "11a is %ssupported",
+		     wl->enable_11a ? "" : "not ");
+
+	wl->state = WLCORE_STATE_ON;
+	ret = 0;
+	goto out;
+
+disable_irq:
+	wlcore_disable_interrupts_nosync(wl);
+
+power_off:
+	cc33xx_power_off(wl);
+
+out:
+	fw_download_free(wl);
+	return ret;
+}
diff --git drivers/net/wireless/ti/cc33xx/boot.h drivers/net/wireless/ti/cc33xx/boot.h
new file mode 100644
index 000000000000..534221c8b66e
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/boot.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __BOOT_H__
+#define __BOOT_H__
+
+#include "wlcore.h"
+
+int cc33xx_init_fw(struct cc33xx *wl);
+
+void cc33xx_handle_boot_irqs(struct cc33xx *wl, u32 pending_interrupts);
+
+#define SECOND_LOADER_NAME "ti-connectivity/cc33xx_2nd_loader.bin"
+#define FW_NAME "ti-connectivity/cc33xx_fw.bin"
+
+
+struct cc33xx_fw_download {
+	atomic_t pending_irqs;
+	struct completion wait_on_irq;
+	size_t max_transfer_size;
+};
+
+/* number of times we try to read the INIT interrupt */
+#define INIT_LOOP 20000
+
+/* delay between retries */
+#define INIT_LOOP_DELAY 50
+
+#define WU_COUNTER_PAUSE_VAL 0x3FF
+#define WELP_ARM_COMMAND_VAL 0x4
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/cc33xx_80211.h drivers/net/wireless/ti/cc33xx/cc33xx_80211.h
new file mode 100644
index 000000000000..34be85ee73b8
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/cc33xx_80211.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __CC33XX_80211_H__
+#define __CC33XX_80211_H__
+
+#include <linux/if_ether.h>	/* ETH_ALEN */
+#include <linux/if_arp.h>
+
+
+/* This really should be 8, but not for our firmware */
+#define MAX_SUPPORTED_RATES 32
+#define MAX_COUNTRY_TRIPLETS 32
+
+/* Headers */
+struct ieee80211_header {
+	__le16 frame_ctl;
+	__le16 duration_id;
+	u8 da[ETH_ALEN];
+	u8 sa[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	__le16 seq_ctl;
+	u8 payload[0];
+} __packed;
+
+
+/* Templates */
+
+struct cc33xx_null_data_template {
+	struct ieee80211_header header;
+} __packed;
+
+struct cc33xx_arp_rsp_template {
+	/* not including ieee80211 header */
+
+	u8 llc_hdr[sizeof(rfc1042_header)];
+	__be16 llc_type;
+
+	struct arphdr arp_hdr;
+	u8 sender_hw[ETH_ALEN];
+	__be32 sender_ip;
+	u8 target_hw[ETH_ALEN];
+	__be32 target_ip;
+} __packed;
+
+struct cc33xx_disconn_template {
+	struct ieee80211_header header;
+	__le16 disconn_reason;
+} __packed;
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/cmd.c drivers/net/wireless/ti/cc33xx/cmd.c
new file mode 100644
index 000000000000..b95022fbd4aa
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/cmd.c
@@ -0,0 +1,2369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "io.h"
+#include "acx.h"
+#include "cc33xx_80211.h"
+#include "cmd.h"
+#include "event.h"
+#include "tx.h"
+
+#define CC33XX_REBOOT_TIMEOUT_MSEC		100
+
+#define CC33XX_CMD_FAST_POLL_COUNT       50
+#define CC33XX_WAIT_EVENT_FAST_POLL_COUNT 20
+static inline void init_cmd_header(
+	struct cc33xx_cmd_header* header, 
+	size_t cmd_len,
+	u16 id)
+{
+	header->NAB_header.len = cpu_to_le16(cmd_len);
+	BUG_ON(header->NAB_header.len != cmd_len);
+
+	header->NAB_header.sync_pattern = cpu_to_le32(HOST_SYNC_PATTERN);
+	header->NAB_header.opcode = cpu_to_le16(id);
+}
+int cc33xx_set_max_buffer_size(struct cc33xx *wl, BufferSize_e max_buffer_size)
+{
+	switch(max_buffer_size)
+	{
+		case INI_MAX_BUFFER_SIZE:
+			wl->max_cmd_size = CC33XX_INI_CMD_MAX_SIZE + sizeof(struct cc33xx_cmd_ini_params_download)+ sizeof(u32);//INI FILE PAYLOAD SIZE + INI CMD PARAM + INT
+			break;
+
+		case CMD_MAX_BUFFER_SIZE:
+			wl->max_cmd_size = CC33XX_CMD_MAX_SIZE;
+			break;
+		default:
+			cc33xx_warning("max_buffer_size invalid, not changing buffer size");
+			break;
+	}
+	return 0;
+
+}
+
+static int send_buffer(struct cc33xx *wl, int cmd_box_addr,
+			       void *buf, size_t len)
+{
+	size_t max_cmd_size_align;
+	memcpy(wl->cmd_buf, buf, len);
+	
+	memset(wl->cmd_buf + len, 0, (CC33XX_CMD_BUFFER_SIZE) - len);
+
+	max_cmd_size_align = __ALIGN_MASK(wl->max_cmd_size,CC33XX_BUS_BLOCK_SIZE*2 - 1);
+	
+	return wlcore_write(wl, cmd_box_addr, wl->cmd_buf,
+				(max_cmd_size_align), true);
+}
+
+/*
+ * send command to firmware
+ *
+ * @wl: wl struct
+ * @id: command id
+ * @buf: buffer containing the command, must work with dma
+ * @len: length of the buffer
+ * return the cmd status code on success.
+ */
+static int __wlcore_cmd_send(struct cc33xx *wl, u16 id, void *buf,
+			     size_t len, size_t res_len, bool sync)
+{
+	struct cc33xx_cmd_header *cmd;
+	unsigned long timeout;
+	int ret;
+
+	if (id >= CMD_LAST_SUPPORTED_COMMAND)
+	{
+	    cc33xx_debug(DEBUG_CMD, "command ID: %d, blocked",id);
+	    return(CMD_STATUS_SUCCESS);
+	}
+
+	if (unlikely(wl->state == WLCORE_STATE_RESTARTING &&
+		     id != CMD_STOP_FWLOGGER))
+		return -EIO;
+
+	if (WARN_ON_ONCE(len < sizeof(*cmd)))
+		return -EIO;
+
+	BUG_ON(len > wl->max_cmd_size);
+		
+	cmd = buf;
+	cmd->id = cpu_to_le16(id);
+	cmd->status = 0;
+
+	WARN_ON(len % 4 != 0);
+	WARN_ON(test_bit(CC33XX_FLAG_IN_ELP, &wl->flags));
+	init_cmd_header(cmd, len, id);
+	init_completion (&wl->command_complete);
+	ret = send_buffer(wl, NAB_DATA_ADDR, buf, len);
+	if (ret < 0)
+		return ret;
+
+	if (unlikely(!sync))
+		return CMD_STATUS_SUCCESS;
+	timeout = msecs_to_jiffies(CC33XX_COMMAND_TIMEOUT);
+	ret = wait_for_completion_timeout(
+		&wl->command_complete, timeout);
+	if (ret < 1)
+	{
+		cc33xx_debug(DEBUG_CMD, "Command T.O");
+		return -EIO;
+	}	
+
+	switch(id){
+	case CMD_INTERROGATE:
+	case CMD_DEBUG_READ:
+	case CMD_TEST_MODE:
+	case CMD_BM_READ_DEVICE_INFO:
+		cc33xx_debug(DEBUG_CMD, 
+			"Response len %d, allocated buffer len %d",
+			wl->result_length,
+			res_len);
+
+		if (!res_len)
+			break; // Response should be discarded  
+
+		if (WARN_ON(wl->result_length > res_len)){
+			cc33xx_error("Error, insufficient response buffer");
+			break;
+		}
+
+		memcpy(buf + sizeof(struct NAB_header),
+			wl->command_result,
+			wl->result_length);
+
+		break;
+
+	default:
+		break;
+	}
+	
+	return CMD_STATUS_SUCCESS;
+}
+
+/*
+ * send command to fw and return cmd status on success
+ * valid_rets contains a bitmap of allowed error codes
+ */
+static int wlcore_cmd_send_failsafe(struct cc33xx *wl, u16 id, void *buf,
+				    size_t len, size_t res_len,
+				    unsigned long valid_rets)
+{
+	int ret = __wlcore_cmd_send(wl, id, buf, len, res_len, true);
+	
+
+	cc33xx_debug(DEBUG_TESTMODE, "CMD# %d, len=%d", id, len);
+
+	if (ret < 0)
+		goto fail;
+
+	/* success is always a valid status */
+	valid_rets |= BIT(CMD_STATUS_SUCCESS);
+
+	if (ret >= MAX_COMMAND_STATUS ||
+	    !test_bit(ret, &valid_rets)) {
+		cc33xx_error("command execute failure %d", ret);
+		ret = -EIO;
+		//goto fail;
+	}
+	return ret;
+fail:
+	cc33xx_queue_recovery_work(wl);
+	return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
+ * return 0 on success.
+ */
+int cc33xx_cmd_send(struct cc33xx *wl, u16 id, void *buf, size_t len,
+		    size_t res_len)
+{
+	int ret;
+	/* Support id */
+	switch((enum cc33xx_cmd)id)
+	{
+		case CMD_EMPTY:
+		case CMD_CHANNEL_SWITCH          :
+		case CMD_STOP_CHANNEL_SWICTH     :
+		case CMD_START_DHCP_MGMT_SEQ     :
+		case CMD_STOP_DHCP_MGMT_SEQ      :
+		case CMD_START_SECURITY_MGMT_SEQ :
+		case CMD_STOP_SECURITY_MGMT_SEQ  :
+		case CMD_START_ARP_MGMT_SEQ      :
+		case CMD_STOP_ARP_MGMT_SEQ       :
+		case CMD_START_DNS_MGMT_SEQ      :
+		case CMD_STOP_DNS_MGMT_SEQ       :
+		case CMD_SEND_DEAUTH_DISASSOC    :
+		case CMD_SCHED_STATE_EVENT	 :
+		{
+			return 0;
+		}break;
+		default:
+		{
+			if ( (enum cc33xx_cmd)id >= CMD_LAST_SUPPORTED_COMMAND) 
+				return 0;
+			goto send;
+		}
+	}
+send:
+	ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+int cc33xx_count_role_set_bits(unsigned long role_map)
+{
+	int count = 0;
+	// if device bit is set ( BIT_2 = ROLE_DEVICE)
+	// since role device is not counted
+	// remove it from map
+	role_map &= ~BIT(2);
+	
+	while (role_map != 0)
+	{
+		count += role_map & 1;
+		role_map >>= 1;
+	}
+	return count;
+}
+
+int cc33xx_cmd_role_enable(struct cc33xx *wl, u8 *addr, u8 role_type,
+			   u8 *role_id)
+{
+	struct cc33xx_cmd_role_enable *cmd;
+
+	int ret;
+	unsigned long role_count;
+	
+
+	struct cc33xx_cmd_complete_role_enable *command_complete = 
+		  (struct cc33xx_cmd_complete_role_enable *)&wl->command_result;
+
+	role_count = *wl->roles_map;
+        ret = cc33xx_count_role_set_bits(role_count);
+	cc33xx_debug(DEBUG_CMD, "cmd roles enabled: bitmap before: %ld, ret=%d", role_count, ret);
+
+	//do not enable more than 2 roles at once, exception is device role
+	if ((ret >= 2) && (CC33XX_ROLE_DEVICE != role_type))
+	{
+		cc33xx_debug(DEBUG_CMD, "cmd role enable: 2 roles already have beed allocated");
+		cc33xx_error("failed to initiate cmd role enable");
+		ret = -EBUSY;
+		goto out;
+	}
+	cc33xx_debug(DEBUG_CMD, "cmd role enable, role type %d, addr = %pM", role_type, addr);
+
+	if (WARN_ON(*role_id != CC33XX_INVALID_ROLE_ID))
+		return -EBUSY;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(cmd->mac_address, addr, ETH_ALEN);
+	cmd->role_type = role_type;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_ENABLE, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role enable");
+		goto out_free;
+	}
+	cc33xx_debug(DEBUG_CMD, "complete role_id = %d", 
+						      command_complete->role_id);
+	__set_bit(command_complete->role_id, wl->roles_map);
+	*role_id = command_complete->role_id;
+
+out_free:
+	kfree(cmd);
+out:
+	return ret;
+}
+
+int cc33xx_cmd_role_disable(struct cc33xx *wl, u8 *role_id)
+{
+	struct cc33xx_cmd_role_disable *cmd;
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD, "cmd role disable");
+
+	if (WARN_ON(*role_id == CC33XX_INVALID_ROLE_ID))
+		return -ENOENT;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	cmd->role_id = *role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_DISABLE, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role disable");
+		goto out_free;
+	}
+
+	__clear_bit(*role_id, wl->roles_map);
+	*role_id = CC33XX_INVALID_ROLE_ID;
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_set_link(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 link)
+{
+	unsigned long flags;
+
+	/* these bits are used by op_tx */
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	__set_bit(link, wl->links_map);
+	__set_bit(link, wlvif->links_map);
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+	wl->links[link].wlvif = wlvif;
+
+	/*
+	 * Take saved value for total freed packets from wlvif, in case this is
+	 * recovery/resume
+	 */
+	if (wlvif->bss_type != BSS_TYPE_AP_BSS)
+		wl->links[link].total_freed_pkts = wlvif->total_freed_pkts;
+
+	wl->active_link_count++;
+	return 0;
+}
+
+void cc33xx_clear_link(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 *hlid)
+{
+	unsigned long flags;
+
+	if (*hlid == CC33XX_INVALID_LINK_ID)
+		return;
+
+	/* these bits are used by op_tx */
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	__clear_bit(*hlid, wl->links_map);
+	__clear_bit(*hlid, wlvif->links_map);
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	wl->links[*hlid].prev_freed_pkts = 0;
+	wl->links[*hlid].ba_bitmap = 0;
+	eth_zero_addr(wl->links[*hlid].addr);
+
+	/*
+	 * At this point op_tx() will not add more packets to the queues. We
+	 * can purge them.
+	 */
+	cc33xx_tx_reset_link_queues(wl, *hlid);
+	wl->links[*hlid].wlvif = NULL;
+
+	if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+	    *hlid == wlvif->ap.bcast_hlid) {
+		u32 sqn_padding = CC33XX_TX_SQN_POST_RECOVERY_PADDING;
+		/*
+		 * save the total freed packets in the wlvif, in case this is
+		 * recovery or suspend
+		 */
+		wlvif->total_freed_pkts = wl->links[*hlid].total_freed_pkts;
+
+		/*
+		 * increment the initial seq number on recovery to account for
+		 * transmitted packets that we haven't yet got in the FW status
+		 */
+		if (wlvif->encryption_type == KEY_GEM)
+			sqn_padding = CC33XX_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
+		if (test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+			wlvif->total_freed_pkts += sqn_padding;
+	}
+
+	wl->links[*hlid].total_freed_pkts = 0;
+
+	*hlid = CC33XX_INVALID_LINK_ID;
+	wl->active_link_count--;
+	WARN_ON_ONCE(wl->active_link_count < 0);
+}
+
+u8 wlcore_get_native_channel_type(u8 nl_channel_type)
+{
+	switch (nl_channel_type) {
+	case NL80211_CHAN_NO_HT:
+		return WLCORE_CHAN_NO_HT;
+	case NL80211_CHAN_HT20:
+		return WLCORE_CHAN_HT20;
+	case NL80211_CHAN_HT40MINUS:
+		return WLCORE_CHAN_HT40MINUS;
+	case NL80211_CHAN_HT40PLUS:
+		return WLCORE_CHAN_HT40PLUS;
+	default:
+		WARN_ON(1);
+		return WLCORE_CHAN_NO_HT;
+	}
+}
+
+int cc33xx_cmd_role_start_dev(struct cc33xx *wl,
+				     struct cc33xx_vif *wlvif,
+				     enum nl80211_band band,
+				     int channel)
+{
+	struct cc33xx_cmd_role_start *cmd;
+	int ret;
+
+	struct cc33xx_cmd_complete_role_start *command_complete = 
+	          (struct cc33xx_cmd_complete_role_start *)&wl->command_result;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
+
+	cmd->role_id = wlvif->dev_role_id;
+	cmd->role_type = CC33XX_ROLE_DEVICE;
+	if (band == NL80211_BAND_5GHZ)
+		cmd->band = WLCORE_BAND_5GHZ;
+	cmd->channel = channel;
+	cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
+	
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role device start");
+		goto err_hlid;
+	}
+
+	wlvif->dev_hlid = command_complete->sta.hlid;
+	wl->links[wlvif->dev_hlid].allocated_pkts = 0;
+    	wl->session_ids[wlvif->dev_hlid] = command_complete->sta.session;
+    	cc33xx_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d ",
+             wlvif->dev_role_id, command_complete->sta.hlid, command_complete->sta.session);
+	ret = cc33xx_set_link(wl, wlvif, wlvif->dev_hlid);
+	goto out_free;
+
+err_hlid:
+	/* clear links on error */
+	cc33xx_clear_link(wl, wlvif, &wlvif->dev_hlid);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_role_stop_transceiver(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_role_stop *cmd;
+	int ret;
+
+	if (unlikely((wl->state != WLCORE_STATE_ON) || (!wl->plt))) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	cc33xx_debug(DEBUG_CMD, "cmd role stop transceiver");
+
+	cmd->role_id = wl->plt_role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("transceiver - failed to initiate cmd role stop");
+		goto out_free;
+	}
+	
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+
+int cc33xx_cmd_plt_disable(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_PLT_disable *cmd;
+	int ret;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	
+	ret = cc33xx_cmd_send(wl, CMD_PLT_DISABLE, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("transceiver: failed to disable Transceiver mode");
+		goto out_free;
+	}
+	else
+	{
+		cc33xx_debug(DEBUG_CMD, "Succeed to disable Transceiver mode");
+	}
+	
+	out_free:
+	kfree(cmd);
+
+	out:
+	return ret;
+}
+
+static int cc333xx_cmd_role_stop_dev(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_cmd_role_stop *cmd;
+	int ret;
+
+	if (WARN_ON(wlvif->dev_hlid == CC33XX_INVALID_LINK_ID))
+		return -EINVAL;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role stop dev");
+
+	cmd->role_id = wlvif->dev_role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role stop");
+		goto out_free;
+	}
+
+	cc33xx_clear_link(wl, wlvif, &wlvif->dev_hlid);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+
+int cc33xx_cmd_plt_enable(struct cc33xx *wl, u8 role_id)
+{
+	struct cc33xx_cmd_PLT_enable *cmd;
+	int32_t ret;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_send(wl, CMD_PLT_ENABLE, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("Failed to send CMD_PLT_ENABLE");
+		goto out_free;
+	}
+	cc33xx_debug(DEBUG_CMD, "Success to send CMD_PLT_ENABLE");
+	
+out_free:
+	kfree(cmd);
+out:
+	return ret;
+
+}
+
+int cc33xx_cmd_role_start_transceiver(struct cc33xx *wl, u8 role_id)
+{
+	struct cc33xx_cmd_role_start *cmd;
+	int32_t ret;
+	u8 role_type = ROLE_TRANSCEIVER;
+
+	/* Default values */
+	u8 band = NL80211_BAND_2GHZ;
+	u8 channel = 6;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+    cmd->role_type = role_type;
+	cmd->role_id = role_id;
+	cmd->channel = channel;
+	cmd->band = band;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role start PLT");
+		goto out_free;
+	}
+	cc33xx_debug(DEBUG_CMD, "cmd role start PLT. Role ID number: %u", role_id);
+
+out_free:
+	kfree(cmd);
+out:
+	return ret;
+}
+
+int cc33xx_cmd_role_start_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	struct cc33xx_cmd_role_start *cmd;
+
+	u32 supported_rates;
+	int ret;
+
+	struct cc33xx_cmd_complete_role_start *command_complete = 
+	          (struct cc33xx_cmd_complete_role_start *)&wl->command_result;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role start sta %d", wlvif->role_id);
+
+	cmd->role_id = wlvif->role_id;
+	cmd->role_type = CC33XX_ROLE_STA;
+	if (wlvif->band == NL80211_BAND_5GHZ)
+		cmd->band = WLCORE_BAND_5GHZ;
+	cmd->channel = wlvif->channel;
+	if (wlvif->band == NL80211_BAND_5GHZ)
+		cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set & ~CONF_TX_CCK_RATES);
+	else
+		cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+	cmd->sta.beacon_interval = cpu_to_le16(wlvif->beacon_int);
+	cmd->sta.ssid_type = CC33XX_SSID_TYPE_ANY;
+	cmd->sta.ssid_len = wlvif->ssid_len;
+	memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
+	memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
+	
+	supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | wlvif->rate_set;
+	if (wlvif->band == NL80211_BAND_5GHZ)
+		supported_rates &= ~CONF_TX_CCK_RATES;
+		
+	if (wlvif->p2p)
+		supported_rates &= ~CONF_TX_CCK_RATES;
+
+	cmd->sta.local_rates = cpu_to_le32(supported_rates);
+
+	cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
+
+	/*
+	 * We don't have the correct remote rates in this stage.  The
+	 * rates will be reconfigured later, after association, if the
+	 * firmware supports ACX_PEER_CAP.  Otherwise, there's nothing
+	 * we can do, so use all supported_rates here.
+	 */
+	cmd->sta.remote_rates = cpu_to_le32(supported_rates);
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role start sta");
+		goto err_hlid;
+	}
+
+	wlvif->sta.role_chan_type = wlvif->channel_type;
+
+    	wlvif->sta.hlid = command_complete->sta.hlid;
+	wl->links[wlvif->sta.hlid].allocated_pkts = 0;
+    	wl->session_ids[wlvif->sta.hlid] = command_complete->sta.session;
+    	cc33xx_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
+             "basic_rate_set: 0x%x, remote_rates: 0x%x",
+             wlvif->role_id, command_complete->sta.hlid, command_complete->sta.session,
+             wlvif->basic_rate_set, wlvif->rate_set);
+	ret = cc33xx_set_link(wl, wlvif, wlvif->sta.hlid);
+
+	goto out_free;
+
+err_hlid:
+
+	cc33xx_clear_link(wl, wlvif, &wlvif->sta.hlid);
+
+out_free:
+	kfree(cmd);
+out:
+	return ret;
+}
+
+/* use this function to stop ibss as well */
+int cc33xx_cmd_role_stop_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_cmd_role_stop *cmd;
+	int ret;
+
+	if (WARN_ON(wlvif->sta.hlid == CC33XX_INVALID_LINK_ID))
+		return -EINVAL;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role stop sta %d", wlvif->role_id);
+
+	cmd->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role stop sta");
+		goto out_free;
+	}
+
+	cc33xx_clear_link(wl, wlvif, &wlvif->sta.hlid);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_role_start_ap(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_cmd_role_start *cmd;
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+	u32 supported_rates;
+	int ret;
+
+	struct cc33xx_cmd_complete_role_start *command_complete = 
+	          (struct cc33xx_cmd_complete_role_start *)&wl->command_result;
+
+
+	cc33xx_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
+	cc33xx_debug(DEBUG_CMD, "cmd role start ap basic rateset:  0x%x ", wlvif->basic_rate_set);
+
+	/* If MESH --> ssid_len is always 0 */
+	if (!ieee80211_vif_is_mesh(vif)) {
+		/* trying to use hidden SSID with an old hostapd version */
+		if (wlvif->ssid_len == 0 && !bss_conf->hidden_ssid) {
+			cc33xx_error("got a null SSID from beacon/bss");
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->role_id = wlvif->role_id;
+	cmd->role_type = CC33XX_ROLE_AP;
+	cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+	cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
+	cmd->ap.dtim_interval = bss_conf->dtim_period;
+	cmd->ap.wmm = wlvif->wmm_enabled;
+	cmd->channel = wlvif->channel;
+	cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
+
+	supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES ;
+	if (wlvif->p2p)
+		supported_rates &= ~CONF_TX_CCK_RATES;
+
+	cc33xx_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
+		     supported_rates);
+
+	cmd->ap.local_rates = cpu_to_le32(supported_rates);
+
+	switch (wlvif->band) {
+	case NL80211_BAND_2GHZ:
+		cmd->band = WLCORE_BAND_2_4GHZ;
+		break;
+	case NL80211_BAND_5GHZ:
+		cmd->band = WLCORE_BAND_5GHZ;
+		break;
+	default:
+		cc33xx_warning("ap start - unknown band: %d", (int)wlvif->band);
+		cmd->band = WLCORE_BAND_2_4GHZ;
+		break;
+	}
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role start ap");
+		goto out_free_bcast;
+	}
+
+	wlvif->ap.global_hlid = command_complete->ap.global_hlid;
+	wlvif->ap.bcast_hlid  = command_complete->ap.broadcast_hlid;
+	wl->session_ids[wlvif->ap.global_hlid] = command_complete->ap.global_session_id;
+	wl->session_ids[wlvif->ap.bcast_hlid] = command_complete->ap.bcast_session_id;
+
+    	cc33xx_debug(DEBUG_CMD, "role start: roleid=%d, global_hlid=%d, "
+	     "broadcast_hlid=%d global_session_id=%d, bcast_session_id=%d "
+             "basic_rate_set: 0x%x, remote_rates: 0x%x",
+             wlvif->role_id, command_complete->ap.global_hlid, 
+	     command_complete->ap.broadcast_hlid, command_complete->ap.global_session_id,
+	     command_complete->ap.bcast_session_id, wlvif->basic_rate_set, wlvif->rate_set);
+
+	ret = cc33xx_set_link(wl, wlvif, wlvif->ap.global_hlid);
+	ret = cc33xx_set_link(wl, wlvif, wlvif->ap.bcast_hlid );
+
+	goto out_free;
+
+out_free_bcast:
+
+	cc33xx_clear_link(wl, wlvif, &wlvif->ap.bcast_hlid);
+	cc33xx_clear_link(wl, wlvif, &wlvif->ap.global_hlid);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_role_stop_ap(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_cmd_role_stop *cmd;
+	int ret;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role stop ap %d", wlvif->role_id);
+
+	cmd->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role stop ap");
+		goto out_free;
+	}
+
+
+	cc33xx_clear_link(wl, wlvif, &wlvif->ap.bcast_hlid);
+	cc33xx_clear_link(wl, wlvif, &wlvif->ap.global_hlid);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_role_start_ibss(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+//TODO: RazB - need to implemntation role ibss
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	struct cc33xx_cmd_role_start *cmd;
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+	int ret;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role start ibss %d", wlvif->role_id);
+
+	cmd->role_id = wlvif->role_id;
+	cmd->role_type = CC33XX_ROLE_IBSS;
+	if (wlvif->band == NL80211_BAND_5GHZ)
+		cmd->band = WLCORE_BAND_5GHZ;
+	cmd->channel = wlvif->channel;
+	cmd->ibss.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+	cmd->ibss.beacon_interval = cpu_to_le16(wlvif->beacon_int);
+	cmd->ibss.dtim_interval = bss_conf->dtim_period;
+	cmd->ibss.ssid_type = CC33XX_SSID_TYPE_ANY;
+	cmd->ibss.ssid_len = wlvif->ssid_len;
+	memcpy(cmd->ibss.ssid, wlvif->ssid, wlvif->ssid_len);
+	memcpy(cmd->ibss.bssid, vif->bss_conf.bssid, ETH_ALEN);
+	cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+
+	if (wlvif->sta.hlid == CC33XX_INVALID_LINK_ID) {
+		ret = cc33xx_set_link(wl, wlvif, wlvif->sta.hlid);
+		if (ret)
+			goto out_free;
+	}
+	cmd->ibss.hlid = wlvif->sta.hlid;
+	cmd->ibss.remote_rates = cpu_to_le32(wlvif->rate_set);
+
+	ret = cc33xx_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd role enable");
+		goto err_hlid;
+	}
+
+	goto out_free;
+
+err_hlid:
+	/* clear links on error. */
+	cc33xx_clear_link(wl, wlvif, &wlvif->sta.hlid);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+
+/**
+ * send test command to firmware
+ *
+ * @wl: wl struct
+ * @buf: buffer containing the command, with all headers, must work with dma
+ * @len: length of the buffer
+ * @answer: is answer needed
+ */
+int cc33xx_cmd_test(struct cc33xx *wl, void *buf, size_t buf_len, u8 answer)
+{
+	int ret;
+	size_t res_len = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd test");
+
+	if (answer)
+		res_len = buf_len;
+
+	ret = cc33xx_cmd_send(wl, CMD_TEST_MODE, buf, buf_len, res_len);
+
+	if (ret < 0) {
+		cc33xx_warning("TEST command failed");
+		return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * read acx from firmware
+ *
+ * @wl: wl struct
+ * @id: acx id
+ * @buf: buffer for the response, including all headers, must work with dma
+ * @len: length of buf
+ */
+int cc33xx_cmd_interrogate(struct cc33xx *wl, u16 id, void *buf,
+			   size_t cmd_len, size_t res_len)
+{
+	struct acx_header *acx = buf;
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD, "cmd interrogate");
+
+	acx->id = cpu_to_le16(id);
+
+	/* response payload length, does not include any headers */
+	acx->len = cpu_to_le16(res_len - sizeof(*acx));
+
+	ret = cc33xx_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len);
+	if (ret < 0)
+		cc33xx_error("INTERROGATE command failed");
+
+	return ret;
+}
+
+/**
+ * read debug acx from firmware
+ *
+ * @wl: wl struct
+ * @id: acx id
+ * @buf: buffer for the response, including all headers, must work with dma
+ * @len: length of buf
+ */
+int cc33xx_cmd_debug_inter(struct cc33xx *wl, u16 id, void *buf,
+			   size_t cmd_len, size_t res_len)
+{
+	struct acx_header *acx = buf;
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD, "cmd debug interrogate");
+
+	acx->id = cpu_to_le16(id);
+
+	/* response payload length, does not include any headers */
+	acx->len = cpu_to_le16(res_len - sizeof(*acx));
+
+	ret = cc33xx_cmd_send(wl, CMD_DEBUG_READ, acx, cmd_len, res_len);
+	if (ret < 0)
+		cc33xx_error("CMD_DEBUG_READ command failed");
+
+	return ret;
+}
+
+/**
+ * write acx value to firmware
+ *
+ * @wl: wl struct
+ * @id: acx id
+ * @buf: buffer containing acx, including all headers, must work with dma
+ * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
+ */
+int wlcore_cmd_configure_failsafe(struct cc33xx *wl, u16 id, void *buf,
+				  size_t len, unsigned long valid_rets)
+{
+	struct acx_header *acx = buf;
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD, "cmd configure (%d), TSFL %x", 
+		id, wl->core_status->tsf);
+
+	if (WARN_ON_ONCE(len < sizeof(*acx)))
+		return -EIO;
+
+	acx->id = cpu_to_le16(id);
+
+	/* payload length, does not include any headers */
+	acx->len = cpu_to_le16(len - sizeof(*acx));
+
+	ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
+				       valid_rets);
+	if (ret < 0) {
+		cc33xx_warning("CONFIGURE command NOK");
+		return ret;
+	}
+
+	return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_configure that accepts only success status.
+ * return 0 on success
+ */
+int cc33xx_cmd_configure(struct cc33xx *wl, u16 id, void *buf, size_t len)
+{
+	int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
+
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+
+/**
+ * write acx value to firmware
+ *
+ * @wl: wl struct
+ * @id: acx id
+ * @buf: buffer containing debug, including all headers, must work with dma
+ * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
+ */
+int wlcore_cmd_debug_failsafe(struct cc33xx *wl, u16 id, void *buf,
+				  size_t len, unsigned long valid_rets)
+{
+	struct debug_header *acx = buf;
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD, "cmd debug (%d)", id);
+
+	if (WARN_ON_ONCE(len < sizeof(*acx)))
+		return -EIO;
+
+	acx->id = cpu_to_le16(id);
+
+	/* payload length, does not include any headers */
+	acx->len = cpu_to_le16(len - sizeof(*acx));
+
+	ret = wlcore_cmd_send_failsafe(wl, CMD_DEBUG, acx, len, 0,
+				       valid_rets);
+	if (ret < 0) {
+		cc33xx_warning("CONFIGURE command NOK");
+		return ret;
+	}
+
+	return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_debug that accepts only success status.
+ * return 0 on success
+ */
+int cc33xx_cmd_debug(struct cc33xx *wl, u16 id, void *buf, size_t len)
+{
+	int ret = wlcore_cmd_debug_failsafe(wl, id, buf, len, 0);
+
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+int cc33xx_cmd_ps_mode(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       u8 ps_mode, u16 auto_ps_timeout)
+{
+	struct cc33xx_cmd_ps_params *ps_params = NULL;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd set ps mode");
+
+	ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL);
+	if (!ps_params) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ps_params->role_id = wlvif->role_id;
+	ps_params->ps_mode = ps_mode;
+	ps_params->auto_ps_timeout = cpu_to_le16(auto_ps_timeout);
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
+			      sizeof(*ps_params), 0);
+	if (ret < 0) {
+		cc33xx_error("cmd set_ps_mode failed");
+		goto out;
+	}
+
+out:
+	kfree(ps_params);
+	return ret;
+}
+
+int cc33xx_cmd_template_set(struct cc33xx *wl, u8 role_id,
+			    u16 template_id, void *buf, size_t buf_len,
+			    int index, u32 rates)
+{
+	struct cc33xx_cmd_template_set *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd template_set %d (role %d)",
+		     template_id, role_id);
+
+	WARN_ON(buf_len > CC33XX_CMD_TEMPL_MAX_SIZE);
+	buf_len = min_t(size_t, buf_len, CC33XX_CMD_TEMPL_MAX_SIZE);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* during initialization wlvif is NULL */
+	cmd->role_id = role_id;
+	cmd->len = cpu_to_le16(buf_len);
+	cmd->template_type = template_id;
+	cmd->enabled_rates = cpu_to_le32(rates);
+	cmd->short_retry_limit = wl->conf.host_conf.tx.tmpl_short_retry_limit;
+	cmd->long_retry_limit = wl->conf.host_conf.tx.tmpl_long_retry_limit;
+	cmd->index = index;
+
+	if (buf)
+		memcpy(cmd->template_data, buf, buf_len);
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_warning("cmd set_template failed: %d", ret);
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_build_null_data(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct sk_buff *skb = NULL;
+	int size;
+	void *ptr;
+	int ret = -ENOMEM;
+
+
+	if (wlvif->bss_type == BSS_TYPE_IBSS) {
+		size = sizeof(struct cc33xx_null_data_template);
+		ptr = NULL;
+	} else {
+		skb = ieee80211_nullfunc_get(wl->hw,
+					     cc33xx_wlvif_to_vif(wlvif),
+					     -1, false);
+		if (!skb)
+			goto out;
+		size = skb->len;
+		ptr = skb->data;
+	}
+
+	ret = cc33xx_cmd_template_set(wl, wlvif->role_id,
+				      CMD_TEMPL_NULL_DATA, ptr, size, 0,
+				      wlvif->basic_rate);
+
+out:
+	dev_kfree_skb(skb);
+	if (ret)
+		cc33xx_warning("cmd buld null data failed %d", ret);
+
+	return ret;
+
+}
+
+struct sk_buff *cc33xx_cmd_build_ap_probe_req(struct cc33xx *wl,
+					      struct cc33xx_vif *wlvif,
+					      struct sk_buff *skb)
+{
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	int ret;
+	u32 rate;
+
+	if (!skb)
+		skb = ieee80211_ap_probereq_get(wl->hw, vif);
+	if (!skb)
+		goto out;
+
+	cc33xx_debug(DEBUG_SCAN, "set ap probe request template");
+
+	rate = cc33xx_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]);
+	if (wlvif->band == NL80211_BAND_2GHZ)
+		ret = cc33xx_cmd_template_set(wl, wlvif->role_id,
+					      CMD_TEMPL_CFG_PROBE_REQ_2_4,
+					      skb->data, skb->len, 0, rate);
+	else
+		ret = cc33xx_cmd_template_set(wl, wlvif->role_id,
+					      CMD_TEMPL_CFG_PROBE_REQ_5,
+					      skb->data, skb->len, 0, rate);
+
+	if (ret < 0)
+		cc33xx_error("Unable to set ap probe request template.");
+
+out:
+	return skb;
+}
+
+int cc33xx_cmd_build_arp_rsp(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret, extra = 0;
+	u16 fc;
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	struct sk_buff *skb;
+	struct cc33xx_arp_rsp_template *tmpl;
+	struct ieee80211_hdr_3addr *hdr;
+	struct arphdr *arp_hdr;
+
+	skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) +
+			    CC33XX_EXTRA_SPACE_MAX);
+	if (!skb) {
+		cc33xx_error("failed to allocate buffer for arp rsp template");
+		return -ENOMEM;
+	}
+
+	skb_reserve(skb, sizeof(*hdr) + CC33XX_EXTRA_SPACE_MAX);
+
+	tmpl = skb_put_zero(skb, sizeof(*tmpl));
+
+	/* llc layer */
+	memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header));
+	tmpl->llc_type = cpu_to_be16(ETH_P_ARP);
+
+	/* arp header */
+	arp_hdr = &tmpl->arp_hdr;
+	arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
+	arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
+	arp_hdr->ar_hln = ETH_ALEN;
+	arp_hdr->ar_pln = 4;
+	arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
+
+	/* arp payload */
+	memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN);
+	tmpl->sender_ip = wlvif->ip_addr;
+
+	/* encryption space */
+	switch (wlvif->encryption_type) {
+	case KEY_TKIP:
+		if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+			extra = CC33XX_EXTRA_SPACE_TKIP;
+		break;
+	case KEY_AES:
+	case KEY_GCMP_256:
+		extra = CC33XX_EXTRA_SPACE_AES;
+		break;
+	case KEY_NONE:
+	case KEY_WEP:
+	case KEY_GEM:
+		extra = 0;
+		break;
+	default:
+		cc33xx_warning("Unknown encryption type: %d",
+			       wlvif->encryption_type);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (extra) {
+		u8 *space = skb_push(skb, extra);
+		memset(space, 0, extra);
+	}
+
+	/* QoS header - BE */
+	if (wlvif->sta.qos)
+		memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16));
+
+	/* mac80211 header */
+	hdr = skb_push(skb, sizeof(*hdr));
+	memset(hdr, 0, sizeof(*hdr));
+	fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS;
+	if (wlvif->sta.qos)
+		fc |= IEEE80211_STYPE_QOS_DATA;
+	else
+		fc |= IEEE80211_STYPE_DATA;
+	if (wlvif->encryption_type != KEY_NONE)
+		fc |= IEEE80211_FCTL_PROTECTED;
+
+	hdr->frame_control = cpu_to_le16(fc);
+	memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
+	memcpy(hdr->addr2, vif->addr, ETH_ALEN);
+	eth_broadcast_addr(hdr->addr3);
+
+	ret = cc33xx_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP,
+				      skb->data, skb->len, 0,
+				      wlvif->basic_rate);
+out:
+	dev_kfree_skb(skb);
+	return ret;
+}
+
+int cc33xx_build_qos_null_data(struct cc33xx *wl, struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct ieee80211_qos_hdr template;
+
+	memset(&template, 0, sizeof(template));
+
+	memcpy(template.addr1, vif->bss_conf.bssid, ETH_ALEN);
+	memcpy(template.addr2, vif->addr, ETH_ALEN);
+	memcpy(template.addr3, vif->bss_conf.bssid, ETH_ALEN);
+
+	template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+					     IEEE80211_STYPE_QOS_NULLFUNC |
+					     IEEE80211_FCTL_TODS);
+
+	/* FIXME: not sure what priority to use here */
+	template.qos_ctrl = cpu_to_le16(0);
+
+	return cc33xx_cmd_template_set(wl, wlvif->role_id,
+				       CMD_TEMPL_QOS_NULL_DATA, &template,
+				       sizeof(template), 0,
+				       wlvif->basic_rate);
+}
+
+int cc33xx_cmd_set_default_wep_key(struct cc33xx *wl, u8 id, u8 hlid)
+{
+	struct cc33xx_cmd_set_keys *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->hlid = hlid;
+	cmd->key_id = id;
+	cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
+	cmd->key_action = cpu_to_le16(KEY_SET_ID);
+	cmd->key_type = KEY_WEP;
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_warning("cmd set_default_wep_key failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(cmd);
+
+	return ret;
+}
+
+int cc33xx_cmd_set_sta_key(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       u16 action, u8 id, u8 key_type,
+		       u8 key_size, const u8 *key, const u8 *addr,
+		       u32 tx_seq_32, u16 tx_seq_16)
+{
+	struct cc33xx_cmd_set_keys *cmd;
+	int ret = 0;
+
+	/* hlid might have already been deleted */
+	if (wlvif->sta.hlid == CC33XX_INVALID_LINK_ID)
+		return 0;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->hlid = wlvif->sta.hlid;
+
+	if (key_type == KEY_WEP)
+		cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
+	else if (is_broadcast_ether_addr(addr))
+		cmd->lid_key_type = BROADCAST_LID_TYPE;
+	else
+		cmd->lid_key_type = UNICAST_LID_TYPE;
+
+	cmd->key_action = cpu_to_le16(action);
+	cmd->key_size = key_size;
+	cmd->key_type = key_type;
+
+	cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
+	cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
+
+	cmd->key_id = id;
+
+	if (key_type == KEY_TKIP) {
+		/*
+		 * We get the key in the following form:
+		 * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
+		 * but the target is expecting:
+		 * TKIP - RX MIC - TX MIC
+		 */
+		memcpy(cmd->key, key, 16);
+		memcpy(cmd->key + 16, key + 24, 8);
+		memcpy(cmd->key + 24, key + 16, 8);
+
+	} else {
+		memcpy(cmd->key, key, key_size);
+	}
+
+	cc33xx_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd));
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_warning("could not set keys");
+		goto out;
+	}
+
+out:
+	kfree(cmd);
+
+	return ret;
+}
+
+/*
+ * TODO: merge with sta/ibss into 1 set_key function.
+ * note there are slight diffs
+ */
+int cc33xx_cmd_set_ap_key(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  u16 action, u8 id, u8 key_type,
+			  u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
+			  u16 tx_seq_16)
+{
+	struct cc33xx_cmd_set_keys *cmd;
+	int ret = 0;
+	u8 lid_type;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	if (hlid == wlvif->ap.bcast_hlid) {
+		if (key_type == KEY_WEP)
+			lid_type = WEP_DEFAULT_LID_TYPE;
+		else
+			lid_type = BROADCAST_LID_TYPE;
+	} else {
+		lid_type = UNICAST_LID_TYPE;
+	}
+
+	cc33xx_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d"
+		     " hlid: %d", (int)action, (int)id, (int)lid_type,
+		     (int)key_type, (int)hlid);
+
+	cmd->lid_key_type = lid_type;
+	cmd->hlid = hlid;
+	cmd->key_action = cpu_to_le16(action);
+	cmd->key_size = key_size;
+	cmd->key_type = key_type;
+	cmd->key_id = id;
+	cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
+	cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
+
+	if (key_type == KEY_TKIP) {
+		/*
+		 * We get the key in the following form:
+		 * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
+		 * but the target is expecting:
+		 * TKIP - RX MIC - TX MIC
+		 */
+		memcpy(cmd->key, key, 16);
+		memcpy(cmd->key + 16, key + 24, 8);
+		memcpy(cmd->key + 24, key + 16, 8);
+	} else {
+		memcpy(cmd->key, key, key_size);
+	}
+
+	cc33xx_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd));
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_warning("could not set ap keys");
+		goto out;
+	}
+
+out:
+	kfree(cmd);
+	return ret;
+}
+
+int cc33xx_cmd_set_peer_state(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      u8 hlid)
+{
+	struct cc33xx_cmd_set_peer_state *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd set peer state (hlid=%d)", hlid);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->hlid = hlid;
+	cmd->state = CC33XX_CMD_STA_STATE_CONNECTED;
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_LINK_CONNECTION_STATE, cmd, sizeof(*cmd), 0);
+    if (ret < 0) {
+        cc33xx_error("failed to send set peer state command");
+        goto out_free;
+    }
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_add_peer(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			struct ieee80211_sta *sta, u8 *hlid, u8 is_connected)
+{
+	struct cc33xx_cmd_add_peer *cmd;
+
+	struct cc33xx_cmd_complete_add_peer *command_complete = 
+	          (struct cc33xx_cmd_complete_add_peer *)&wl->command_result;
+
+
+	int i, ret;
+	u32 sta_rates;
+
+	//cc33xx_debug(DEBUG_CMD, "cmd add peer %d", (int)hlid);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+
+	cc33xx_debug(DEBUG_CMD, "cmd add peer is ap %d", is_connected);
+	cmd->is_connected = is_connected;
+	cmd->role_id = wlvif->role_id;
+	cmd->role_type = CC33XX_ROLE_AP;
+	cmd->link_type = 1;
+
+	memcpy(cmd->addr, sta->addr, ETH_ALEN);
+	cmd->bss_index = CC33XX_AP_BSS_INDEX;
+	cmd->aid = cpu_to_le16(sta->aid);
+	cmd->sp_len = sta->max_sp;
+	cmd->wmm = sta->wme ? 1 : 0;
+	
+	for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
+		if (sta->wme && (sta->uapsd_queues & BIT(i)))
+			cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
+					CC33XX_PSD_UPSD_TRIGGER;
+		else
+			cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
+					CC33XX_PSD_LEGACY;
+
+
+	sta_rates = sta->deflink.supp_rates[wlvif->band];
+	if (sta->deflink.ht_cap.ht_supported)
+		sta_rates |=
+			(sta->deflink.ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
+			(sta->deflink.ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
+
+	cmd->supported_rates =
+		cpu_to_le32(cc33xx_tx_enabled_rates_get(wl, sta_rates,
+							wlvif->band));
+
+	if (!cmd->supported_rates) {
+		cc33xx_debug(DEBUG_CMD,
+			     "peer has no supported rates yet, configuring basic rates: 0x%x",
+			     wlvif->basic_rate_set);
+		cmd->supported_rates = cpu_to_le32(wlvif->basic_rate_set);
+	}
+
+	cc33xx_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
+		     cmd->supported_rates, sta->uapsd_queues);
+			
+	if(sta->deflink.ht_cap.ht_supported)
+	{
+		cmd->ht_capabilities = cpu_to_le32(sta->deflink.ht_cap.cap);
+		cmd->ht_capabilities|= cpu_to_le32(CC33XX_HT_CAP_HT_OPERATION);
+		cmd->ampdu_params = sta->deflink.ht_cap.ampdu_factor | sta->deflink.ht_cap.ampdu_density;
+
+	}
+	cmd->has_he= sta->deflink.he_cap.has_he;
+	cmd->mfp= sta->mfp;
+	ret = cc33xx_cmd_send(wl, CMD_ADD_PEER, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd add peer");
+		goto out_free;
+	}
+
+	if(NULL != hlid) {
+		if (command_complete->header.status == CMD_STATUS_SUCCESS) {
+			*hlid = command_complete->hlid;
+			wl->links[*hlid].allocated_pkts = 0;
+		 	wl->session_ids[*hlid] = command_complete->session_id;
+			cc33xx_debug(DEBUG_CMD, "new peer hlid=%d session_ids=%d",
+			command_complete->hlid, command_complete->session_id);
+		} else {
+			ret = -EMLINK;
+		}
+	} else {
+		cc33xx_debug(DEBUG_CMD, "update peer done !");
+	}
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_remove_peer(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   u8 hlid)
+{
+	struct cc33xx_cmd_remove_peer *cmd;
+	int ret;
+	bool timeout = false;
+
+	cc33xx_debug(DEBUG_CMD, "cmd remove peer %d", (int)hlid);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->hlid = hlid;
+	
+	cmd->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to initiate cmd remove peer");
+		goto out_free;
+	}
+
+	ret = cc33xx_wait_for_event(wl,
+		WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+		&timeout);
+
+	/*
+	 * We are ok with a timeout here. The event is sometimes not sent
+	 * due to a firmware bug. In case of another error (like SDIO timeout)
+	 * queue a recovery.
+	 */
+	if (ret < 0)
+		cc33xx_queue_recovery_work(wl);
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+static int wlcore_get_reg_conf_ch_idx(enum nl80211_band band, u16 ch)
+{
+	/*
+	 * map the given band/channel to the respective predefined
+	 * bit expected by the fw
+	 */
+	switch (band) {
+	case NL80211_BAND_2GHZ:
+		/* channels 1..14 are mapped to 0..13 */
+		if (ch >= 1 && ch <= 14)
+			return ch - 1;
+		break;
+	case NL80211_BAND_5GHZ:
+		switch (ch) {
+		case 8 ... 16:
+			/* channels 8,12,16 are mapped to 18,19,20 */
+			return 18 + (ch-8)/4;
+		case 34 ... 48:
+			/* channels 34,36..48 are mapped to 21..28 */
+			return 21 + (ch-34)/2;
+		case 52 ... 64:
+			/* channels 52,56..64 are mapped to 29..32 */
+			return 29 + (ch-52)/4;
+		case 100 ... 140:
+			/* channels 100,104..140 are mapped to 33..43 */
+			return 33 + (ch-100)/4;
+		case 149 ... 165:
+			/* channels 149,153..165 are mapped to 44..48 */
+			return 44 + (ch-149)/4;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	cc33xx_error("%s: unknown band/channel: %d/%d", __func__, band, ch);
+	return -1;
+}
+
+void wlcore_set_pending_regdomain_ch(struct cc33xx *wl, u16 channel,
+				     enum nl80211_band band)
+{
+	int ch_bit_idx = 0;
+
+	if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+		return;
+
+	ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel);
+
+	if (ch_bit_idx >= 0 && ch_bit_idx <= CC33XX_MAX_CHANNELS)
+		__set_bit_le(ch_bit_idx, (long *)wl->reg_ch_conf_pending);
+}
+
+int wlcore_cmd_regdomain_config_locked(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_regdomain_dfs_config *cmd = NULL;
+	int ret = 0, i, b, ch_bit_idx;
+	__le32 tmp_ch_bitmap[2] __aligned(sizeof(unsigned long));
+	struct wiphy *wiphy = wl->hw->wiphy;
+	struct ieee80211_supported_band *band;
+	bool timeout = false;
+	return 0;
+
+	if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+		return 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd reg domain config");
+
+	memcpy(tmp_ch_bitmap, wl->reg_ch_conf_pending, sizeof(tmp_ch_bitmap));
+
+	for (b = NL80211_BAND_2GHZ; b <= NL80211_BAND_5GHZ; b++) {
+		band = wiphy->bands[b];
+		for (i = 0; i < band->n_channels; i++) {
+			struct ieee80211_channel *channel = &band->channels[i];
+			u16 ch = channel->hw_value;
+			u32 flags = channel->flags;
+
+			if (flags & (IEEE80211_CHAN_DISABLED |
+				     IEEE80211_CHAN_NO_IR))
+				continue;
+
+			if ((flags & IEEE80211_CHAN_RADAR) &&
+			    channel->dfs_state != NL80211_DFS_AVAILABLE)
+				continue;
+
+			ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch);
+			if (ch_bit_idx < 0)
+				continue;
+
+			__set_bit_le(ch_bit_idx, (long *)tmp_ch_bitmap);
+		}
+	}
+
+	if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap)))
+		goto out;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->ch_bit_map1 = tmp_ch_bitmap[0];
+	cmd->ch_bit_map2 = tmp_ch_bitmap[1];
+	cmd->dfs_region = wl->dfs_region;
+
+	cc33xx_debug(DEBUG_CMD,
+		     "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
+		     cmd->ch_bit_map1, cmd->ch_bit_map2);
+
+	ret = cc33xx_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send reg domain dfs config");
+		goto out;
+	}
+
+	ret = cc33xx_wait_for_event(wl,
+		WLCORE_EVENT_DFS_CONFIG_COMPLETE,
+		&timeout);
+
+	if (ret < 0 || timeout) {
+		cc33xx_error("reg domain conf %serror",
+			     timeout ? "completion " : "");
+		ret = timeout ? -ETIMEDOUT : ret;
+		goto out;
+	}
+
+	memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap));
+	memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending));
+
+out:
+	kfree(cmd);
+	return ret;
+}
+
+int cc33xx_cmd_config_fwlog(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_config_fwlog *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd config firmware logger");
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->logger_mode = wl->conf.host_conf.fwlog.mode;
+	cmd->log_severity = wl->conf.host_conf.fwlog.severity;
+	cmd->timestamp = wl->conf.host_conf.fwlog.timestamp;
+	cmd->output = wl->conf.host_conf.fwlog.output;
+	cmd->threshold = wl->conf.host_conf.fwlog.threshold;
+
+	ret = cc33xx_cmd_send(wl, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send config firmware logger command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_start_fwlog(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_start_fwlog *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd start firmware logger");
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_send(wl, CMD_START_FWLOGGER, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send start firmware logger command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_cmd_stop_fwlog(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_stop_fwlog *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd stop firmware logger");
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_send(wl, CMD_STOP_FWLOGGER, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send stop firmware logger command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+static int cc33xx_cmd_roc(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  u8 role_id, enum nl80211_band band, u8 channel)
+{
+	struct cc33xx_cmd_roc *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);
+
+	if (WARN_ON(role_id == CC33XX_INVALID_ROLE_ID))
+		return -EINVAL;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->role_id = role_id;
+	cmd->channel = channel;
+	switch (band) {
+	case NL80211_BAND_2GHZ:
+		cmd->band = WLCORE_BAND_2_4GHZ;
+		break;
+	case NL80211_BAND_5GHZ:
+		cmd->band = WLCORE_BAND_5GHZ;
+		break;
+	default:
+		cc33xx_error("roc - unknown band: %d", (int)wlvif->band);
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+
+	ret = cc33xx_cmd_send(wl, CMD_REMAIN_ON_CHANNEL, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send ROC command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+static int cc33xx_cmd_croc(struct cc33xx *wl, u8 role_id)
+{
+	struct cc33xx_cmd_croc *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd croc (%d)", role_id);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	cmd->role_id = role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_CANCEL_REMAIN_ON_CHANNEL, cmd,
+			      sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send ROC command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+int cc33xx_roc(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 role_id,
+	       enum nl80211_band band, u8 channel)
+{
+	int ret = 0;
+
+	if (WARN_ON(test_bit(role_id, wl->roc_map)))
+		return 0;
+
+	ret = cc33xx_cmd_roc(wl, wlvif, role_id, band, channel);
+	if (ret < 0)
+		goto out;
+
+	__set_bit(role_id, wl->roc_map);
+out:
+	return ret;
+}
+
+int cc33xx_croc(struct cc33xx *wl, u8 role_id)
+{
+	int ret = 0;
+
+	if (WARN_ON(!test_bit(role_id, wl->roc_map)))
+		return 0;
+
+	ret = cc33xx_cmd_croc(wl, role_id);
+	if (ret < 0)
+		goto out;
+
+	__clear_bit(role_id, wl->roc_map);
+
+	/*
+	 * Rearm the tx watchdog when removing the last ROC. This prevents
+	 * recoveries due to just finished ROCs - when Tx hasn't yet had
+	 * a chance to get out.
+	 */
+	if (find_first_bit(wl->roc_map, CC33XX_MAX_ROLES) >= CC33XX_MAX_ROLES)
+		cc33xx_rearm_tx_watchdog_locked(wl);
+out:
+	return ret;
+}
+
+int cc33xx_cmd_stop_channel_switch(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_cmd_stop_channel_switch *cmd;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "cmd stop channel switch");
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to stop channel switch command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+
+out:
+	return ret;
+}
+
+/* start dev role and roc on its channel */
+int cc33xx_start_dev(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		     enum nl80211_band band, int channel)
+{
+	int ret;
+
+	if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
+		      wlvif->bss_type == BSS_TYPE_IBSS)))
+		return -EINVAL;
+
+	/* the dev role is already started for p2p mgmt interfaces */
+	
+	if (!wlcore_is_p2p_mgmt(wlvif)) {
+		ret = cc33xx_cmd_role_enable(wl,
+					     cc33xx_wlvif_to_vif(wlvif)->addr,
+					     CC33XX_ROLE_DEVICE,
+					     &wlvif->dev_role_id);
+		if (ret < 0)
+			goto out;
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd role start dev");
+	ret = cc33xx_cmd_role_start_dev(wl, wlvif, band, channel);
+	if (ret < 0)
+		goto out_disable;
+
+	cc33xx_debug(DEBUG_CMD, "cmd roc");
+	ret = cc33xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel);
+	if (ret < 0)
+		goto out_stop;
+
+	return 0;
+
+out_stop:
+	cc333xx_cmd_role_stop_dev(wl, wlvif);
+out_disable:
+	if (!wlcore_is_p2p_mgmt(wlvif))
+		cc33xx_cmd_role_disable(wl, &wlvif->dev_role_id);
+out:
+	return ret;
+}
+
+/* croc dev hlid, and stop the role */
+int cc33xx_stop_dev(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
+		      wlvif->bss_type == BSS_TYPE_IBSS)))
+		return -EINVAL;
+
+	/* flush all pending packets */
+	ret = wlcore_tx_work_locked(wl);
+	if (ret < 0)
+		goto out;
+
+	if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
+		ret = cc33xx_croc(wl, wlvif->dev_role_id);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = cc333xx_cmd_role_stop_dev(wl, wlvif);
+	if (ret < 0)
+		goto out;
+
+	if (!wlcore_is_p2p_mgmt(wlvif)) {
+		ret = cc33xx_cmd_role_disable(wl, &wlvif->dev_role_id);
+		if (ret < 0)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+int wlcore_cmd_generic_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   u8 feature, u8 enable, u8 value)
+{
+	struct wlcore_cmd_generic_cfg *cmd;
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD,
+		     "cmd generic cfg (role %d feature %d enable %d value %d)",
+		     wlvif->role_id, feature, enable, value);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->role_id = wlvif->role_id;
+	cmd->feature = feature;
+	cmd->enable = enable;
+	cmd->value = value;
+
+	ret = cc33xx_cmd_send(wl, CMD_GENERIC_CFG, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send generic cfg command");
+		goto out_free;
+	}
+out_free:
+	kfree(cmd);
+	return ret;
+}
+
+int cmd_channel_switch(struct cc33xx *wl,
+			      struct cc33xx_vif *wlvif,
+			      struct ieee80211_channel_switch *ch_switch)
+{
+	struct cmd_channel_switch *cmd;
+	u32 supported_rates;
+	int ret;
+
+	cc33xx_debug(DEBUG_ACX, "cmd channel switch (count=%d)",
+		     ch_switch->count);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cmd->role_id = wlvif->role_id;
+	cmd->channel = ch_switch->chandef.chan->hw_value;
+	cmd->switch_time = ch_switch->count;
+	cmd->stop_tx = ch_switch->block_tx;
+
+	switch (ch_switch->chandef.chan->band) {
+	case NL80211_BAND_2GHZ:
+		cmd->band = WLCORE_BAND_2_4GHZ;
+		break;
+	case NL80211_BAND_5GHZ:
+		cmd->band = WLCORE_BAND_5GHZ;
+		break;
+	default:
+		cc33xx_error("invalid channel switch band: %d",
+			     ch_switch->chandef.chan->band);
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES;
+	supported_rates |= wlvif->rate_set;
+	if (wlvif->p2p)
+		supported_rates &= ~CONF_TX_CCK_RATES;
+	cmd->local_supported_rates = cpu_to_le32(supported_rates);
+	cmd->channel_type = wlvif->channel_type;
+
+	ret = cc33xx_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send channel switch command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+out:
+	return ret;
+}
+
+int cmd_dfs_master_restart(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct cmd_dfs_master_restart *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd dfs master restart (role %d)",
+		     wlvif->role_id);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->role_id = wlvif->role_id;
+
+	ret = cc33xx_cmd_send(wl, CMD_DFS_MASTER_RESTART,
+			      cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send dfs master restart command");
+		goto out_free;
+	}
+out_free:
+	kfree(cmd);
+	return ret;
+}
+
+int cmd_set_cac(struct cc33xx *wl, struct cc33xx_vif *wlvif, bool start)
+{
+	struct cmd_cac_start *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd cac (channel %d) %s",
+		     wlvif->channel, start ? "start" : "stop");
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->role_id = wlvif->role_id;
+	cmd->channel = wlvif->channel;
+	if (wlvif->band == NL80211_BAND_5GHZ)
+		cmd->band = WLCORE_BAND_5GHZ;
+	cmd->bandwidth = wlcore_get_native_channel_type(wlvif->channel_type);
+
+	ret = cc33xx_cmd_send(wl,
+			      start ? CMD_CAC_START : CMD_CAC_STOP,
+			      cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send cac command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+	return ret;
+}
+
+int cmd_set_bd_addr(struct cc33xx *wl, u8 *bd_addr)
+{
+	struct cmd_set_bd_addr *cmd;
+	int ret = 0;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(cmd->bd_addr, bd_addr, sizeof cmd->bd_addr);
+
+	ret = cc33xx_cmd_send(wl, CMD_SET_BD_ADDR, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to set BD address");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+out:
+	return ret;
+}
+
+int cmd_get_device_info(struct cc33xx *wl, u8 *info_buffer, size_t buffer_len)
+{
+	struct cc33xx_cmd_get_device_info *cmd;
+	int ret = 0;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	ret = cc33xx_cmd_send(wl, CMD_BM_READ_DEVICE_INFO, cmd,
+			      sizeof(*cmd), sizeof(*cmd));
+	if (ret < 0) {
+		cc33xx_error("Device info command failure ");
+	} else {
+		WARN_ON(buffer_len > sizeof cmd->device_info);
+		memcpy(info_buffer, cmd->device_info, buffer_len);
+	}
+
+	kfree(cmd);
+
+	return ret;
+}
+
+int cmd_download_container_chunk(struct cc33xx *wl, u8 *chunk, size_t chunk_len, bool is_last_chunk)
+{
+	struct cc33xx_cmd_container_download *cmd;
+	const size_t command_size = sizeof(*cmd) + chunk_len;
+	int ret;
+	bool  is_sync_transfer = !is_last_chunk;
+
+	cmd = kzalloc(command_size, GFP_KERNEL);
+
+	if (!cmd) {
+		cc33xx_error("Chunk buffer allocation failure");
+		return -ENOMEM;
+	}
+
+	memcpy(cmd->payload, chunk, chunk_len);
+	cmd->length = cpu_to_le32(chunk_len);
+
+	if (is_last_chunk){
+		cc33xx_debug(DEBUG_BOOT, "Suspending IRQ while device reboots");
+		wlcore_disable_interrupts_nosync(wl);
+	}
+
+	ret = __wlcore_cmd_send(wl, CMD_CONTAINER_DOWNLOAD, cmd, 
+				command_size, sizeof (u32), is_sync_transfer);
+
+	kfree(cmd);
+
+	if (is_last_chunk){
+		msleep(CC33XX_REBOOT_TIMEOUT_MSEC);
+		cc33xx_debug(DEBUG_BOOT, "Resuming IRQ");
+		wlcore_enable_interrupts(wl);
+	}
+
+	return ret;
+}
diff --git drivers/net/wireless/ti/cc33xx/cmd.h drivers/net/wireless/ti/cc33xx/cmd.h
new file mode 100644
index 000000000000..000ed8c35938
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/cmd.h
@@ -0,0 +1,805 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __CMD_H__
+#define __CMD_H__
+
+#include "wlcore.h"
+
+struct acx_header;
+
+typedef enum {
+	INI_MAX_BUFFER_SIZE,
+	CMD_MAX_BUFFER_SIZE
+} BufferSize_e;
+
+int cc33xx_set_max_buffer_size(struct cc33xx *wl, BufferSize_e max_buffer_size);
+
+int cc33xx_cmd_send(struct cc33xx *wl, u16 id, void *buf, size_t len,
+		    size_t res_len);
+int cc33xx_cmd_role_enable(struct cc33xx *wl, u8 *addr, u8 role_type,
+			   u8 *role_id);
+int cc33xx_cmd_role_disable(struct cc33xx *wl, u8 *role_id);
+int cc33xx_cmd_role_start_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_stop_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_start_ap(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_stop_ap(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_start_ibss(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_start_dev(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		     enum nl80211_band band, int channel);
+int cc33xx_stop_dev(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_test(struct cc33xx *wl, void *buf, size_t buf_len, u8 answer);
+int cc33xx_cmd_interrogate(struct cc33xx *wl, u16 id, void *buf,
+			   size_t cmd_len, size_t res_len);
+int cc33xx_cmd_debug_inter(struct cc33xx *wl, u16 id, void *buf,
+						size_t cmd_len, size_t res_len);
+int cc33xx_cmd_configure(struct cc33xx *wl, u16 id, void *buf, size_t len);
+int cc33xx_cmd_debug(struct cc33xx *wl, u16 id, void *buf, size_t len);
+int wlcore_cmd_configure_failsafe(struct cc33xx *wl, u16 id, void *buf,
+				  size_t len, unsigned long valid_rets);
+int cc33xx_cmd_ps_mode(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       u8 ps_mode, u16 auto_ps_timeout);
+int cc33xx_cmd_template_set(struct cc33xx *wl, u8 role_id,
+			    u16 template_id, void *buf, size_t buf_len,
+			    int index, u32 rates);
+int cc33xx_cmd_build_null_data(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+struct sk_buff *cc33xx_cmd_build_ap_probe_req(struct cc33xx *wl,
+					      struct cc33xx_vif *wlvif,
+					      struct sk_buff *skb);
+int cc33xx_cmd_build_arp_rsp(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_build_qos_null_data(struct cc33xx *wl, struct ieee80211_vif *vif);
+int cc33xx_cmd_set_default_wep_key(struct cc33xx *wl, u8 id, u8 hlid);
+int cc33xx_cmd_set_sta_key(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   u16 action, u8 id, u8 key_type,
+			   u8 key_size, const u8 *key, const u8 *addr,
+			   u32 tx_seq_32, u16 tx_seq_16);
+int cc33xx_cmd_set_ap_key(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  u16 action, u8 id, u8 key_type,
+			  u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
+			  u16 tx_seq_16);
+int cc33xx_cmd_set_peer_state(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      u8 hlid);
+int cc33xx_roc(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 role_id,
+	       enum nl80211_band band, u8 channel);
+int cc33xx_croc(struct cc33xx *wl, u8 role_id);
+
+int cc33xx_cmd_add_peer(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			struct ieee80211_sta *sta, u8 *hlid, u8 is_connected);
+
+int cc33xx_cmd_remove_peer(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   u8 hlid);
+void wlcore_set_pending_regdomain_ch(struct cc33xx *wl, u16 channel,
+				     enum nl80211_band band);
+int wlcore_cmd_regdomain_config_locked(struct cc33xx *wl);
+int wlcore_cmd_generic_cfg(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			   u8 feature, u8 enable, u8 value);
+int cc33xx_cmd_config_fwlog(struct cc33xx *wl);
+int cc33xx_cmd_start_fwlog(struct cc33xx *wl);
+int cc33xx_cmd_stop_fwlog(struct cc33xx *wl);
+int cc33xx_cmd_stop_channel_switch(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif);
+
+int cc33xx_set_link(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 link);
+void cc33xx_clear_link(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 *hlid);
+
+u8 wlcore_get_native_channel_type(u8 nl_channel_type);
+int cc33xx_cmd_role_start_transceiver(struct cc33xx *wl, u8 role_id);
+int cc33xx_cmd_role_stop_transceiver(struct cc33xx *wl);
+int cc33xx_cmd_plt_enable(struct cc33xx *wl, u8 role_id);
+int cc33xx_cmd_plt_disable(struct cc33xx *wl);
+
+int cmd_channel_switch(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      struct ieee80211_channel_switch *ch_switch);
+int cmd_dfs_master_restart(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cmd_set_cac(struct cc33xx *wl, struct cc33xx_vif *wlvif, bool start);
+
+int cmd_set_bd_addr(struct cc33xx *wl, u8 *bd_addr);
+int cmd_get_device_info(struct cc33xx *wl, u8 *info_buffer, size_t buffer_len);
+int cmd_download_container_chunk(struct cc33xx *wl, u8 *chunk, size_t chunk_len, bool is_last_chunk);
+
+int cc33xx_count_role_set_bits(unsigned long role_map);
+
+enum cc33xx_cmd {
+	CMD_EMPTY,
+	CMD_SET_KEYS = 1,
+	CMD_SET_LINK_CONNECTION_STATE = 2,
+
+	CMD_CHANNEL_SWITCH = 3,
+	CMD_STOP_CHANNEL_SWICTH = 4,
+
+	CMD_REMAIN_ON_CHANNEL = 5,
+	CMD_CANCEL_REMAIN_ON_CHANNEL = 6,
+
+	CMD_START_DHCP_MGMT_SEQ = 7,
+	CMD_STOP_DHCP_MGMT_SEQ = 8,
+
+	CMD_START_SECURITY_MGMT_SEQ = 9,
+	CMD_STOP_SECURITY_MGMT_SEQ = 10,
+
+	CMD_START_ARP_MGMT_SEQ = 11,
+	CMD_STOP_ARP_MGMT_SEQ = 12,
+
+	CMD_START_DNS_MGMT_SEQ = 13,
+	CMD_STOP_DNS_MGMT_SEQ = 14,
+
+	/* Access point commands */
+	CMD_ADD_PEER = 15,
+	CMD_REMOVE_PEER = 16,
+
+	/* Role API */
+	CMD_ROLE_ENABLE = 17,
+	CMD_ROLE_DISABLE = 18,
+	CMD_ROLE_START = 19,
+	CMD_ROLE_STOP = 20,
+
+	CMD_AP_SET_BEACON_INFO = 21, /* Set AP beacon template */
+
+	// Managed sequence of sending deauth / disassoc frame
+	CMD_SEND_DEAUTH_DISASSOC = 22,
+
+	CMD_SCHED_STATE_EVENT = 23,
+	CMD_SCAN = 24,
+	CMD_STOP_SCAN = 25,
+	CMD_SET_PROBE_IE = 26,
+
+	CMD_CONFIGURE = 27,
+	CMD_INTERROGATE = 28,
+
+	CMD_DEBUG = 29,
+	CMD_DEBUG_READ = 30,
+
+	CMD_TEST_MODE = 31,
+	CMD_PLT_ENABLE = 32,
+	CMD_PLT_DISABLE = 33,
+	CMD_CONNECTION_SCAN_SSID_CFG = 34,
+	CMD_BM_READ_DEVICE_INFO = 35,
+	CMD_CONTAINER_DOWNLOAD = 36,
+	CMD_DOWNLOAD_INI_PARAMS = 37,
+	CMD_SET_BD_ADDR = 38,
+
+	CMD_LAST_SUPPORTED_COMMAND, // The following commands are legacy and are not yet supported
+
+	CMD_SET_PS_MODE,
+	CMD_SET_TEMPLATE,
+	CMD_DFS_CHANNEL_CONFIG,
+	CMD_CONFIG_FWLOGGER,
+	CMD_START_FWLOGGER,
+	CMD_STOP_FWLOGGER,
+	CMD_GENERIC_CFG,
+	CMD_DFS_MASTER_RESTART,
+	CMD_CAC_START,
+	CMD_CAC_STOP,
+	CMD_DFS_RADAR_DETECTION_DEBUG,
+
+	MAX_COMMAND_ID_CC33xx = 0x7FFF,
+};
+
+#define MAX_CMD_PARAMS 572
+
+enum cmd_templ {
+	CMD_TEMPL_NULL_DATA = 0,
+	CMD_TEMPL_BEACON,
+	CMD_TEMPL_CFG_PROBE_REQ_2_4,
+	CMD_TEMPL_CFG_PROBE_REQ_5,
+	CMD_TEMPL_PROBE_RESPONSE,
+	CMD_TEMPL_QOS_NULL_DATA,
+	CMD_TEMPL_PS_POLL,
+	CMD_TEMPL_DISCONNECT,
+	CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
+	CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
+	CMD_TEMPL_BAR,           /* for firmware internal use only */
+	CMD_TEMPL_CTS,           /*
+				  * For CTS-to-self (FastCTS) mechanism
+				  * for BT/WLAN coexistence (SoftGemini). */
+	CMD_TEMPL_AP_BEACON,
+	CMD_TEMPL_AP_PROBE_RESPONSE,
+	CMD_TEMPL_ARP_RSP,
+	CMD_TEMPL_DEAUTH_AP,
+	CMD_TEMPL_TEMPORARY,
+	CMD_TEMPL_LINK_MEASUREMENT_REPORT,
+	CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
+	CMD_TEMPL_PROBE_REQ_5_PERIODIC,
+
+	CMD_TEMPL_MAX = 0xff
+};
+
+/* unit ms */
+#define CC33XX_COMMAND_TIMEOUT     2000
+#define CC33XX_CMD_TEMPL_DFLT_SIZE 252
+#define CC33XX_CMD_TEMPL_MAX_SIZE  512
+#define CC33XX_EVENT_TIMEOUT       5000
+
+struct cc33xx_cmd_header {
+
+	struct NAB_header NAB_header;
+	__le16 id;
+	__le16 status;
+
+	/* payload */
+	u8 data[0];
+} __packed;
+
+#define CC33XX_CMD_MAX_PARAMS 572
+
+struct cc33xx_command {
+	struct cc33xx_cmd_header header;
+	u8  parameters[CC33XX_CMD_MAX_PARAMS];
+} __packed;
+
+enum {
+	CMD_MAILBOX_IDLE		=  0,
+	CMD_STATUS_SUCCESS		=  1,
+	CMD_STATUS_UNKNOWN_CMD		=  2,
+	CMD_STATUS_UNKNOWN_IE		=  3,
+	CMD_STATUS_REJECT_MEAS_SG_ACTIVE	= 11,
+	CMD_STATUS_RX_BUSY		= 13,
+	CMD_STATUS_INVALID_PARAM		= 14,
+	CMD_STATUS_TEMPLATE_TOO_LARGE		= 15,
+	CMD_STATUS_OUT_OF_MEMORY		= 16,
+	CMD_STATUS_STA_TABLE_FULL		= 17,
+	CMD_STATUS_RADIO_ERROR		= 18,
+	CMD_STATUS_WRONG_NESTING		= 19,
+	CMD_STATUS_TIMEOUT		= 21, /* Driver internal use.*/
+	CMD_STATUS_FW_RESET		= 22, /* Driver internal use.*/
+	CMD_STATUS_TEMPLATE_OOM		= 23,
+	CMD_STATUS_NO_RX_BA_SESSION	= 24,
+
+	MAX_COMMAND_STATUS
+};
+
+#define CMDMBOX_HEADER_LEN 4
+#define CMDMBOX_INFO_ELEM_HEADER_LEN 4
+
+enum {
+	BSS_TYPE_IBSS = 0,
+	BSS_TYPE_STA_BSS = 2,
+	BSS_TYPE_AP_BSS = 3,
+	MAX_BSS_TYPE = 0xFF
+};
+
+struct cc33xx_cmd_role_enable {
+	struct cc33xx_cmd_header header;
+
+	u8 role_type;
+	u8 mac_address[ETH_ALEN];
+	u8 padding;
+} __packed;
+
+
+
+struct command_complete_header {
+	__le16 id;
+	__le16 status;
+
+	/* payload */
+	u8 data[0];
+} __packed;
+
+
+
+struct cc33xx_cmd_complete_role_enable {
+    struct command_complete_header header;
+    u8 role_id;
+    u8 padding[3];
+} __packed;
+
+
+struct cc33xx_cmd_role_disable {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 padding[3];
+} __packed;
+
+enum wlcore_band {
+	WLCORE_BAND_2_4GHZ		= 0,
+	WLCORE_BAND_5GHZ		= 1,
+	WLCORE_BAND_JAPAN_4_9_GHZ	= 2,
+	WLCORE_BAND_DEFAULT		= WLCORE_BAND_2_4GHZ,
+	WLCORE_BAND_INVALID		= 0x7E,
+	WLCORE_BAND_MAX_RADIO		= 0x7F,
+};
+
+enum wlcore_channel_type {
+	WLCORE_CHAN_NO_HT,
+	WLCORE_CHAN_HT20,
+	WLCORE_CHAN_HT40MINUS,
+	WLCORE_CHAN_HT40PLUS
+};
+
+struct cc33xx_cmd_role_start {
+	struct cc33xx_cmd_header header;
+	u8 role_id;
+	u8 role_type;
+	u8 band;
+	u8 channel;
+
+	/* enum wlcore_channel_type */
+	u8 channel_type;
+
+	union {
+		struct {
+			u8 padding_1[54];
+		} __packed device;
+		/* sta & p2p_cli use the same struct */
+		struct {
+			u8 bssid[ETH_ALEN];
+
+			__le32 remote_rates; /* remote supported rates */
+
+			/*
+			 * The target uses this field to determine the rate at
+			 * which to transmit control frame responses (such as
+			 * ACK or CTS frames).
+			 */
+			__le32 basic_rate_set;
+			__le32 local_rates; /* local supported rates */
+
+			u8 ssid_type;
+			u8 ssid_len;
+			u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+			__le16 beacon_interval; /* in TBTTs */
+		} __packed sta;
+		struct {
+			u8 bssid[ETH_ALEN];
+			u8 hlid; /* data hlid */
+			u8 dtim_interval;
+			__le32 remote_rates; /* remote supported rates */
+
+			__le32 basic_rate_set;
+			__le32 local_rates; /* local supported rates */
+
+			u8 ssid_type;
+			u8 ssid_len;
+			u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+			__le16 beacon_interval; /* in TBTTs */
+
+			u8 padding_1[2];
+		} __packed ibss;
+		/* ap & p2p_go use the same struct */
+		struct {
+			__le16 beacon_interval; /* in TBTTs */
+
+			__le32 basic_rate_set;
+			__le32 local_rates; /* local supported rates */
+
+			u8 dtim_interval;
+			/*
+			 * ap supports wmm (note that there is additional
+			 * per-sta wmm configuration)
+			 */
+			u8 wmm;
+			u8 padding_1[42];
+		} __packed ap;
+	};
+	u8 padding;
+} __packed;
+
+struct cc33xx_cmd_complete_role_start {
+    struct command_complete_header header;
+    union {
+        struct {
+            u8 hlid;
+            u8 session;
+			u8 padding[2];
+        } __packed sta;
+        struct {
+            /* The host link id for the AP's global queue */
+            u8 global_hlid;
+            /* The host link id for the AP's broadcast queue */
+            u8 broadcast_hlid;
+            u8 bcast_session_id;
+            u8 global_session_id;
+        } __packed ap;
+    };
+} __packed;
+struct cc33xx_cmd_role_stop {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 padding[3];
+
+} __packed;
+
+struct cmd_enabledisable_path {
+	struct cc33xx_cmd_header header;
+
+	u8 channel;
+	u8 padding[3];
+} __packed;
+
+struct cc33xx_cmd_template_set {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 template_type;
+	__le16 len;
+	u8 index;  /* relevant only for KLV_TEMPLATE type */
+	u8 padding[3];
+
+	__le32 enabled_rates;
+	u8 short_retry_limit;
+	u8 long_retry_limit;
+	u8 aflags;
+	u8 reserved;
+
+	u8 template_data[CC33XX_CMD_TEMPL_MAX_SIZE];
+} __packed;
+
+#define TIM_ELE_ID    5
+#define PARTIAL_VBM_MAX    251
+
+enum cc33xx_cmd_ps_mode {
+	STATION_AUTO_PS_MODE,   /* Dynamic Power Save */
+	STATION_ACTIVE_MODE,
+	STATION_POWER_SAVE_MODE
+};
+
+struct cc33xx_cmd_ps_params {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 ps_mode; /* STATION_* */
+	__le16 auto_ps_timeout;
+} __packed;
+
+/* HW encryption keys */
+#define NUM_ACCESS_CATEGORIES_COPY 4
+
+enum cc33xx_cmd_key_action {
+	KEY_ADD_OR_REPLACE = 1,
+	KEY_REMOVE         = 2,
+	KEY_SET_ID         = 3,
+	MAX_KEY_ACTION     = 0xffff,
+};
+
+enum cc33xx_cmd_lid_key_type {
+	UNICAST_LID_TYPE     = 0,
+	BROADCAST_LID_TYPE   = 1,
+	WEP_DEFAULT_LID_TYPE = 2
+};
+
+enum cc33xx_cmd_key_type {
+	KEY_NONE = 0,
+	KEY_WEP  = 1,
+	KEY_TKIP = 2,
+	KEY_AES  = 3, //aes_ccmp_128
+	KEY_GEM  = 4,
+	KEY_IGTK = 5, //bip_cmac_128
+	KEY_CMAC_256 = 6, 
+	KEY_GMAC_128 = 7,
+	KEY_GMAC_256 = 8,
+	KEY_GCMP_256 = 9,
+	KEY_CCMP256 = 10,
+	KEY_GCMP128	= 11,
+};
+
+struct cc33xx_cmd_set_keys {
+	struct cc33xx_cmd_header header;
+
+	/*
+	 * Indicates whether the HLID is a unicast key set
+	 * or broadcast key set. A special value 0xFF is
+	 * used to indicate that the HLID is on WEP-default
+	 * (multi-hlids). of type cc33xx_cmd_lid_key_type.
+	 */
+	u8 hlid;
+
+	/*
+	 * In WEP-default network (hlid == 0xFF) used to
+	 * indicate which network STA/IBSS/AP role should be
+	 * changed
+	 */
+	u8 lid_key_type;
+
+	/*
+	 * Key ID - For TKIP and AES key types, this field
+	 * indicates the value that should be inserted into
+	 * the KeyID field of frames transmitted using this
+	 * key entry. For broadcast keys the index use as a
+	 * marker for TX/RX key.
+	 * For WEP default network (HLID=0xFF), this field
+	 * indicates the ID of the key to add or remove.
+	 */
+	u8 key_id;
+	u8 reserved_1;
+
+	/* key_action_e */
+	__le16 key_action;
+
+	/* key size in bytes */
+	u8 key_size;
+
+	/* key_type_e */
+	u8 key_type;
+
+	/* This field holds the security key data to add to the STA table */
+	u8 key[MAX_KEY_SIZE];
+	__le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
+	__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
+} __packed;
+
+struct cc33xx_cmd_test_header {
+	u8 id;
+	u8 padding[3];
+} __packed;
+
+#define CC33XX_CMD_STA_STATE_CONNECTED  1
+
+struct cc33xx_cmd_set_peer_state {
+	struct cc33xx_cmd_header header;
+
+	u8 hlid;
+	u8 state;
+	u8 padding[2];
+} __packed;
+
+struct cc33xx_cmd_roc {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 channel;
+	u8 band;
+	u8 padding;
+};
+
+struct cc33xx_cmd_croc {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 padding[3];
+};
+
+enum cc33xx_ssid_type {
+	CC33XX_SSID_TYPE_PUBLIC = 0,
+	CC33XX_SSID_TYPE_HIDDEN = 1,
+	CC33XX_SSID_TYPE_ANY = 2,
+};
+
+enum CC33XX_psd_type {
+	CC33XX_PSD_LEGACY = 0,
+	CC33XX_PSD_UPSD_TRIGGER = 1,
+	CC33XX_PSD_LEGACY_PSPOLL = 2,
+	CC33XX_PSD_SAPSD = 3
+};
+#define MAX_SIZE_BEACON_TEMP    (450)
+struct cc33xx_cmd_set_beacon_info
+{
+	struct cc33xx_cmd_header header;
+	
+    u8    	role_id;
+    __le16	beacon_len;
+    u8   	beacon[MAX_SIZE_BEACON_TEMP];
+	u8		padding[3];
+} __packed;
+
+struct cc33xx_cmd_add_peer {
+	struct cc33xx_cmd_header header;
+
+	u8 is_connected;
+	u8 role_id;
+	u8 role_type;
+	u8 link_type;
+	u8 addr[ETH_ALEN];
+	__le16 aid;
+	u8 psd_type[NUM_ACCESS_CATEGORIES_COPY];
+	__le32 supported_rates;
+	u8 bss_index;
+	u8 sp_len;
+	u8 wmm;
+    __le32 ht_capabilities;
+    u8  ampdu_params;
+
+    /* HE peer support */
+    bool has_he;
+	bool mfp;
+	u8 padding[2];
+} __packed;
+
+struct cc33xx_cmd_complete_add_peer {
+	struct command_complete_header header;
+	u8 hlid;
+	u8 session_id;
+} __packed;
+
+struct cc33xx_cmd_remove_peer {
+	struct cc33xx_cmd_header header;
+	u8 hlid;
+	u8 role_id;
+	u8 padding[2];
+	
+} __packed;
+
+/*
+ * Continuous mode - packets are transferred to the host periodically
+ * via the data path.
+ * On demand - Log messages are stored in a cyclic buffer in the
+ * firmware, and only transferred to the host when explicitly requested
+ */
+enum cc33xx_fwlogger_log_mode {
+	CC33XX_FWLOG_CONTINUOUS,
+};
+
+/* Include/exclude timestamps from the log messages */
+enum cc33xx_fwlogger_timestamp {
+	CC33XX_FWLOG_TIMESTAMP_DISABLED,
+	CC33XX_FWLOG_TIMESTAMP_ENABLED
+};
+
+/*
+ * Logs can be routed to the debug pinouts (where available), to the host bus
+ * (SDIO/SPI), or dropped
+ */
+enum cc33xx_fwlogger_output {
+	CC33XX_FWLOG_OUTPUT_NONE,
+	CC33XX_FWLOG_OUTPUT_DBG_PINS,
+	CC33XX_FWLOG_OUTPUT_HOST,
+};
+
+struct cc33xx_cmd_regdomain_dfs_config {
+	struct cc33xx_cmd_header header;
+
+	__le32 ch_bit_map1;
+	__le32 ch_bit_map2;
+	u8 dfs_region;
+	u8 padding[3];
+} __packed;
+
+enum wlcore_generic_cfg_feature {
+	WLCORE_CFG_FEATURE_RADAR_DEBUG = 2,
+};
+
+struct wlcore_cmd_generic_cfg {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 feature;
+	u8 enable;
+	u8 value;
+} __packed;
+
+struct cc33xx_cmd_config_fwlog {
+	struct cc33xx_cmd_header header;
+
+	/* See enum cc33xx_fwlogger_log_mode */
+	u8 logger_mode;
+
+	/* Minimum log level threshold */
+	u8 log_severity;
+
+	/* Include/exclude timestamps from the log messages */
+	u8 timestamp;
+
+	/* See enum cc33xx_fwlogger_output */
+	u8 output;
+
+	/* Regulates the frequency of log messages */
+	u8 threshold;
+
+	u8 padding[3];
+} __packed;
+
+struct cc33xx_cmd_start_fwlog {
+	struct cc33xx_cmd_header header;
+} __packed;
+
+struct cc33xx_cmd_stop_fwlog {
+	struct cc33xx_cmd_header header;
+} __packed;
+
+struct cc33xx_cmd_stop_channel_switch {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 padding[3];
+} __packed;
+
+/* Used to check radio status after calibration */
+#define MAX_TLV_LENGTH		500
+#define TEST_CMD_P2G_CAL	2	/* TX BiP */
+
+struct cc33xx_cmd_cal_p2g {
+	struct cc33xx_cmd_header header;
+
+	struct cc33xx_cmd_test_header test;
+
+	__le32 ver;
+	__le16 len;
+	u8 buf[MAX_TLV_LENGTH];
+	u8 type;
+	u8 padding;
+
+	__le16 radio_status;
+
+	u8 sub_band_mask;
+	u8 padding2;
+} __packed;
+
+struct cmd_channel_switch {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+
+	/* The new serving channel */
+	u8 channel;
+	/* Relative time of the serving channel switch in TBTT units */
+	u8 switch_time;
+	/* Stop the role TX, should expect it after radar detection */
+	u8 stop_tx;
+
+	__le32 local_supported_rates;
+
+	u8 channel_type;
+	u8 band;
+
+	u8 padding[2];
+} __packed;
+
+struct cmd_set_bd_addr {
+	struct cc33xx_cmd_header header;
+
+	u8 bd_addr[ETH_ALEN];
+	u8 padding[2];
+} __packed;
+
+struct cmd_dfs_master_restart {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 padding[3];
+} __packed;
+
+/* cac_start and cac_stop share the same params */
+struct cmd_cac_start {
+	struct cc33xx_cmd_header header;
+
+	u8 role_id;
+	u8 channel;
+	u8 band;
+	u8 bandwidth;
+} __packed;
+
+
+/* PLT structs */
+
+
+struct cc33xx_cmd_PLT_enable
+{
+	struct cc33xx_cmd_header header;
+	__le32 dummy;
+};
+
+struct cc33xx_cmd_PLT_disable
+{
+	struct cc33xx_cmd_header header;
+	__le32 dummy;
+};
+
+struct cc33xx_cmd_ini_params_download
+{
+	struct cc33xx_cmd_header header;
+	__le32 length;
+	u8 payload[0];
+}__packed;
+
+struct cc33xx_cmd_container_download {
+	struct cc33xx_cmd_header header;
+	__le32 length;
+	u8 payload[0];
+} __packed;
+
+struct cc33xx_cmd_get_device_info {
+	struct cc33xx_cmd_header header;
+	u8 device_info[700];
+} __packed;
+
+#endif /* __CC33XX_CMD_H__ */
diff --git drivers/net/wireless/ti/cc33xx/conf.h drivers/net/wireless/ti/cc33xx/conf.h
new file mode 100644
index 000000000000..abca395c95a2
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/conf.h
@@ -0,0 +1,1253 @@
+/*
+ * This file is part of CC33XX
+ *
+ * Copyright (C) 2023 Texas Instruments
+ *
+ * Author: Bashar Badir <bashar_badir@ti.com>
+ *
+ */
+
+#ifndef __CONF_H__
+#define __CONF_H__
+
+
+struct cc33xx_conf_header {
+	u32 magic;
+	u32 version;
+	u32 checksum;
+} __packed;
+
+#define CC33XX_CONF_MAGIC	0x10e100ca
+#define CC33XX_CONF_VERSION	0x01070047
+#define CC33XX_CONF_MASK	0x0000ffff
+#define CC33X_CONF_SIZE	(sizeof(struct cc33xx_conf_file))
+
+enum {
+	CONF_HW_BIT_RATE_1MBPS   = BIT(1),
+	CONF_HW_BIT_RATE_2MBPS   = BIT(2),
+	CONF_HW_BIT_RATE_5_5MBPS = BIT(3),
+	CONF_HW_BIT_RATE_11MBPS  = BIT(4),
+	CONF_HW_BIT_RATE_6MBPS   = BIT(5),
+	CONF_HW_BIT_RATE_9MBPS   = BIT(6),
+	CONF_HW_BIT_RATE_12MBPS  = BIT(7),
+	CONF_HW_BIT_RATE_18MBPS  = BIT(8),
+	CONF_HW_BIT_RATE_24MBPS  = BIT(9),
+	CONF_HW_BIT_RATE_36MBPS  = BIT(10),
+	CONF_HW_BIT_RATE_48MBPS  = BIT(11),
+	CONF_HW_BIT_RATE_54MBPS  = BIT(12),
+	CONF_HW_BIT_RATE_MCS_0   = BIT(13),
+	CONF_HW_BIT_RATE_MCS_1   = BIT(14),
+	CONF_HW_BIT_RATE_MCS_2   = BIT(15),
+	CONF_HW_BIT_RATE_MCS_3   = BIT(16),
+	CONF_HW_BIT_RATE_MCS_4   = BIT(17),
+	CONF_HW_BIT_RATE_MCS_5   = BIT(18),
+	CONF_HW_BIT_RATE_MCS_6   = BIT(19),
+	CONF_HW_BIT_RATE_MCS_7   = BIT(20)
+};
+
+enum {
+	CONF_HW_RATE_INDEX_1MBPS      = 1,
+	CONF_HW_RATE_INDEX_2MBPS      = 2,
+	CONF_HW_RATE_INDEX_5_5MBPS    = 3,
+	CONF_HW_RATE_INDEX_11MBPS     = 4,
+	CONF_HW_RATE_INDEX_6MBPS      = 5,
+	CONF_HW_RATE_INDEX_9MBPS      = 6,
+	CONF_HW_RATE_INDEX_12MBPS     = 7,
+	CONF_HW_RATE_INDEX_18MBPS     = 8,
+	CONF_HW_RATE_INDEX_24MBPS     = 9,
+	CONF_HW_RATE_INDEX_36MBPS     = 10,
+	CONF_HW_RATE_INDEX_48MBPS     = 11,
+	CONF_HW_RATE_INDEX_54MBPS     = 12,
+	CONF_HW_RATE_INDEX_MCS0       = 13,
+	CONF_HW_RATE_INDEX_MCS1       = 14,
+	CONF_HW_RATE_INDEX_MCS2       = 15,
+	CONF_HW_RATE_INDEX_MCS3       = 16,
+	CONF_HW_RATE_INDEX_MCS4       = 17,
+	CONF_HW_RATE_INDEX_MCS5       = 18,
+	CONF_HW_RATE_INDEX_MCS6       = 19,
+	CONF_HW_RATE_INDEX_MCS7       = 20,
+
+	CONF_HW_RATE_INDEX_MAX        = CONF_HW_RATE_INDEX_MCS7,
+};
+
+#define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff
+
+
+enum conf_rx_queue_type {
+	CONF_RX_QUEUE_TYPE_LOW_PRIORITY,  /* All except the high priority */
+	CONF_RX_QUEUE_TYPE_HIGH_PRIORITY, /* Management and voice packets */
+};
+
+struct cc33xx_clk_cfg {
+	u32 n;
+	u32 m;
+	u32 p;
+	u32 q;
+	u8 swallow;
+};
+
+struct conf_rx_settings {
+	/*
+	 * The maximum amount of time, in TU, before the
+	 * firmware discards the MSDU.
+	 *
+	 * Range: 0 - 0xFFFFFFFF
+	 */
+	u32 rx_msdu_life_time;
+
+	/*
+	 * Packet detection threshold in the PHY.
+	 *
+	 * FIXME: details unknown.
+	 */
+	u32 packet_detection_threshold;
+
+	/*
+	 * The longest time the STA will wait to receive traffic from the AP
+	 * after a PS-poll has been transmitted.
+	 *
+	 * Range: 0 - 200000
+	 */
+	u16 ps_poll_timeout;
+	/*
+	 * The longest time the STA will wait to receive traffic from the AP
+	 * after a frame has been sent from an UPSD enabled queue.
+	 *
+	 * Range: 0 - 200000
+	 */
+	u16 upsd_timeout;
+
+	/*
+	 * The number of octets in an MPDU, below which an RTS/CTS
+	 * handshake is not performed.
+	 *
+	 * Range: 0 - 4096
+	 */
+	u16 rts_threshold;
+
+	/*
+	 * The RX Clear Channel Assessment threshold in the PHY
+	 * (the energy threshold).
+	 *
+	 * Range: ENABLE_ENERGY_D  == 0x140A
+	 *        DISABLE_ENERGY_D == 0xFFEF
+	 */
+	u16 rx_cca_threshold;
+
+	/*
+	 * Occupied Rx mem-blocks number which requires interrupting the host
+	 * (0 = no buffering, 0xffff = disabled).
+	 *
+	 * Range: u16
+	 */
+	u16 irq_blk_threshold;
+
+	/*
+	 * Rx packets number which requires interrupting the host
+	 * (0 = no buffering).
+	 *
+	 * Range: u16
+	 */
+	u16 irq_pkt_threshold;
+
+	/*
+	 * Max time in msec the FW may delay RX-Complete interrupt.
+	 *
+	 * Range: 1 - 100
+	 */
+	u16 irq_timeout;
+
+	/*
+	 * The RX queue type.
+	 *
+	 * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY,
+	 */
+	u8 queue_type;
+} __packed;
+
+#define CONF_TX_MAX_RATE_CLASSES       10
+
+#define CONF_TX_RATE_MASK_UNSPECIFIED  0
+#define CONF_TX_RATE_MASK_BASIC        (CONF_HW_BIT_RATE_1MBPS | \
+					CONF_HW_BIT_RATE_2MBPS)
+#define CONF_TX_RATE_RETRY_LIMIT       10
+
+/* basic rates for p2p operations (probe req/resp, etc.) */
+#define CONF_TX_RATE_MASK_BASIC_P2P    CONF_HW_BIT_RATE_6MBPS
+
+/*
+ * Rates supported for data packets when operating as STA/AP. Note the absence
+ * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
+ * one. The rate dropped is not mandatory under any operating mode.
+ */
+#define CONF_TX_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS |    \
+	CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS |      \
+	CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS |        \
+	CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS |      \
+	CONF_HW_BIT_RATE_18MBPS | CONF_HW_BIT_RATE_24MBPS |      \
+	CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS |      \
+	CONF_HW_BIT_RATE_54MBPS)
+
+#define CONF_TX_CCK_RATES  (CONF_HW_BIT_RATE_1MBPS |		\
+	CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS |	\
+	CONF_HW_BIT_RATE_11MBPS)
+
+#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS |             \
+	CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS |      \
+	CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS |      \
+	CONF_HW_BIT_RATE_54MBPS)
+
+#define CONF_TX_MCS_RATES (CONF_HW_BIT_RATE_MCS_0 |              \
+	CONF_HW_BIT_RATE_MCS_1 | CONF_HW_BIT_RATE_MCS_2 |        \
+	CONF_HW_BIT_RATE_MCS_3 | CONF_HW_BIT_RATE_MCS_4 |        \
+	CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 |        \
+	CONF_HW_BIT_RATE_MCS_7)
+
+
+/*
+ * Default rates for management traffic when operating in AP mode. This
+ * should be configured according to the basic rate set of the AP
+ */
+#define CONF_TX_AP_DEFAULT_MGMT_RATES  (CONF_HW_BIT_RATE_1MBPS | \
+	CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS)
+
+/* default rates for working as IBSS (11b and OFDM) */
+#define CONF_TX_IBSS_DEFAULT_RATES  (CONF_HW_BIT_RATE_1MBPS |       \
+		CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \
+		CONF_HW_BIT_RATE_11MBPS | CONF_TX_OFDM_RATES);
+
+struct conf_tx_rate_class {
+
+	/*
+	 * The rates enabled for this rate class.
+	 *
+	 * Range: CONF_HW_BIT_RATE_* bit mask
+	 */
+	u32 enabled_rates;
+
+	/*
+	 * The dot11 short retry limit used for TX retries.
+	 *
+	 * Range: u8
+	 */
+	u8 short_retry_limit;
+
+	/*
+	 * The dot11 long retry limit used for TX retries.
+	 *
+	 * Range: u8
+	 */
+	u8 long_retry_limit;
+
+	/*
+	 * Flags controlling the attributes of TX transmission.
+	 *
+	 * Range: bit 0: Truncate - when set, FW attempts to send a frame stop
+	 *               when the total valid per-rate attempts have
+	 *               been exhausted; otherwise transmissions
+	 *               will continue at the lowest available rate
+	 *               until the appropriate one of the
+	 *               short_retry_limit, long_retry_limit,
+	 *               dot11_max_transmit_msdu_life_time, or
+	 *               max_tx_life_time, is exhausted.
+	 *            1: Preamble Override - indicates if the preamble type
+	 *               should be used in TX.
+	 *            2: Preamble Type - the type of the preamble to be used by
+	 *               the policy (0 - long preamble, 1 - short preamble.
+	 */
+	u8 aflags;
+} __packed;
+
+#define CONF_TX_MAX_AC_COUNT 4
+
+/* Slot number setting to start transmission at PIFS interval */
+#define CONF_TX_AIFS_PIFS 1
+/* Slot number setting to start transmission at DIFS interval normal
+ * DCF access */
+#define CONF_TX_AIFS_DIFS 2
+
+
+enum conf_tx_ac {
+	CONF_TX_AC_BE = 0,         /* best effort / legacy */
+	CONF_TX_AC_BK = 1,         /* background */
+	CONF_TX_AC_VI = 2,         /* video */
+	CONF_TX_AC_VO = 3,         /* voice */
+	CONF_TX_AC_CTS2SELF = 4,   /* fictitious AC, follows AC_VO */
+	CONF_TX_AC_ANY_TID = 0xff
+};
+
+struct conf_sig_weights {
+
+	/*
+	 * RSSI from beacons average weight.
+	 *
+	 * Range: u8
+	 */
+	u8 rssi_bcn_avg_weight;
+
+	/*
+	 * RSSI from data average weight.
+	 *
+	 * Range: u8
+	 */
+	u8 rssi_pkt_avg_weight;
+
+	/*
+	 * SNR from beacons average weight.
+	 *
+	 * Range: u8
+	 */
+	u8 snr_bcn_avg_weight;
+
+	/*
+	 * SNR from data average weight.
+	 *
+	 * Range: u8
+	 */
+	u8 snr_pkt_avg_weight;
+} __packed;
+
+
+struct conf_tx_ac_category {
+	/*
+	 * The AC class identifier.
+	 *
+	 * Range: enum conf_tx_ac
+	 */
+	u8 ac;
+
+	/*
+	 * The contention window minimum size (in slots) for the access
+	 * class.
+	 *
+	 * Range: u8
+	 */
+	u8 cw_min;
+
+	/*
+	 * The contention window maximum size (in slots) for the access
+	 * class.
+	 *
+	 * Range: u8
+	 */
+	u16 cw_max;
+
+	/*
+	 * The AIF value (in slots) for the access class.
+	 *
+	 * Range: u8
+	 */
+	u8 aifsn;
+
+	/*
+	 * The TX Op Limit (in microseconds) for the access class.
+	 *
+	 * Range: u16
+	 */
+	u16 tx_op_limit;
+	
+	/*
+	* Is the MU EDCA configured
+	*
+	* Range: u8
+	*/
+	u8 is_mu_edca;
+
+	/*
+	*  The AIFSN value for the corresonding access class 
+	*
+	* Range: u8
+	*/
+	u8 mu_edca_aifs;
+
+	/*
+	* The ECWmin and ECWmax value is indicating contention window maximum 
+	* size (in slots) for the access
+	*
+	* Range: u8
+	*/
+	u8 mu_edca_ecw_min_max;
+
+	/*
+	* The MU EDCA timer (in microseconds) obtaining an EDCA TXOP
+	* for STA using MU EDCA parameters
+	*
+	* Range: u8
+	*/
+	u8 mu_edca_timer;
+} __packed;
+
+#define CONF_TX_MAX_TID_COUNT 8
+
+/* Allow TX BA on all TIDs but 6,7. These are currently reserved in the FW */
+#define CONF_TX_BA_ENABLED_TID_BITMAP 0x3F
+
+enum {
+	CONF_CHANNEL_TYPE_DCF = 0,   /* DC/LEGACY*/
+	CONF_CHANNEL_TYPE_EDCF = 1,  /* EDCA*/
+	CONF_CHANNEL_TYPE_HCCA = 2,  /* HCCA*/
+};
+
+enum {
+	CONF_PS_SCHEME_LEGACY = 0,
+	CONF_PS_SCHEME_UPSD_TRIGGER = 1,
+	CONF_PS_SCHEME_LEGACY_PSPOLL = 2,
+	CONF_PS_SCHEME_SAPSD = 3,
+};
+
+enum {
+	CONF_ACK_POLICY_LEGACY = 0,
+	CONF_ACK_POLICY_NO_ACK = 1,
+	CONF_ACK_POLICY_BLOCK = 2,
+};
+
+
+struct conf_tx_tid {
+	u8 queue_id;
+	u8 channel_type;
+	u8 tsid;
+	u8 ps_scheme;
+	u8 ack_policy;
+	u32 apsd_conf[2];
+} __packed;
+
+struct conf_tx_settings {
+	/*
+	 * The TX ED value for TELEC Enable/Disable.
+	 *
+	 * Range: 0, 1
+	 */
+	u8 tx_energy_detection;
+
+	/*
+	 * Configuration for rate classes for TX (currently only one
+	 * rate class supported). Used in non-AP mode.
+	 */
+	struct conf_tx_rate_class sta_rc_conf;
+
+	/*
+	 * Configuration for access categories for TX rate control.
+	 */
+	u8 ac_conf_count;
+	/*struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT];*/
+	struct conf_tx_ac_category ac_conf0;
+	struct conf_tx_ac_category ac_conf1;
+	struct conf_tx_ac_category ac_conf2;
+	struct conf_tx_ac_category ac_conf3;
+
+	/*
+	 * AP-mode - allow this number of TX retries to a station before an
+	 * event is triggered from FW.
+	 * In AP-mode the hlids of unreachable stations are given in the
+	 * "sta_tx_retry_exceeded" member in the event mailbox.
+	 */
+	u8 max_tx_retries;
+
+	/*
+	 * AP-mode - after this number of seconds a connected station is
+	 * considered inactive.
+	 */
+	u16 ap_aging_period;
+
+	/*
+	 * Configuration for TID parameters.
+	 */
+	u8 tid_conf_count;
+	/* struct conf_tx_tid tid_conf[]; */
+	struct conf_tx_tid tid_conf0;
+	struct conf_tx_tid tid_conf1;
+	struct conf_tx_tid tid_conf2;
+	struct conf_tx_tid tid_conf3;
+	struct conf_tx_tid tid_conf4;
+	struct conf_tx_tid tid_conf5;
+	struct conf_tx_tid tid_conf6;
+	struct conf_tx_tid tid_conf7;
+
+	/*
+	 * The TX fragmentation threshold.
+	 *
+	 * Range: u16
+	 */
+	u16 frag_threshold;
+
+	/*
+	 * Max time in msec the FW may delay frame TX-Complete interrupt.
+	 *
+	 * Range: u16
+	 */
+	u16 tx_compl_timeout;
+
+	/*
+	 * Completed TX packet count which requires to issue the TX-Complete
+	 * interrupt.
+	 *
+	 * Range: u16
+	 */
+	u16 tx_compl_threshold;
+
+	/*
+	 * The rate used for control messages and scanning on the 2.4GHz band
+	 *
+	 * Range: CONF_HW_BIT_RATE_* bit mask
+	 */
+	u32 basic_rate;
+
+	/*
+	 * The rate used for control messages and scanning on the 5GHz band
+	 *
+	 * Range: CONF_HW_BIT_RATE_* bit mask
+	 */
+	u32 basic_rate_5;
+
+	/*
+	 * TX retry limits for templates
+	 */
+	u8 tmpl_short_retry_limit;
+	u8 tmpl_long_retry_limit;
+
+	/* Time in ms for Tx watchdog timer to expire */
+	u32 tx_watchdog_timeout;
+
+	/*
+	 * when a slow link has this much packets pending, it becomes a low
+	 * priority link, scheduling-wise
+	 */
+	u8 slow_link_thold;
+
+	/*
+	 * when a fast link has this much packets pending, it becomes a low
+	 * priority link, scheduling-wise
+	 */
+	u8 fast_link_thold;
+} __packed;
+
+enum {
+	CONF_WAKE_UP_EVENT_BEACON    = 0x00, /* Wake on every Beacon */
+	CONF_WAKE_UP_EVENT_DTIM      = 0x01, /* Wake on every DTIM */
+	CONF_WAKE_UP_EVENT_N_DTIM    = 0x02, /* Wake every Nth DTIM */
+	CONF_WAKE_UP_EVENT_LIMIT     = CONF_WAKE_UP_EVENT_N_DTIM,
+	/* Not supported: */
+	CONF_WAKE_UP_EVENT_N_BEACONS = 0x03, /* Wake every Nth beacon */
+	CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F
+};
+
+#define CONF_MAX_BCN_FILT_IE_COUNT 32
+
+#define CONF_BCN_RULE_PASS_ON_CHANGE         BIT(0)
+#define CONF_BCN_RULE_PASS_ON_APPEARANCE     BIT(1)
+
+#define CONF_BCN_IE_OUI_LEN    3
+#define CONF_BCN_IE_VER_LEN    2
+
+struct conf_bcn_filt_rule {
+	/*
+	 * IE number to which to associate a rule.
+	 *
+	 * Range: u8
+	 */
+	u8 ie;
+
+	/*
+	 * Rule to associate with the specific ie.
+	 *
+	 * Range: CONF_BCN_RULE_PASS_ON_*
+	 */
+	u8 rule;
+
+	/*
+	 * OUI for the vendor specifie IE (221)
+	 */
+	u8 oui[3];
+
+	/*
+	 * Type for the vendor specifie IE (221)
+	 */
+	u8 type;
+
+	/*
+	 * Version for the vendor specifie IE (221)
+	 */
+	u8 version[2];
+} __packed;
+
+
+enum conf_bcn_filt_mode {
+	CONF_BCN_FILT_MODE_DISABLED = 0,
+	CONF_BCN_FILT_MODE_ENABLED = 1
+};
+
+enum conf_bet_mode {
+	CONF_BET_MODE_DISABLE = 0,
+	CONF_BET_MODE_ENABLE = 1,
+};
+
+struct conf_conn_settings {
+	/*
+	 * Enable or disable the beacon filtering.
+	 *
+	 * Range: CONF_BCN_FILT_MODE_*
+	 */
+	u8 bcn_filt_mode;
+
+	/*
+	 * Configure Beacon filter pass-thru rules.
+	 */
+	u8 bcn_filt_ie_count;
+	/*struct conf_bcn_filt_rule bcn_filt_ie[CONF_MAX_BCN_FILT_IE_COUNT];*/
+	/* struct conf_bcn_filt_rule bcn_filt_ie[32]; */
+	struct conf_bcn_filt_rule bcn_filt_ie0;
+	struct conf_bcn_filt_rule bcn_filt_ie1;
+	struct conf_bcn_filt_rule bcn_filt_ie2;
+	struct conf_bcn_filt_rule bcn_filt_ie3;
+	struct conf_bcn_filt_rule bcn_filt_ie4;
+	struct conf_bcn_filt_rule bcn_filt_ie5;
+	struct conf_bcn_filt_rule bcn_filt_ie6;
+	struct conf_bcn_filt_rule bcn_filt_ie7;
+	struct conf_bcn_filt_rule bcn_filt_ie8;
+	struct conf_bcn_filt_rule bcn_filt_ie9;
+	struct conf_bcn_filt_rule bcn_filt_ie10;
+	struct conf_bcn_filt_rule bcn_filt_ie11;
+	struct conf_bcn_filt_rule bcn_filt_ie12;
+	struct conf_bcn_filt_rule bcn_filt_ie13;
+	struct conf_bcn_filt_rule bcn_filt_ie14;
+	struct conf_bcn_filt_rule bcn_filt_ie15;
+	struct conf_bcn_filt_rule bcn_filt_ie16;
+	struct conf_bcn_filt_rule bcn_filt_ie17;
+	struct conf_bcn_filt_rule bcn_filt_ie18;
+	struct conf_bcn_filt_rule bcn_filt_ie19;
+	struct conf_bcn_filt_rule bcn_filt_ie20;
+	struct conf_bcn_filt_rule bcn_filt_ie21;
+	struct conf_bcn_filt_rule bcn_filt_ie22;
+	struct conf_bcn_filt_rule bcn_filt_ie23;
+	struct conf_bcn_filt_rule bcn_filt_ie24;
+	struct conf_bcn_filt_rule bcn_filt_ie25;
+	struct conf_bcn_filt_rule bcn_filt_ie26;
+	struct conf_bcn_filt_rule bcn_filt_ie27;
+	struct conf_bcn_filt_rule bcn_filt_ie28;
+	struct conf_bcn_filt_rule bcn_filt_ie29;
+	struct conf_bcn_filt_rule bcn_filt_ie30;
+	struct conf_bcn_filt_rule bcn_filt_ie31;
+
+	/*
+	 * The number of consecutive beacons to lose, before the firmware
+	 * becomes out of synch.
+	 *
+	 * Range: u32
+	 */
+	u32 synch_fail_thold;
+
+	/*
+	 * After out-of-synch, the number of TU's to wait without a further
+	 * received beacon (or probe response) before issuing the BSS_EVENT_LOSE
+	 * event.
+	 *
+	 * Range: u32
+	 */
+	u32 bss_lose_timeout;
+
+	/*
+	 * Beacon receive timeout.
+	 *
+	 * Range: u32
+	 */
+	u32 beacon_rx_timeout;
+
+	/*
+	 * Broadcast receive timeout.
+	 *
+	 * Range: u32
+	 */
+	u32 broadcast_timeout;
+
+	/*
+	 * Enable/disable reception of broadcast packets in power save mode
+	 *
+	 * Range: 1 - enable, 0 - disable
+	 */
+	u8 rx_broadcast_in_ps;
+
+	/*
+	 * Consecutive PS Poll failures before sending event to driver
+	 *
+	 * Range: u8
+	 */
+	u8 ps_poll_threshold;
+
+	/*
+	 * Configuration of signal average weights.
+	 */
+	struct conf_sig_weights sig_weights;
+
+	/*
+	 * Specifies if beacon early termination procedure is enabled or
+	 * disabled.
+	 *
+	 * Range: CONF_BET_MODE_*
+	 */
+	u8 bet_enable;
+
+	/*
+	 * Specifies the maximum number of consecutive beacons that may be
+	 * early terminated. After this number is reached at least one full
+	 * beacon must be correctly received in FW before beacon ET
+	 * resumes.
+	 *
+	 * Range 0 - 255
+	 */
+	u8 bet_max_consecutive;
+
+	/*
+	 * Specifies the maximum number of times to try PSM entry if it fails
+	 * (if sending the appropriate null-func message fails.)
+	 *
+	 * Range 0 - 255
+	 */
+	u8 psm_entry_retries;
+
+	/*
+	 * Specifies the maximum number of times to try PSM exit if it fails
+	 * (if sending the appropriate null-func message fails.)
+	 *
+	 * Range 0 - 255
+	 */
+	u8 psm_exit_retries;
+
+	/*
+	 * Specifies the maximum number of times to try transmit the PSM entry
+	 * null-func frame for each PSM entry attempt
+	 *
+	 * Range 0 - 255
+	 */
+	u8 psm_entry_nullfunc_retries;
+
+	/*
+	 * Specifies the dynamic PS timeout in ms that will be used
+	 * by the FW when in AUTO_PS mode
+	 */
+	u16 dynamic_ps_timeout;
+
+	/*
+	 * Specifies whether dynamic PS should be disabled and PSM forced.
+	 * This is required for certain WiFi certification tests.
+	 */
+	u8 forced_ps;
+
+	/*
+	 *
+	 * Specifies the interval of the connection keep-alive null-func
+	 * frame in ms.
+	 *
+	 * Range: 1000 - 3600000
+	 */
+	u32 keep_alive_interval;
+
+	/*
+	 * Maximum listen interval supported by the driver in units of beacons.
+	 *
+	 * Range: u16
+	 */
+	u8 max_listen_interval;
+
+	/*
+	 * Default sleep authorization for a new STA interface. This determines
+	 * whether we can go to ELP.
+	 */
+	u8 sta_sleep_auth;
+
+	/*
+	 * Default RX BA Activity filter configuration
+	 */
+	u8 suspend_rx_ba_activity;
+} __packed;
+
+
+struct conf_itrim_settings {
+	/* enable dco itrim */
+	u8 enable;
+
+	/* moderation timeout in microsecs from the last TX */
+	u32 timeout;
+} __packed;
+
+enum conf_fast_wakeup {
+	CONF_FAST_WAKEUP_ENABLE,
+	CONF_FAST_WAKEUP_DISABLE,
+};
+
+struct conf_pm_config_settings {
+	/*
+	 * Host clock settling time
+	 *
+	 * Range: 0 - 30000 us
+	 */
+	u32 host_clk_settling_time;
+
+	/*
+	 * Host fast wakeup support
+	 *
+	 * Range: enum conf_fast_wakeup
+	 */
+	u8 host_fast_wakeup_support;
+} __packed;
+
+struct conf_roam_trigger_settings {
+	/*
+	 * The minimum interval between two trigger events.
+	 *
+	 * Range: 0 - 60000 ms
+	 */
+	u16 trigger_pacing;
+
+	/*
+	 * The weight for rssi/beacon average calculation
+	 *
+	 * Range: 0 - 255
+	 */
+	u8 avg_weight_rssi_beacon;
+
+	/*
+	 * The weight for rssi/data frame average calculation
+	 *
+	 * Range: 0 - 255
+	 */
+	u8 avg_weight_rssi_data;
+
+	/*
+	 * The weight for snr/beacon average calculation
+	 *
+	 * Range: 0 - 255
+	 */
+	u8 avg_weight_snr_beacon;
+
+	/*
+	 * The weight for snr/data frame average calculation
+	 *
+	 * Range: 0 - 255
+	 */
+	u8 avg_weight_snr_data;
+} __packed;
+
+struct conf_scan_settings {
+	/*
+	 * The minimum time to wait on each channel for active scans
+	 * This value will be used whenever there's a connected interface.
+	 *
+	 * Range: u32 tu/1000
+	 */
+	u32 min_dwell_time_active;
+
+	/*
+	 * The maximum time to wait on each channel for active scans
+	 * This value will be currently used whenever there's a
+	 * connected interface. It shouldn't exceed 30000 (~30ms) to avoid
+	 * possible interference of voip traffic going on while scanning.
+	 *
+	 * Range: u32 tu/1000
+	 */
+	u32 max_dwell_time_active;
+
+	/* The minimum time to wait on each channel for active scans
+	 * when it's possible to have longer scan dwell times.
+	 * Currently this is used whenever we're idle on all interfaces.
+	 * Longer dwell times improve detection of networks within a
+	 * single scan.
+	 *
+	 * Range: u32 tu/1000
+	 */
+	u32 min_dwell_time_active_long;
+
+	/* The maximum time to wait on each channel for active scans
+	 * when it's possible to have longer scan dwell times.
+	 * See min_dwell_time_active_long
+	 *
+	 * Range: u32 tu/1000
+	 */
+	u32 max_dwell_time_active_long;
+
+	/* time to wait on the channel for passive scans (in TU/1000) */
+	u32 dwell_time_passive;
+
+	/* time to wait on the channel for DFS scans (in TU/1000) */
+	u32 dwell_time_dfs;
+
+	/*
+	 * Number of probe requests to transmit on each active scan channel
+	 *
+	 * Range: u8
+	 */
+	u16 num_probe_reqs;
+
+	/*
+	 * Scan trigger (split scan) timeout. The FW will split the scan
+	 * operation into slices of the given time and allow the FW to schedule
+	 * other tasks in between.
+	 *
+	 * Range: u32 Microsecs
+	 */
+	u32 split_scan_timeout;
+} __packed;
+
+struct conf_sched_scan_settings {
+	/*
+	 * The base time to wait on the channel for active scans (in TU/1000).
+	 * The minimum dwell time is calculated according to this:
+	 * min_dwell_time = base + num_of_probes_to_be_sent * delta_per_probe
+	 * The maximum dwell time is calculated according to this:
+	 * max_dwell_time = min_dwell_time + max_dwell_time_delta
+	 */
+	u32 base_dwell_time;
+
+	/* The delta between the min dwell time and max dwell time for
+	 * active scans (in TU/1000s). The max dwell time is used by the FW once
+	 * traffic is detected on the channel.
+	 */
+	u32 max_dwell_time_delta;
+
+	/* Delta added to min dwell time per each probe in 2.4 GHz (TU/1000) */
+	u32 dwell_time_delta_per_probe;
+
+	/* Delta added to min dwell time per each probe in 5 GHz (TU/1000) */
+	u32 dwell_time_delta_per_probe_5;
+
+	/* time to wait on the channel for passive scans (in TU/1000) */
+	u32 dwell_time_passive;
+
+	/* time to wait on the channel for DFS scans (in TU/1000) */
+	u32 dwell_time_dfs;
+
+	/* number of probe requests to send on each channel in active scans */
+	u8 num_probe_reqs;
+
+	/* RSSI threshold to be used for filtering */
+	s8 rssi_threshold;
+
+	/* SNR threshold to be used for filtering */
+	s8 snr_threshold;
+
+	/*
+	 * number of short intervals scheduled scan cycles before
+	 * switching to long intervals
+	 */
+	u8 num_short_intervals;
+
+	/* interval between each long scheduled scan cycle (in ms) */
+	u16 long_interval;
+} __packed;
+
+struct conf_ht_setting {
+	u8 rx_ba_win_size;
+	u8 tx_ba_win_size;
+	u16 inactivity_timeout;
+
+	/* bitmap of enabled TIDs for TX BA sessions */
+	u8 tx_ba_tid_bitmap;
+
+	/* DEFAULT / WIDE / SISO20 */
+	u8 mode;
+} __packed;
+
+struct conf_memory_settings {
+	/* Number of stations supported in IBSS mode */
+	u8 num_stations;
+
+	/* Number of ssid profiles used in IBSS mode */
+	u8 ssid_profiles;
+
+	/* Number of memory buffers allocated to rx pool */
+	u8 rx_block_num;
+
+	/* Minimum number of blocks allocated to tx pool */
+	u8 tx_min_block_num;
+
+	/* Disable/Enable dynamic memory */
+	u8 dynamic_memory;
+
+	/*
+	 * Minimum required free tx memory blocks in order to assure optimum
+	 * performance
+	 *
+	 * Range: 0-120
+	 */
+	u8 min_req_tx_blocks;
+
+	/*
+	 * Minimum required free rx memory blocks in order to assure optimum
+	 * performance
+	 *
+	 * Range: 0-120
+	 */
+	u8 min_req_rx_blocks;
+
+	/*
+	 * Minimum number of mem blocks (free+used) guaranteed for TX
+	 *
+	 * Range: 0-120
+	 */
+	u8 tx_min;
+} __packed;
+
+
+struct conf_rx_streaming_settings {
+	/*
+	 * RX Streaming duration (in msec) from last tx/rx
+	 *
+	 * Range: u32
+	 */
+	u32 duration;
+
+	/*
+	 * Bitmap of tids to be polled during RX streaming.
+	 * (Note: it doesn't look like it really matters)
+	 *
+	 * Range: 0x1-0xff
+	 */
+	u8 queues;
+
+	/*
+	 * RX Streaming interval.
+	 * (Note:this value is also used as the rx streaming timeout)
+	 * Range: 0 (disabled), 10 - 100
+	 */
+	u8 interval;
+
+	/*
+	 * enable rx streaming also when there is no coex activity
+	 */
+	u8 always;
+} __packed;
+
+struct conf_fwlog {
+	/* Continuous or on-demand */
+	u8 mode;
+
+	/*
+	 * Number of memory blocks dedicated for the FW logger
+	 *
+	 * Range: 2-16, or 0 to disable the FW logger
+	 */
+	u8 mem_blocks;
+
+	/* Minimum log level threshold */
+	u8 severity;
+
+	/* Include/exclude timestamps from the log messages */
+	u8 timestamp;
+
+	/* See enum cc33xx_fwlogger_output */
+	u8 output;
+
+	/* Regulates the frequency of log messages */
+	u8 threshold;
+} __packed;
+
+#define ACX_RATE_MGMT_NUM_OF_RATES 13
+struct conf_rate_policy_settings {
+	u16 rate_retry_score;
+	u16 per_add;
+	u16 per_th1;
+	u16 per_th2;
+	u16 max_per;
+	u8 inverse_curiosity_factor;
+	u8 tx_fail_low_th;
+	u8 tx_fail_high_th;
+	u8 per_alpha_shift;
+	u8 per_add_shift;
+	u8 per_beta1_shift;
+	u8 per_beta2_shift;
+	u8 rate_check_up;
+	u8 rate_check_down;
+	u8 rate_retry_policy[13];
+} __packed;
+
+struct conf_hangover_settings {
+	u32 recover_time;
+	u8 hangover_period;
+	u8 dynamic_mode;
+	u8 early_termination_mode;
+	u8 max_period;
+	u8 min_period;
+	u8 increase_delta;
+	u8 decrease_delta;
+	u8 quiet_time;
+	u8 increase_time;
+	u8 window_size;
+} __packed;
+
+
+enum {
+	CLOCK_CONFIG_16_2_M	= 1,
+	CLOCK_CONFIG_16_368_M,
+	CLOCK_CONFIG_16_8_M,
+	CLOCK_CONFIG_19_2_M,
+	CLOCK_CONFIG_26_M,
+	CLOCK_CONFIG_32_736_M,
+	CLOCK_CONFIG_33_6_M,
+	CLOCK_CONFIG_38_468_M,
+	CLOCK_CONFIG_52_M,
+
+	NUM_CLOCK_CONFIGS,
+}; 
+
+enum cc33xx_ht_mode {
+	/* Default - use MIMO, fallback to SISO20 */
+	HT_MODE_DEFAULT = 0,
+
+	/* Wide - use SISO40 */
+	HT_MODE_WIDE = 1,
+
+	/* Use SISO20 */
+	HT_MODE_SISO20 = 2,
+};
+
+
+struct conf_ap_sleep_settings {
+	/* Duty Cycle (20-80% of staying Awake) for IDLE AP
+	 * (0: disable)
+	 */
+	u8 idle_duty_cycle;
+	/* Duty Cycle (20-80% of staying Awake) for Connected AP
+	 * (0: disable)
+	 */
+	u8 connected_duty_cycle;
+	/* Maximum stations that are allowed to be connected to AP
+	 *  (255: no limit)
+	 */
+	u8 max_stations_thresh;
+	/* Timeout till enabling the Sleep Mechanism after data stops
+	 * [unit: 100 msec]
+	 */
+	u8 idle_conn_thresh;
+} __packed;
+
+
+#define CHANNELS_COUNT 14
+#define PER_CHANNEL_REG_RULE_BYTES 13
+#define REG_RULES_COUNT (CHANNELS_COUNT * PER_CHANNEL_REG_RULE_BYTES) /* 182 */
+
+
+/* TX Power limitation for a channel, used for reg domain */
+struct conf_channel_power_limit {
+	u32 reg_lim_0;
+	u32 reg_lim_1;
+	u32 reg_lim_2;
+	u8  reg_lim_3;
+} __packed;
+
+struct conf_coex_configuration {
+	/*
+	 * Work without Coex HW
+	 *
+	 * Range: 1 - YES, 0 - NO
+	 */
+	u8 Disable_coex;
+	/*
+	 * Yes/No Choose if External SoC entity is connected
+	 *
+	 * Range: 1 - YES, 0 - NO
+	 */
+	u8 is_Ext_soc_enable;
+	/* 
+	 * External SoC grant polarity
+	 * 
+	 * 0 - Active Low
+	 *
+	 * 1 - Active High (Default)
+	 */
+	u8 ext_soc_grant_polarity;
+	/* 
+	 * External SoC priority polarity
+	 *
+	 * 0 - Active Low (Default)
+	 *
+	 * 1 - Active High
+	 */
+	u8 ext_soc_priority_polarity;
+	/* 
+	 * External SoC request polarity
+	 * 
+	 * 0 - Active Low (Default)
+	 *
+	 * 1 - Active High
+	 */
+	u8 ext_soc_request_polarity;
+	u16 ext_soc_min_grant_time;
+	u16 ext_soc_max_grant_time;
+	/* 
+	 * Range: 0 - 20 us
+	 */
+	u8 ext_soc_t2_time;
+
+	u8 ext_soc_to_wifi_grant_delay;
+	u8 ext_soc_to_ble_grant_delay;
+} __packed;
+
+struct cc33xx_core_conf {
+	u8 enable_5ghz;
+	u8 enable_ble;
+	u8 enable_at_test_debug; //only for at-test chips, debug mode (ignoring disable efuses)
+	u8 disable_beamforming_fftp; // for PG version 2.0
+	u32 BleUartBaudrate;
+	u8 enable_FlowCtrl;
+	u8 listen_interval;
+	u8 wake_up_event;
+	u8 suspend_listen_interval;
+	u8 suspend_wake_up_event;
+	/*
+	 * Per channel power limitations, will be addressed per channel as
+	 * described in struct conf_channel_power_limit 
+	 */
+	u8 per_channel_power_limit[182];
+	u32 internalSlowclk_wakeupEarlier;
+	u32 internalSlowclk_OpenWindowLonger;
+	u32 externalSlowclk_wakeupEarlier;
+	u32 externalSlowclk_OpenWindowLonger;
+	struct conf_coex_configuration coex_configuration;
+	/* Prevent HW recovery. FW will remain stuck. */
+	u8 no_recovery;
+	u8 disable_logger;
+	u8 mixed_mode_support;
+} __packed;
+
+struct cc33xx_mac_conf {
+	u8 ps_scheme;
+	u8 he_enable;
+	u8 ApMaxNumStations;
+} __packed;
+
+struct cc33xx_phy_conf {
+	u8 insertion_loss[2];
+	u8 ant_gain[2];
+	u8 ble_ch_lim_1M[40];
+	u8 ble_ch_lim_2M[40];
+	u8 one_time_calibration_only;
+} __packed;
+
+struct cc33xx_host_conf {
+	struct conf_rx_settings rx;
+	struct conf_tx_settings tx;
+	struct conf_conn_settings conn;
+	struct conf_itrim_settings itrim;
+	struct conf_pm_config_settings pm_config;
+	struct conf_roam_trigger_settings roam_trigger;
+	struct conf_scan_settings scan;
+	struct conf_sched_scan_settings sched_scan;
+	struct conf_ht_setting ht;
+	struct conf_memory_settings mem;
+	struct conf_rx_streaming_settings rx_streaming;//BasharB: is this even used
+	struct conf_fwlog fwlog;
+	struct conf_rate_policy_settings rate;
+	struct conf_hangover_settings hangover;
+	struct conf_ap_sleep_settings ap_sleep;	
+
+} __packed;
+
+
+struct cc33xx_conf_file {
+	struct cc33xx_conf_header header;
+	struct cc33xx_phy_conf phy;
+	struct cc33xx_mac_conf mac;
+	struct cc33xx_core_conf core;
+	struct cc33xx_host_conf host_conf;
+} __packed;
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/debug.h drivers/net/wireless/ti/cc33xx/debug.h
new file mode 100644
index 000000000000..a84bb8063579
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/debug.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <coelho@ti.com>
+ */
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#include <linux/bitops.h>
+#include <linux/printk.h>
+
+#define DRIVER_NAME "wlcore"
+#define DRIVER_PREFIX DRIVER_NAME ": "
+
+enum {
+	DEBUG_NONE	= 0,
+	DEBUG_IRQ	= BIT(0),
+	DEBUG_SPI	= BIT(1),
+	DEBUG_BOOT	= BIT(2),
+	DEBUG_CORE_STATUS = BIT(3),
+	DEBUG_TESTMODE	= BIT(4),
+	DEBUG_EVENT	= BIT(5),
+	DEBUG_TX	= BIT(6),
+	DEBUG_RX	= BIT(7),
+	DEBUG_SCAN	= BIT(8),
+	DEBUG_CRYPT	= BIT(9),
+	DEBUG_PSM	= BIT(10),
+	DEBUG_MAC80211	= BIT(11),
+	DEBUG_CMD	= BIT(12),
+	DEBUG_ACX	= BIT(13),
+	DEBUG_SDIO	= BIT(14),
+	DEBUG_FILTERS   = BIT(15),
+	DEBUG_ADHOC     = BIT(16),
+	DEBUG_AP	= BIT(17),
+	DEBUG_PROBE	= BIT(18),
+	DEBUG_IO	= BIT(19),
+	DEBUG_MASTER	= (DEBUG_ADHOC | DEBUG_AP),
+	DEBUG_CC33xx = BIT(20),
+	DEBUG_ALL	= ~0,
+	DEBUG_NO_DATAPATH = (DEBUG_ALL & ~DEBUG_IRQ & ~DEBUG_TX & ~DEBUG_RX & ~DEBUG_CORE_STATUS),
+};
+
+extern u32 cc33xx_debug_level;
+
+#define DEBUG_DUMP_LIMIT 1024
+
+#define cc33xx_error(fmt, arg...) \
+	pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
+
+#define cc33xx_warning(fmt, arg...) \
+	pr_warn(DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
+
+#define cc33xx_notice(fmt, arg...) \
+	pr_info(DRIVER_PREFIX fmt "\n", ##arg)
+
+#define cc33xx_info(fmt, arg...) \
+	pr_info(DRIVER_PREFIX fmt "\n", ##arg)
+
+/* define the debug macro differently if dynamic debug is supported */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define cc33xx_debug(level, fmt, arg...) \
+	do { \
+		if (unlikely(level & cc33xx_debug_level)) \
+			dynamic_pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \
+	} while (0)
+#else
+#define cc33xx_debug(level, fmt, arg...) \
+	do { \
+		if (unlikely(level & cc33xx_debug_level)) \
+			printk(KERN_DEBUG pr_fmt(DRIVER_PREFIX fmt "\n"), \
+			       ##arg); \
+	} while (0)
+#endif
+
+#define cc33xx_dump(level, prefix, buf, len)				      \
+	do {								      \
+		if (level & cc33xx_debug_level)				      \
+			print_hex_dump_debug(DRIVER_PREFIX prefix,	      \
+					DUMP_PREFIX_OFFSET, 16, 1,	      \
+					buf,				      \
+					min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+					0);				      \
+	} while (0)
+
+#define cc33xx_dump_ascii(level, prefix, buf, len)			      \
+	do {								      \
+		if (level & cc33xx_debug_level)				      \
+			print_hex_dump_debug(DRIVER_PREFIX prefix,	      \
+					DUMP_PREFIX_OFFSET, 16, 1,	      \
+					buf,				      \
+					min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+					true);				      \
+	} while (0)
+
+#endif /* __DEBUG_H__ */
diff --git drivers/net/wireless/ti/cc33xx/debugfs.c drivers/net/wireless/ti/cc33xx/debugfs.c
new file mode 100644
index 000000000000..41b9b90f8a45
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/debugfs.c
@@ -0,0 +1,1984 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include "debugfs.h"
+
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "acx.h"
+#include "ps.h"
+#include "io.h"
+#include "tx.h"
+#include "../net/mac80211/ieee80211_i.h"
+
+
+#define CC33XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
+	DEBUGFS_FWSTATS_FILE(a, b, c, cc33xx_acx_statistics)
+#define CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \
+	DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, cc33xx_acx_statistics)
+
+
+CC33XX_DEBUGFS_FWSTATS_FILE(error, error_frame_non_ctrl, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, error_frame_ctrl, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, error_frame_during_protection, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, null_frame_tx_start, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, null_frame_cts_start, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, bar_retry, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, num_frame_cts_nul_flid, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, tx_abort_failure, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, tx_resume_failure, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, rx_cmplt_db_overflow_cnt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_rx_exch, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_tx_exch, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_tx, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_nvic_pending, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, rx_excessive_frame_len, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, burst_mismatch, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(error, tbc_exch_mismatch, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_stop, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(tx, tx_retry_per_rate,
+				  NUM_OF_RATES_INDEXES);
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_cfe1, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_cfe2, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_rts_timeout, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_xfr, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread,
+				  PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD);
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_time_count, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_time_avg, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_cycle_avg, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_percent, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, ap_sleep_active_conf, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, ap_sleep_user_conf, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pwr, ap_sleep_counter, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(rx_rate, rx_frames_per_rates, 50);
+
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_rate,
+				  AGGR_STATS_TX_AGG);
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_len,
+				  AGGR_STATS_TX_AGG);
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size,
+				  AGGR_STATS_RX_SIZE_LEN);
+
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_rx_xfer_swi, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full,
+				  PIPE_STATS_HW_FIFO);
+
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(diversity, num_of_packets_per_ant,
+				  DIVERSITY_STATS_NUM_OF_ANT);
+CC33XX_DEBUGFS_FWSTATS_FILE(diversity, total_num_of_toggles, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(thermal, irq_thr_low, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(thermal, irq_thr_high, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(thermal, tx_stop, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(thermal, tx_resume, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(thermal, false_irq, "%u");
+CC33XX_DEBUGFS_FWSTATS_FILE(thermal, adc_source_unexpected, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(calib, fail_count,
+				  CC33XX_NUM_OF_CALIBRATIONS_ERRORS);
+CC33XX_DEBUGFS_FWSTATS_FILE(calib, calib_count, "%u");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(roaming, rssi_level, "%d");
+
+CC33XX_DEBUGFS_FWSTATS_FILE(dfs, num_of_radar_detections, "%d");
+
+struct cc33xx_cmd_dfs_radar_debug {
+	struct cc33xx_cmd_header header;
+
+	u8 channel;
+	u8 padding[3];
+} __packed;
+
+
+/* ms */
+#define CC33XX_DEBUGFS_STATS_LIFETIME 1000
+
+#define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE))
+
+#define MAX_VERSIONS_LEN	59
+
+#define MAX_VERSIONS_EXTENDED_LEN	86
+
+
+
+int cc33xx_cmd_radar_detection_debug(struct cc33xx *wl, u8 channel)
+{
+	struct cc33xx_cmd_dfs_radar_debug *cmd;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_CMD, "cmd radar detection debug (chan %d)",
+		     channel);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->channel = channel;
+
+	ret = cc33xx_cmd_send(wl, CMD_DFS_RADAR_DETECTION_DEBUG,
+			      cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send radar detection debug command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+	return ret;
+}
+
+static ssize_t conf_read(struct file *file, char __user *user_buf,
+			 size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_conf_header header;
+	char *buf, *pos;
+	size_t len;
+	int ret;
+
+	len = CC33X_CONF_SIZE;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	header.magic	= cpu_to_le32(CC33XX_CONF_MAGIC);
+	header.version	= cpu_to_le32(CC33XX_CONF_VERSION);
+	header.checksum	= 0;
+
+	mutex_lock(&wl->mutex);
+
+	pos = buf;
+	memcpy(pos, &header, sizeof(header));
+	pos += sizeof(header);
+	memcpy(pos, &wl->conf, sizeof(wl->conf));
+
+	mutex_unlock(&wl->mutex);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations conf_ops = {
+	.read = conf_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t clear_fw_stats_write(struct file *file,
+			      const char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	int ret;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	ret = cc33xx_acx_clear_statistics(wl);
+	if (ret < 0) {
+		count = ret;
+		goto out;
+	}
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations clear_fw_stats_ops = {
+	.write = clear_fw_stats_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t radar_detection_write(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	int ret;
+	u8 channel;
+
+	ret = kstrtou8_from_user(user_buf, count, 10, &channel);
+	if (ret < 0) {
+		cc33xx_warning("illegal channel");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	ret = cc33xx_cmd_radar_detection_debug(wl, channel);
+	if (ret < 0)
+		count = ret;
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations radar_detection_ops = {
+	.write = radar_detection_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t dynamic_fw_traces_write(struct file *file,
+					const char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&wl->mutex);
+
+	wl->dynamic_fw_traces = value;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	ret = cc33xx_acx_dynamic_fw_traces(wl);
+	if (ret < 0)
+		count = ret;
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static ssize_t dynamic_fw_traces_read(struct file *file,
+					char __user *userbuf,
+					size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	return cc33xx_format_buffer(userbuf, count, ppos,
+				    "%d\n", wl->dynamic_fw_traces);
+}
+
+static const struct file_operations dynamic_fw_traces_ops = {
+	.read = dynamic_fw_traces_read,
+	.write = dynamic_fw_traces_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+static ssize_t radar_debug_mode_write(struct file *file,
+				      const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal radar_debug_mode value!");
+		return -EINVAL;
+	}
+
+	/* valid values: 0/1 */
+	if (!(value == 0 || value == 1)) {
+		cc33xx_warning("value is not in valid!");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->radar_debug_mode = value;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	cc33xx_for_each_wlvif_ap(wl, wlvif) {
+		wlcore_cmd_generic_cfg(wl, wlvif,
+				       WLCORE_CFG_FEATURE_RADAR_DEBUG,
+				       wl->radar_debug_mode, 0);
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static ssize_t radar_debug_mode_read(struct file *file,
+				     char __user *userbuf,
+				     size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(userbuf, count, ppos,
+				    "%d\n", wl->radar_debug_mode);
+}
+
+static const struct file_operations radar_debug_mode_ops = {
+	.write = radar_debug_mode_write,
+	.read = radar_debug_mode_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+#endif /* CFG80211_CERTIFICATION_ONUS */
+
+
+/* debugfs macros idea from mac80211 */
+int cc33xx_format_buffer(char __user *userbuf, size_t count,
+			 loff_t *ppos, char *fmt, ...)
+{
+	va_list args;
+	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
+	int res;
+
+	va_start(args, fmt);
+	res = vscnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+
+void cc33xx_debugfs_update_stats(struct cc33xx *wl)
+{
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+
+	if (!wl->plt &&
+	    time_after(jiffies, wl->stats.fw_stats_update +
+		       msecs_to_jiffies(CC33XX_DEBUGFS_STATS_LIFETIME))) {
+		cc33xx_acx_statistics(wl, wl->stats.fw_stats);
+		wl->stats.fw_stats_update = jiffies;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count);
+DEBUGFS_READONLY_FILE(excessive_retries, "%u",
+		      wl->stats.excessive_retries);
+
+static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf,
+				 size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	u32 queue_len;
+	char buf[20];
+	int res;
+
+	queue_len = cc33xx_tx_total_queue_count(wl);
+
+	res = scnprintf(buf, sizeof(buf), "%u\n", queue_len);
+	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+
+static const struct file_operations tx_queue_len_ops = {
+	.read = tx_queue_len_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static void chip_op_handler(struct cc33xx *wl, unsigned long value,
+			    void *arg)
+{
+	int (*chip_op) (struct cc33xx *wl);
+
+	if (!arg) {
+		cc33xx_warning("debugfs chip_op_handler with no callback");
+		return;
+	}
+
+	chip_op = arg;
+	chip_op(wl);
+
+}
+
+
+static inline void no_write_handler(struct cc33xx *wl,
+				    unsigned long value,
+				    unsigned long param)
+{
+}
+
+#define CC33XX_CONF_DEBUGFS(param, conf_sub_struct,			\
+			    min_val, max_val, write_handler_locked,	\
+			    write_handler_arg)				\
+	static ssize_t param##_read(struct file *file,			\
+				      char __user *user_buf,		\
+				      size_t count, loff_t *ppos)	\
+	{								\
+	struct cc33xx *wl = file->private_data;				\
+	return cc33xx_format_buffer(user_buf, count,			\
+				    ppos, "%d\n",			\
+				    wl->conf.host_conf.conf_sub_struct.param);	\
+	}								\
+									\
+	static ssize_t param##_write(struct file *file,			\
+				     const char __user *user_buf,	\
+				     size_t count, loff_t *ppos)	\
+	{								\
+	struct cc33xx *wl = file->private_data;				\
+	unsigned long value;						\
+	int ret;							\
+									\
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);		\
+	if (ret < 0) {							\
+		cc33xx_warning("illegal value for " #param);		\
+		return -EINVAL;						\
+	}								\
+									\
+	if (value < min_val || value > max_val) {			\
+		cc33xx_warning(#param " is not in valid range");	\
+		return -ERANGE;						\
+	}								\
+									\
+	mutex_lock(&wl->mutex);						\
+	wl->conf.host_conf.conf_sub_struct.param = value;				\
+									\
+	write_handler_locked(wl, value, write_handler_arg);		\
+									\
+	mutex_unlock(&wl->mutex);					\
+	return count;							\
+	}								\
+									\
+	static const struct file_operations param##_ops = {		\
+		.read = param##_read,					\
+		.write = param##_write,					\
+		.open = simple_open,					\
+		.llseek = default_llseek,				\
+	};
+
+CC33XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535,
+		    chip_op_handler, cc33xx_acx_init_rx_interrupt)
+CC33XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535,
+		    chip_op_handler, cc33xx_acx_init_rx_interrupt)
+CC33XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100,
+		    chip_op_handler, cc33xx_acx_init_rx_interrupt)
+
+static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
+			  size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	bool state = test_bit(CC33XX_FLAG_GPIO_POWER, &wl->flags);
+
+	int res;
+	char buf[10];
+
+	res = scnprintf(buf, sizeof(buf), "%d\n", state);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
+}
+
+static ssize_t gpio_power_write(struct file *file,
+			   const char __user *user_buf,
+			   size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in gpio_power");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (value)
+		cc33xx_power_on(wl);
+	else
+		cc33xx_power_off(wl);
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations gpio_power_ops = {
+	.read = gpio_power_read,
+	.write = gpio_power_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t start_recovery_write(struct file *file,
+				    const char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	mutex_lock(&wl->mutex);
+	cc33xx_queue_recovery_work(wl);
+	mutex_unlock(&wl->mutex);
+
+	return count;
+}
+
+static const struct file_operations start_recovery_ops = {
+	.write = start_recovery_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t dynamic_ps_timeout_read(struct file *file, char __user *user_buf,
+			  size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%d\n",
+				    wl->conf.host_conf.conn.dynamic_ps_timeout);
+}
+
+static ssize_t dynamic_ps_timeout_write(struct file *file,
+				    const char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in dynamic_ps");
+		return -EINVAL;
+	}
+
+	if (value < 1 || value > 65535) {
+		cc33xx_warning("dynamic_ps_timeout is not in valid range");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.host_conf.conn.dynamic_ps_timeout = value;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* In case we're already in PSM, trigger it again to set new timeout
+	 * immediately without waiting for re-association
+	 */
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
+			cc33xx_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE);
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations dynamic_ps_timeout_ops = {
+	.read = dynamic_ps_timeout_read,
+	.write = dynamic_ps_timeout_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t forced_ps_read(struct file *file, char __user *user_buf,
+			  size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%d\n",
+				    wl->conf.host_conf.conn.forced_ps);
+}
+
+static ssize_t forced_ps_write(struct file *file,
+				    const char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	unsigned long value;
+	int ret, ps_mode;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in forced_ps");
+		return -EINVAL;
+	}
+
+	if (value != 1 && value != 0) {
+		cc33xx_warning("forced_ps should be either 0 or 1");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (wl->conf.host_conf.conn.forced_ps == value)
+		goto out;
+
+	wl->conf.host_conf.conn.forced_ps = value;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* In case we're already in PSM, trigger it again to switch mode
+	 * immediately without waiting for re-association
+	 */
+
+	ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE;
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
+			cc33xx_ps_set_mode(wl, wlvif, ps_mode);
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations forced_ps_ops = {
+	.read = forced_ps_read,
+	.write = forced_ps_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t split_scan_timeout_read(struct file *file, char __user *user_buf,
+			  size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%d\n",
+				    wl->conf.host_conf.scan.split_scan_timeout / 1000);
+}
+
+static ssize_t split_scan_timeout_write(struct file *file,
+				    const char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in split_scan_timeout");
+		return -EINVAL;
+	}
+
+	if (value == 0)
+		cc33xx_info("split scan will be disabled");
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.host_conf.scan.split_scan_timeout = value * 1000;
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations split_scan_timeout_ops = {
+	.read = split_scan_timeout_read,
+	.write = split_scan_timeout_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t driver_state_read(struct file *file, char __user *user_buf,
+				 size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	int res = 0;
+	ssize_t ret;
+	char *buf;
+	struct cc33xx_vif *wlvif;
+
+#define DRIVER_STATE_BUF_LEN 1024
+
+	buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&wl->mutex);
+
+#define DRIVER_STATE_PRINT(x, fmt)   \
+	(res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
+			  #x " = " fmt "\n", wl->x))
+
+#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...)   \
+	(res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
+			  #x " = " fmt "\n", args))
+
+#define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld")
+#define DRIVER_STATE_PRINT_INT(x)  DRIVER_STATE_PRINT(x, "%d")
+#define DRIVER_STATE_PRINT_STR(x)  DRIVER_STATE_PRINT(x, "%s")
+#define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx")
+#define DRIVER_STATE_PRINT_HEX(x)  DRIVER_STATE_PRINT(x, "0x%x")
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+			continue;
+
+		DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+					   wlvif->p2p ? "P2P-CL" : "STA");
+	}
+
+	cc33xx_for_each_wlvif_ap(wl, wlvif)
+		DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+					   wlvif->p2p ? "P2P-GO" : "AP");
+
+	DRIVER_STATE_PRINT_INT(tx_blocks_available);
+	DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
+	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
+	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[1]);
+	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[2]);
+	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[3]);
+	DRIVER_STATE_PRINT_INT(tx_frames_cnt);
+	DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]);
+	DRIVER_STATE_PRINT_INT(tx_queue_count[0]);
+	DRIVER_STATE_PRINT_INT(tx_queue_count[1]);
+	DRIVER_STATE_PRINT_INT(tx_queue_count[2]);
+	DRIVER_STATE_PRINT_INT(tx_queue_count[3]);
+	DRIVER_STATE_PRINT_LHEX(flags);
+	DRIVER_STATE_PRINT_INT(rx_counter);
+	DRIVER_STATE_PRINT_INT(state);
+	DRIVER_STATE_PRINT_INT(band);
+	DRIVER_STATE_PRINT_INT(power_level);
+	DRIVER_STATE_PRINT_INT(enable_11a);
+	DRIVER_STATE_PRINT_LHEX(ap_fw_ps_map);
+	DRIVER_STATE_PRINT_LHEX(ap_ps_map);
+	DRIVER_STATE_PRINT_HEX(quirks);
+	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
+	
+
+#undef DRIVER_STATE_PRINT_INT
+#undef DRIVER_STATE_PRINT_LONG
+#undef DRIVER_STATE_PRINT_HEX
+#undef DRIVER_STATE_PRINT_LHEX
+#undef DRIVER_STATE_PRINT_STR
+#undef DRIVER_STATE_PRINT
+#undef DRIVER_STATE_BUF_LEN
+
+	mutex_unlock(&wl->mutex);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations driver_state_ops = {
+	.read = driver_state_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
+				 size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	int ret, res = 0;
+	const int buf_size = 4096;
+	char *buf;
+	char tmp_buf[64];
+
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&wl->mutex);
+
+#define VIF_STATE_PRINT(x, fmt)				\
+	(res += scnprintf(buf + res, buf_size - res,	\
+			  #x " = " fmt "\n", wlvif->x))
+
+#define VIF_STATE_PRINT_LONG(x)  VIF_STATE_PRINT(x, "%ld")
+#define VIF_STATE_PRINT_INT(x)   VIF_STATE_PRINT(x, "%d")
+#define VIF_STATE_PRINT_STR(x)   VIF_STATE_PRINT(x, "%s")
+#define VIF_STATE_PRINT_LHEX(x)  VIF_STATE_PRINT(x, "0x%lx")
+#define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx")
+#define VIF_STATE_PRINT_HEX(x)   VIF_STATE_PRINT(x, "0x%x")
+
+#define VIF_STATE_PRINT_NSTR(x, len)				\
+	do {							\
+		memset(tmp_buf, 0, sizeof(tmp_buf));		\
+		memcpy(tmp_buf, wlvif->x,			\
+		       min_t(u8, len, sizeof(tmp_buf) - 1));	\
+		res += scnprintf(buf + res, buf_size - res,	\
+				 #x " = %s\n", tmp_buf);	\
+	} while (0)
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		VIF_STATE_PRINT_INT(role_id);
+		VIF_STATE_PRINT_INT(bss_type);
+		VIF_STATE_PRINT_LHEX(flags);
+		VIF_STATE_PRINT_INT(p2p);
+		VIF_STATE_PRINT_INT(dev_role_id);
+		VIF_STATE_PRINT_INT(dev_hlid);
+
+		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+		    wlvif->bss_type == BSS_TYPE_IBSS) {
+			VIF_STATE_PRINT_INT(sta.hlid);
+			VIF_STATE_PRINT_INT(sta.basic_rate_idx);
+			VIF_STATE_PRINT_INT(sta.ap_rate_idx);
+			VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
+			VIF_STATE_PRINT_INT(sta.qos);
+		} else {
+			VIF_STATE_PRINT_INT(ap.global_hlid);
+			VIF_STATE_PRINT_INT(ap.bcast_hlid);
+			VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]);
+			VIF_STATE_PRINT_INT(ap.mgmt_rate_idx);
+			VIF_STATE_PRINT_INT(ap.bcast_rate_idx);
+			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]);
+			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]);
+			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]);
+			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
+		}
+		VIF_STATE_PRINT_INT(last_tx_hlid);
+		VIF_STATE_PRINT_INT(tx_queue_count[0]);
+		VIF_STATE_PRINT_INT(tx_queue_count[1]);
+		VIF_STATE_PRINT_INT(tx_queue_count[2]);
+		VIF_STATE_PRINT_INT(tx_queue_count[3]);
+		VIF_STATE_PRINT_LHEX(links_map[0]);
+		VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
+		VIF_STATE_PRINT_INT(band);
+		VIF_STATE_PRINT_INT(channel);
+		VIF_STATE_PRINT_HEX(bitrate_masks[0]);
+		VIF_STATE_PRINT_HEX(bitrate_masks[1]);
+		VIF_STATE_PRINT_HEX(basic_rate_set);
+		VIF_STATE_PRINT_HEX(basic_rate);
+		VIF_STATE_PRINT_HEX(rate_set);
+		VIF_STATE_PRINT_INT(beacon_int);
+		VIF_STATE_PRINT_INT(default_key);
+		VIF_STATE_PRINT_INT(aid);
+		VIF_STATE_PRINT_INT(psm_entry_retry);
+		VIF_STATE_PRINT_INT(power_level);
+		VIF_STATE_PRINT_INT(rssi_thold);
+		VIF_STATE_PRINT_INT(last_rssi_event);
+		VIF_STATE_PRINT_INT(ba_support);
+		VIF_STATE_PRINT_INT(ba_allowed);
+		VIF_STATE_PRINT_LLHEX(total_freed_pkts);
+	}
+
+#undef VIF_STATE_PRINT_INT
+#undef VIF_STATE_PRINT_LONG
+#undef VIF_STATE_PRINT_HEX
+#undef VIF_STATE_PRINT_LHEX
+#undef VIF_STATE_PRINT_LLHEX
+#undef VIF_STATE_PRINT_STR
+#undef VIF_STATE_PRINT_NSTR
+#undef VIF_STATE_PRINT
+
+	mutex_unlock(&wl->mutex);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations vifs_state_ops = {
+	.read = vifs_state_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t dtim_interval_read(struct file *file, char __user *user_buf,
+				  size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	u8 value;
+
+	if (wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
+	    wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
+		value = wl->conf.core.listen_interval;
+	else
+		value = 0;
+
+	return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", value);
+}
+
+static ssize_t dtim_interval_write(struct file *file,
+				   const char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif = NULL;
+	struct ieee80211_sub_if_data *sdata = NULL;
+	struct ieee80211_vif *vif = NULL;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value for dtim_interval");
+		return -EINVAL;
+	}
+
+	if (value < 1 || value > 10) {
+		cc33xx_warning("dtim value is not in valid range");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.core.listen_interval = value;
+
+	if (value == 1)
+		wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
+	else
+		wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		if (!wlcore_is_p2p_mgmt(wlvif))
+		{
+			vif = cc33xx_wlvif_to_vif(wlvif);
+			sdata = vif_to_sdata(vif);
+			cc33xx_debug(DEBUG_CMD, "Setting LSI on interface %s",
+						sdata->name);
+			ret = cc33xx_acx_wake_up_conditions(wl, wlvif,
+						wl->conf.core.wake_up_event,
+						wl->conf.core.listen_interval);
+			if (ret < 0) {
+				vif = cc33xx_wlvif_to_vif(wlvif);
+				sdata = vif_to_sdata(vif);
+				cc33xx_warning("Failed to set LSI on "
+					       "interface %s", sdata->name);
+				return ret;
+			}
+		}
+	}
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations dtim_interval_ops = {
+	.read = dtim_interval_read,
+	.write = dtim_interval_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+
+
+static ssize_t suspend_dtim_interval_read(struct file *file,
+					  char __user *user_buf,
+					  size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	u8 value;
+
+	if (wl->conf.core.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
+	    wl->conf.core.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
+		value = wl->conf.core.suspend_listen_interval;
+	else
+		value = 0;
+
+	return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", value);
+}
+
+static ssize_t suspend_dtim_interval_write(struct file *file,
+					   const char __user *user_buf,
+					   size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value for suspend_dtim_interval");
+		return -EINVAL;
+	}
+
+	if (value < 1 || value > 10) {
+		cc33xx_warning("suspend_dtim value is not in valid range");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.core.suspend_listen_interval = value;
+	/* for some reason there are different event types for 1 and >1 */
+	if (value == 1)
+		wl->conf.core.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
+	else
+		wl->conf.core.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+
+static const struct file_operations suspend_dtim_interval_ops = {
+	.read = suspend_dtim_interval_read,
+	.write = suspend_dtim_interval_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t beacon_interval_read(struct file *file, char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	u8 value;
+
+	if (wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_BEACON ||
+	    wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS)
+		value = wl->conf.core.listen_interval;
+	else
+		value = 0;
+
+	return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", value);
+}
+
+static ssize_t beacon_interval_write(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value for beacon_interval");
+		return -EINVAL;
+	}
+
+	if (value < 1 || value > 255) {
+		cc33xx_warning("beacon interval value is not in valid range");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.core.listen_interval = value;
+	/* for some reason there are different event types for 1 and >1 */
+	if (value == 1)
+		wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_BEACON;
+	else
+		wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS;
+
+	/*
+	 * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only
+	 * take effect on the next time we enter psm.
+	 */
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations beacon_interval_ops = {
+	.read = beacon_interval_read,
+	.write = beacon_interval_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t rx_streaming_interval_write(struct file *file,
+			   const char __user *user_buf,
+			   size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in rx_streaming_interval!");
+		return -EINVAL;
+	}
+
+	/* valid values: 0, 10-100 */
+	if (value && (value < 10 || value > 100)) {
+		cc33xx_warning("value is not in range!");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.host_conf.rx_streaming.interval = value;
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		cc33xx_recalc_rx_streaming(wl, wlvif);
+	}
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static ssize_t rx_streaming_interval_read(struct file *file,
+			    char __user *userbuf,
+			    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	return cc33xx_format_buffer(userbuf, count, ppos,
+				    "%d\n", wl->conf.host_conf.rx_streaming.interval);
+}
+
+static const struct file_operations rx_streaming_interval_ops = {
+	.read = rx_streaming_interval_read,
+	.write = rx_streaming_interval_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t rx_streaming_always_write(struct file *file,
+			   const char __user *user_buf,
+			   size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in rx_streaming_write!");
+		return -EINVAL;
+	}
+
+	/* valid values: 0, 10-100 */
+	if (!(value == 0 || value == 1)) {
+		cc33xx_warning("value is not in valid!");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.host_conf.rx_streaming.always = value;
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		cc33xx_recalc_rx_streaming(wl, wlvif);
+	}
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static ssize_t rx_streaming_always_read(struct file *file,
+			    char __user *userbuf,
+			    size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	return cc33xx_format_buffer(userbuf, count, ppos,
+				    "%d\n", wl->conf.host_conf.rx_streaming.always);
+}
+
+static const struct file_operations rx_streaming_always_ops = {
+	.read = rx_streaming_always_read,
+	.write = rx_streaming_always_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t beacon_filtering_write(struct file *file,
+				      const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	struct cc33xx_vif *wlvif;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value for beacon_filtering!");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, !!value);
+	}
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations beacon_filtering_ops = {
+	.write = beacon_filtering_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t fw_stats_raw_read(struct file *file,
+				 char __user *userbuf,
+				 size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	cc33xx_debugfs_update_stats(wl);
+
+	return simple_read_from_buffer(userbuf, count, ppos,
+				       wl->stats.fw_stats,
+				       wl->stats.fw_stats_len);
+}
+
+static const struct file_operations fw_stats_raw_ops = {
+	.read = fw_stats_raw_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t sleep_auth_read(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%d\n",
+				    wl->sleep_auth);
+}
+
+static ssize_t sleep_auth_write(struct file *file,
+				const char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in sleep_auth");
+		return -EINVAL;
+	}
+
+	if (value > CC33XX_PSM_MAX) {
+		cc33xx_warning("sleep_auth must be between 0 and %d",
+			       CC33XX_PSM_MAX);
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.host_conf.conn.sta_sleep_auth = value;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		/* this will show up on "read" in case we are off */
+		wl->sleep_auth = value;
+		goto out;
+	}
+
+	cc33xx_acx_sleep_auth(wl, value);
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations sleep_auth_ops = {
+	.read = sleep_auth_read,
+	.write = sleep_auth_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+//ble_enable
+static ssize_t ble_enable_read(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%d\n",
+				    wl->ble_enable);
+}
+
+static ssize_t ble_enable_write(struct file *file,
+				const char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
+
+	if (value == wl->ble_enable) {
+		
+		cc33xx_warning("ble_enable is already %d",wl->ble_enable);
+		return -EINVAL;
+	}
+
+	if (value != 1) {
+		cc33xx_warning("illegal value in ble_enable (only value allowed is is 1)");
+		cc33xx_warning("ble_enable cant be disabled after being enabled.");
+		return -EINVAL;
+	}
+
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		/* this will show up on "read" in case we are off */
+		wl->ble_enable = value;
+		goto out;
+	}
+
+	cc33xx_ble_enable(wl, value);
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+
+
+//ble_enable
+
+
+static const struct file_operations ble_enable_ops = {
+	.read = ble_enable_read,
+	.write = ble_enable_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t set_tsf_read(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%llx\n", 0LL);
+}
+
+static ssize_t set_tsf_write(struct file *file,
+				const char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long long value;
+	int ret;
+
+	ret = kstrtoull_from_user(user_buf, count, 0, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in set_tsf");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+	
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		goto out;
+	}
+
+
+	cc33xx_acx_set_tsf(wl, value); 
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations set_tsf_ops = {
+	.open = simple_open,
+	.read = set_tsf_read,
+	.write = set_tsf_write,
+	.llseek = default_llseek,
+};
+
+static ssize_t dev_mem_read(struct file *file,
+	     char __user *user_buf, size_t count,
+	     loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
+		size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig)
+{
+	/* only requests of dword-aligned size and offset are supported */
+	if (offset % 4)
+		return -EINVAL;
+
+	return no_seek_end_llseek(file, offset, orig);
+}
+
+static const struct file_operations dev_mem_ops = {
+	.open = simple_open,
+	.read = dev_mem_read,
+	.write = dev_mem_write,
+	.llseek = dev_mem_seek,
+};
+
+static ssize_t fw_logger_read(struct file *file, char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count,
+					ppos, "%d\n",
+					wl->conf.host_conf.fwlog.output);
+}
+
+static ssize_t fw_logger_write(struct file *file,
+			       const char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	unsigned long value;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in fw_logger");
+		return -EINVAL;
+	}
+
+	if ((value > 2) || (value == 0)) {
+		cc33xx_warning("fw_logger value must be 1-UART 2-SDIO");
+		return -ERANGE;
+	}
+
+	if (wl->conf.host_conf.fwlog.output == 0) {
+		cc33xx_warning("invalid operation - fw logger disabled by default, please change mode via wlconf");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	wl->conf.host_conf.fwlog.output = value;
+
+	cc33xx_cmd_config_fwlog(wl);
+
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations fw_logger_ops = {
+	.open = simple_open,
+	.read = fw_logger_read,
+	.write = fw_logger_write,
+	.llseek = default_llseek,
+};
+
+static ssize_t antenna_select_read(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count, 
+					ppos, "%d\n", wl->antenna_selection);
+}
+
+static ssize_t antenna_select_write(struct file *file, 
+				const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	int ret;
+	u8 selection;
+
+	ret = kstrtou8_from_user(user_buf, count, 0, &selection);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in antenna_select");
+		return -EINVAL;
+	}
+
+	if (selection > 1) {
+		cc33xx_warning("selection should be either 0 or 1");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+	
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		goto out;
+	}
+
+	ret = cc33xx_acx_set_antenna_select(wl, selection); 
+	if (ret == 0) {
+		wl->antenna_selection = selection;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations antenna_select_ops = {
+	.open = simple_open,
+	.read = antenna_select_read,
+	.write = antenna_select_write,
+	.llseek = default_llseek,
+};
+
+static ssize_t get_versions_extended_read(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	char all_versions_str [MAX_VERSIONS_EXTENDED_LEN];
+
+	sprintf(all_versions_str, "Driver Version: %u.%u.%u.%u\nFirmware Version: %u.%u.%u.%u\nPhy Version: %u.%u.%u.%u.%u.%u", 
+		wl->all_versions.driver_ver.major_version, wl->all_versions.driver_ver.minor_version, wl->all_versions.driver_ver.api_version, wl->all_versions.driver_ver.build_version,
+		wl->all_versions.fw_ver->major_version, wl->all_versions.fw_ver->minor_version, wl->all_versions.fw_ver->api_version, wl->all_versions.fw_ver->build_version, 
+		wl->all_versions.fw_ver->phy_version[5], wl->all_versions.fw_ver->phy_version[4], wl->all_versions.fw_ver->phy_version[3], wl->all_versions.fw_ver->phy_version[2],
+		wl->all_versions.fw_ver->phy_version[1], wl->all_versions.fw_ver->phy_version[0]);
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%s\n",
+				    all_versions_str);
+}
+
+static const struct file_operations get_versions_extended_ops = {
+	.read = get_versions_extended_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t get_versions_read(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	char all_versions_str [MAX_VERSIONS_LEN];
+
+	sprintf(all_versions_str, "Driver Version: %u.%u.%u\nFirmware Version: %u.%u.%u", 
+		wl->all_versions.driver_ver.major_version, wl->all_versions.driver_ver.minor_version, wl->all_versions.driver_ver.api_version,
+		wl->all_versions.fw_ver->major_version, wl->all_versions.fw_ver->minor_version, wl->all_versions.fw_ver->api_version);
+
+	return cc33xx_format_buffer(user_buf, count,
+				    ppos, "%s\n",
+				    all_versions_str);
+}
+
+static const struct file_operations get_versions_ops = {
+	.read = get_versions_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t trigger_fw_assert_write(struct file *file, 
+				const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	mutex_lock(&wl->mutex);
+	
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		goto out;
+	}
+	
+	cc33xx_acx_trigger_fw_assert(wl);
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations trigger_fw_assert_ops = {
+	.open = simple_open,
+	.write = trigger_fw_assert_write,
+	.llseek = default_llseek,
+};
+
+static ssize_t burst_mode_read(struct file *file, char __user *user_buf,
+			       	size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+
+	return cc33xx_format_buffer(user_buf, count, 
+					ppos, "%d\n", wl->burst_disable);
+}
+
+static ssize_t burst_mode_write(struct file *file, 
+				const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cc33xx *wl = file->private_data;
+	int ret;
+	u8 burst_disable;
+
+	ret = kstrtou8_from_user(user_buf, count, 0, &burst_disable);
+	if (ret < 0) {
+		cc33xx_warning("illegal value in burst_mode");
+		return -EINVAL;
+	}
+
+	if (burst_disable > 1) {
+		cc33xx_warning("burst_disable should be either 0 or 1");
+		return -ERANGE;
+	}
+
+	mutex_lock(&wl->mutex);
+	
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		goto out;
+	}
+
+	ret = cc33xx_acx_burst_mode_cfg(wl, burst_disable); 
+	if (ret == 0) {
+		wl->burst_disable = burst_disable;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations burst_mode_ops = {
+	.open = simple_open,
+	.read = burst_mode_read,
+	.write = burst_mode_write,
+	.llseek = default_llseek,
+};
+
+int cc33xx_debugfs_add_files(struct cc33xx *wl,
+			     struct dentry *rootdir)
+{
+	struct dentry *streaming, *stats, *moddir;
+
+	moddir = rootdir;
+	stats = debugfs_create_dir("fw_stats", rootdir);
+
+	DEBUGFS_ADD(tx_queue_len, rootdir);
+	DEBUGFS_ADD(retry_count, rootdir);
+	DEBUGFS_ADD(excessive_retries, rootdir);
+
+	DEBUGFS_ADD(gpio_power, rootdir);
+	DEBUGFS_ADD(start_recovery, rootdir);
+	DEBUGFS_ADD(driver_state, rootdir);
+	DEBUGFS_ADD(vifs_state, rootdir);
+	DEBUGFS_ADD(dtim_interval, rootdir);
+	DEBUGFS_ADD(suspend_dtim_interval, rootdir);
+	DEBUGFS_ADD(beacon_interval, rootdir);
+	DEBUGFS_ADD(beacon_filtering, rootdir);
+	DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
+	DEBUGFS_ADD(forced_ps, rootdir);
+	DEBUGFS_ADD(split_scan_timeout, rootdir);
+	DEBUGFS_ADD(irq_pkt_threshold, rootdir);
+	DEBUGFS_ADD(irq_blk_threshold, rootdir);
+	DEBUGFS_ADD(irq_timeout, rootdir);
+	DEBUGFS_ADD(fw_stats_raw, rootdir);
+	DEBUGFS_ADD(sleep_auth, rootdir);
+	DEBUGFS_ADD(ble_enable, rootdir);
+	DEBUGFS_ADD(set_tsf, rootdir);
+	DEBUGFS_ADD(fw_logger, rootdir);
+	DEBUGFS_ADD(antenna_select, rootdir);
+	DEBUGFS_ADD(get_versions, rootdir);
+	DEBUGFS_ADD(get_versions_extended, rootdir);
+	DEBUGFS_ADD(trigger_fw_assert, rootdir);
+	DEBUGFS_ADD(burst_mode, rootdir);
+
+	streaming = debugfs_create_dir("rx_streaming", rootdir);
+
+	DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
+	DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);
+
+	DEBUGFS_ADD_PREFIX(dev, mem, rootdir);
+
+	DEBUGFS_ADD(clear_fw_stats, stats);
+
+	DEBUGFS_FWSTATS_ADD(error, error_frame_non_ctrl);
+	DEBUGFS_FWSTATS_ADD(error, error_frame_ctrl);
+	DEBUGFS_FWSTATS_ADD(error, error_frame_during_protection);
+	DEBUGFS_FWSTATS_ADD(error, null_frame_tx_start);
+	DEBUGFS_FWSTATS_ADD(error, null_frame_cts_start);
+	DEBUGFS_FWSTATS_ADD(error, bar_retry);
+	DEBUGFS_FWSTATS_ADD(error, num_frame_cts_nul_flid);
+	DEBUGFS_FWSTATS_ADD(error, tx_abort_failure);
+	DEBUGFS_FWSTATS_ADD(error, tx_resume_failure);
+	DEBUGFS_FWSTATS_ADD(error, rx_cmplt_db_overflow_cnt);
+	DEBUGFS_FWSTATS_ADD(error, elp_while_rx_exch);
+	DEBUGFS_FWSTATS_ADD(error, elp_while_tx_exch);
+	DEBUGFS_FWSTATS_ADD(error, elp_while_tx);
+	DEBUGFS_FWSTATS_ADD(error, elp_while_nvic_pending);
+	DEBUGFS_FWSTATS_ADD(error, rx_excessive_frame_len);
+	DEBUGFS_FWSTATS_ADD(error, burst_mismatch);
+	DEBUGFS_FWSTATS_ADD(error, tbc_exch_mismatch);
+
+	DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs);
+	DEBUGFS_FWSTATS_ADD(tx, tx_cmplt);
+	DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared);
+	DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared);
+	DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed);
+	DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed);
+	DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed);
+	DEBUGFS_FWSTATS_ADD(tx, tx_starts);
+	DEBUGFS_FWSTATS_ADD(tx, tx_stop);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_templates);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_data);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame);
+	DEBUGFS_FWSTATS_ADD(tx, tx_exch);
+	DEBUGFS_FWSTATS_ADD(tx, tx_retry_template);
+	DEBUGFS_FWSTATS_ADD(tx, tx_retry_data);
+	DEBUGFS_FWSTATS_ADD(tx, tx_retry_per_rate);
+	DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending);
+	DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry);
+	DEBUGFS_FWSTATS_ADD(tx, tx_done_template);
+	DEBUGFS_FWSTATS_ADD(tx, tx_done_data);
+	DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template);
+	DEBUGFS_FWSTATS_ADD(tx, tx_cfe1);
+	DEBUGFS_FWSTATS_ADD(tx, tx_cfe2);
+	DEBUGFS_FWSTATS_ADD(tx, frag_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed);
+	DEBUGFS_FWSTATS_ADD(tx, frag_init_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found);
+	DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation);
+	DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num);
+	DEBUGFS_FWSTATS_ADD(tx, frag_failed);
+	DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit);
+	DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss);
+
+	DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term);
+	DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes);
+	DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow);
+	DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame);
+	DEBUGFS_FWSTATS_ADD(rx, rx_done);
+	DEBUGFS_FWSTATS_ADD(rx, rx_defrag);
+	DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end);
+	DEBUGFS_FWSTATS_ADD(rx, rx_cmplt);
+	DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt);
+	DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task);
+	DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr);
+	DEBUGFS_FWSTATS_ADD(rx, rx_timeout);
+	DEBUGFS_FWSTATS_ADD(rx, rx_rts_timeout);
+	DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_init_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed);
+	DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt);
+	DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays);
+	DEBUGFS_FWSTATS_ADD(rx, rx_xfr);
+
+	DEBUGFS_FWSTATS_ADD(isr, irqs);
+
+	DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt);
+	DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt);
+	DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync);
+	DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread);
+	DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt);
+	DEBUGFS_FWSTATS_ADD(pwr, sleep_time_count);
+	DEBUGFS_FWSTATS_ADD(pwr, sleep_time_avg);
+	DEBUGFS_FWSTATS_ADD(pwr, sleep_cycle_avg);
+	DEBUGFS_FWSTATS_ADD(pwr, sleep_percent);
+	DEBUGFS_FWSTATS_ADD(pwr, ap_sleep_active_conf);
+	DEBUGFS_FWSTATS_ADD(pwr, ap_sleep_user_conf);
+	DEBUGFS_FWSTATS_ADD(pwr, ap_sleep_counter);
+
+	DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, data_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests);
+	DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep);
+
+	DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates);
+
+	DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_rate);
+	DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_len);
+	DEBUGFS_FWSTATS_ADD(aggr_size, rx_size);
+
+	DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_rx_xfer_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in);
+	DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full);
+	DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out);
+	DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full);
+
+	DEBUGFS_FWSTATS_ADD(diversity, num_of_packets_per_ant);
+	DEBUGFS_FWSTATS_ADD(diversity, total_num_of_toggles);
+
+	DEBUGFS_FWSTATS_ADD(thermal, irq_thr_low);
+	DEBUGFS_FWSTATS_ADD(thermal, irq_thr_high);
+	DEBUGFS_FWSTATS_ADD(thermal, tx_stop);
+	DEBUGFS_FWSTATS_ADD(thermal, tx_resume);
+	DEBUGFS_FWSTATS_ADD(thermal, false_irq);
+	DEBUGFS_FWSTATS_ADD(thermal, adc_source_unexpected);
+
+	DEBUGFS_FWSTATS_ADD(calib, fail_count);
+
+	DEBUGFS_FWSTATS_ADD(calib, calib_count);
+
+	DEBUGFS_FWSTATS_ADD(roaming, rssi_level);
+
+	DEBUGFS_FWSTATS_ADD(dfs, num_of_radar_detections);
+
+	DEBUGFS_ADD(conf, moddir);
+	DEBUGFS_ADD(radar_detection, moddir);
+#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+	DEBUGFS_ADD(radar_debug_mode, moddir);
+#endif
+	DEBUGFS_ADD(dynamic_fw_traces, moddir);
+
+	return 0;
+}
+
+
+void cc33xx_debugfs_reset(struct cc33xx *wl)
+{
+	if (!wl->stats.fw_stats)
+		return;
+
+	memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len);
+	wl->stats.retry_count = 0;
+	wl->stats.excessive_retries = 0;
+}
+
+int cc33xx_debugfs_init(struct cc33xx *wl)
+{
+	int ret;
+	struct dentry *rootdir;
+
+	rootdir = debugfs_create_dir(KBUILD_MODNAME,
+				     wl->hw->wiphy->debugfsdir);
+
+	wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
+	if (!wl->stats.fw_stats) {
+		ret = -ENOMEM;
+		goto out_remove;
+	}
+
+	wl->stats.fw_stats_update = jiffies;
+
+	ret = cc33xx_debugfs_add_files(wl, rootdir);
+	if (ret < 0)
+		goto out_exit;
+
+	goto out;
+
+out_exit:
+	cc33xx_debugfs_exit(wl);
+
+out_remove:
+	debugfs_remove_recursive(rootdir);
+
+out:
+	return ret;
+}
+
+void cc33xx_debugfs_exit(struct cc33xx *wl)
+{
+	kfree(wl->stats.fw_stats);
+	wl->stats.fw_stats = NULL;
+}
diff --git drivers/net/wireless/ti/cc33xx/debugfs.h drivers/net/wireless/ti/cc33xx/debugfs.h
new file mode 100644
index 000000000000..fb413a260922
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/debugfs.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+#include "wlcore.h"
+
+__printf(4, 5) int cc33xx_format_buffer(char __user *userbuf, size_t count,
+					loff_t *ppos, char *fmt, ...);
+
+int cc33xx_debugfs_init(struct cc33xx *wl);
+void cc33xx_debugfs_exit(struct cc33xx *wl);
+void cc33xx_debugfs_reset(struct cc33xx *wl);
+void cc33xx_debugfs_update_stats(struct cc33xx *wl);
+
+#define DEBUGFS_FORMAT_BUFFER_SIZE 256
+
+#define DEBUGFS_READONLY_FILE(name, fmt, value...)			\
+static ssize_t name## _read(struct file *file, char __user *userbuf,	\
+			    size_t count, loff_t *ppos)			\
+{									\
+	struct cc33xx *wl = file->private_data;				\
+	return cc33xx_format_buffer(userbuf, count, ppos,		\
+				    fmt "\n", ##value);			\
+}									\
+									\
+static const struct file_operations name## _ops = {			\
+	.read = name## _read,						\
+	.open = simple_open,						\
+	.llseek	= generic_file_llseek,					\
+};
+
+#define DEBUGFS_ADD(name, parent)					\
+	do {								\
+		debugfs_create_file(#name, 0400, parent,		\
+				    wl, &name## _ops);			\
+	} while (0)
+
+
+#define DEBUGFS_ADD_PREFIX(prefix, name, parent)			\
+	do {								\
+		debugfs_create_file(#name, 0400, parent,		\
+				    wl, &prefix## _## name## _ops);	\
+	} while (0)
+
+#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type)		\
+static ssize_t sub## _ ##name## _read(struct file *file,		\
+				      char __user *userbuf,		\
+				      size_t count, loff_t *ppos)	\
+{									\
+	struct cc33xx *wl = file->private_data;				\
+	struct struct_type *stats = wl->stats.fw_stats;			\
+									\
+	cc33xx_debugfs_update_stats(wl);				\
+									\
+	return cc33xx_format_buffer(userbuf, count, ppos, fmt "\n",	\
+				    stats->sub.name);			\
+}									\
+									\
+static const struct file_operations sub## _ ##name## _ops = {		\
+	.read = sub## _ ##name## _read,					\
+	.open = simple_open,						\
+	.llseek	= generic_file_llseek,					\
+};
+
+#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type)		\
+static ssize_t sub## _ ##name## _read(struct file *file,		\
+				      char __user *userbuf,		\
+				      size_t count, loff_t *ppos)	\
+{									\
+	struct cc33xx *wl = file->private_data;				\
+	struct struct_type *stats = wl->stats.fw_stats;			\
+	char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = "";			\
+	int res, i;							\
+									\
+	cc33xx_debugfs_update_stats(wl);				\
+									\
+	for (i = 0; i < len; i++)					\
+		res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n",	\
+			       buf, i, stats->sub.name[i]);		\
+									\
+	return cc33xx_format_buffer(userbuf, count, ppos, "%s", buf);	\
+}									\
+									\
+static const struct file_operations sub## _ ##name## _ops = {		\
+	.read = sub## _ ##name## _read,					\
+	.open = simple_open,						\
+	.llseek	= generic_file_llseek,					\
+};
+
+#define DEBUGFS_FWSTATS_ADD(sub, name)					\
+	DEBUGFS_ADD(sub## _ ##name, stats)
+
+
+#endif /* CC33XX_DEBUGFS_H */
diff --git drivers/net/wireless/ti/cc33xx/event.c drivers/net/wireless/ti/cc33xx/event.c
new file mode 100644
index 000000000000..8b75ec46a258
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/event.c
@@ -0,0 +1,587 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include "wlcore.h"
+#include "debug.h"
+#include "io.h"
+#include "event.h"
+#include "ps.h"
+#include "scan.h"
+#include "cc33xx_80211.h"
+
+#define CC33XX_LOGGER_SDIO_BUFF_MAX	(0x1020)
+#define CC33XX_DATA_RAM_BASE_ADDRESS	(0x20000000)
+#define CC33XX_LOGGER_SDIO_BUFF_ADDR	(0x40159c)
+#define CC33XX_LOGGER_BUFF_OFFSET	(sizeof(struct fw_logger_information))
+#define CC33XX_LOGGER_READ_POINT_OFFSET	(12)
+
+
+struct cc33xx_event_mailbox {
+	__le32 events_vector;
+
+	u8 number_of_scan_results;
+	u8 number_of_sched_scan_results;
+
+	__le16 channel_switch_role_id_bitmap;
+
+	s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+
+	/* bitmap of removed links */
+	__le32 hlid_removed_bitmap;
+
+	/* rx ba constraint */
+	__le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */
+	__le16 rx_ba_allowed_bitmap;
+
+	/* bitmap of roc completed (by role id) */
+	__le16 roc_completed_bitmap;
+
+	/* bitmap of stations (by role id) with bss loss */
+	__le16 bss_loss_bitmap;
+
+	/* bitmap of stations (by HLID) which exceeded max tx retries */
+	__le16 tx_retry_exceeded_bitmap;
+
+	/* time sync high msb*/
+	__le16 time_sync_tsf_high_msb;
+
+	/* bitmap of inactive stations (by HLID) */
+	__le16 inactive_sta_bitmap;
+
+	/* time sync high lsb*/
+	__le16 time_sync_tsf_high_lsb;
+
+	/* rx BA win size indicated by RX_BA_WIN_SIZE_CHANGE_EVENT_ID */
+	u8 rx_ba_role_id;
+	u8 rx_ba_link_id;
+	u8 rx_ba_win_size;
+	u8 padding;
+
+	/* smart config */
+	u8 sc_ssid_len;
+	u8 sc_pwd_len;
+	u8 sc_token_len;
+	u8 padding1;
+	u8 sc_ssid[32];
+	u8 sc_pwd[64];
+	u8 sc_token[32];
+
+	/* smart config sync channel */
+	u8 sc_sync_channel;
+	u8 sc_sync_band;
+
+	/* time sync low msb*/
+	__le16 time_sync_tsf_low_msb;
+
+	/* radar detect */
+	u8 radar_channel;
+	u8 radar_type;
+
+	/* time sync low lsb*/
+	__le16 time_sync_tsf_low_lsb;
+
+} __packed;
+
+struct event_node{
+	struct llist_node node;
+	struct cc33xx_event_mailbox event_data;
+};
+
+void deffer_event(struct cc33xx *wl, 
+			const void *event_payload, size_t event_length)
+{
+	struct event_node* event_node;
+	bool ret;
+	
+	if (WARN_ON(event_length != sizeof event_node->event_data))
+		return;
+
+	event_node = kzalloc(sizeof *event_node, GFP_KERNEL);
+	if (WARN_ON(!event_node))
+		return;
+
+	memcpy(&event_node->event_data, 
+		event_payload, sizeof (event_node->event_data));
+
+	llist_add(&event_node->node, &wl->event_list);
+	ret = queue_work(wl->freezable_wq, &wl->irq_deferred_work);
+
+	cc33xx_debug(DEBUG_IRQ, "Queued deferred work (%d)", ret);
+}
+
+inline static struct llist_node* get_event_list(struct cc33xx *wl)
+{
+	struct llist_node* node;
+
+	node = llist_del_all(&wl->event_list);
+	if (!node)
+		return NULL;
+	
+	return llist_reverse_order(node);
+}
+
+void process_deferred_events(struct cc33xx *wl)
+{
+	struct event_node *event_node, *tmp;
+	struct llist_node *event_list;
+	u32 vector;
+		
+	event_list = get_event_list(wl);
+
+	llist_for_each_entry_safe(event_node, tmp, event_list, node){
+
+		struct cc33xx_event_mailbox *event_data;
+
+		event_data = &event_node->event_data;
+
+		print_hex_dump(KERN_DEBUG, "Deferred event dump:",
+			DUMP_PREFIX_OFFSET, 4, 4,
+			event_data, 64/*sizeof event_node->event_data*/, 
+			false);
+
+		vector = le32_to_cpu(event_node->event_data.events_vector);
+		cc33xx_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+		if (vector & SCAN_COMPLETE_EVENT_ID) {
+			cc33xx_debug(DEBUG_EVENT, "scan results: %d",
+				event_node->event_data.number_of_scan_results);
+
+			if (wl->scan_wlvif)
+				cc33xx_scan_completed(wl, wl->scan_wlvif);
+		}
+
+		if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+		{
+			wlcore_event_sched_scan_completed(wl, 1);
+		}
+
+		if (vector & BSS_LOSS_EVENT_ID)
+			wlcore_event_beacon_loss(wl,
+				le16_to_cpu(event_data->bss_loss_bitmap));
+
+		if (vector & MAX_TX_FAILURE_EVENT_ID)
+			wlcore_event_max_tx_failure(wl,
+				le16_to_cpu(event_data->tx_retry_exceeded_bitmap));
+
+		if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+			cc33xx_debug(DEBUG_EVENT,
+				"PERIODIC_SCAN_REPORT_EVENT (results %d)",
+				event_data->number_of_sched_scan_results);
+
+			wlcore_scan_sched_scan_results(wl);
+		}
+
+		if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+			wlcore_event_roc_complete(wl);
+		kfree(event_node);
+	}
+
+}
+
+void flush_deferred_event_list(struct cc33xx *wl)
+{
+	struct event_node *event_node, *tmp;
+	struct llist_node *event_list;
+		
+	event_list = get_event_list(wl);
+	llist_for_each_entry_safe(event_node, tmp, event_list, node){
+		cc33xx_debug(DEBUG_IRQ, "Freeing event");
+		kfree(event_node);
+	}
+}
+#define CC33XX_WAIT_EVENT_FAST_POLL_COUNT 20
+int wait_for_event_or_timeout(struct cc33xx *wl, u32 mask, bool *timeout)
+{
+	u32 event;
+	unsigned long timeout_time;
+	u16 poll_count = 0;
+	int ret = 0;
+	struct event_node *event_node, *tmp;
+	struct llist_node *event_list;
+	u32 vector;
+
+	*timeout = false;
+
+	timeout_time = jiffies + msecs_to_jiffies(CC33XX_EVENT_TIMEOUT);
+
+	do {
+		if (time_after(jiffies, timeout_time)) {
+			cc33xx_debug(DEBUG_CMD, "timeout waiting for event %d",
+				     (int)mask);
+			*timeout = true;
+			goto out;
+		}
+
+		poll_count++;
+		if (poll_count < CC33XX_WAIT_EVENT_FAST_POLL_COUNT)
+			usleep_range(50, 51);
+		else
+			usleep_range(1000, 5000);
+
+		vector = 0;
+		event_list = get_event_list(wl);
+		llist_for_each_entry_safe(event_node, tmp, event_list, node){
+			vector |= le32_to_cpu(event_node->event_data.events_vector);
+		}
+
+		event  = vector & mask;
+	} while (!event);
+
+out:
+
+	return ret; 
+}
+
+
+
+int cc33xx_wait_for_event(struct cc33xx *wl, enum wlcore_wait_event event,
+			  bool *timeout)
+{
+	u32 local_event;
+
+	switch (event) {
+	case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+		local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+		break;
+
+	case WLCORE_EVENT_DFS_CONFIG_COMPLETE:
+		local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+		break;
+
+	default:
+		/* event not implemented */
+		return 0;
+	}
+	return wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+
+int wlcore_event_fw_logger(struct cc33xx *wl)
+{
+	int ret;
+	struct fw_logger_information fw_log;
+	u8  *buffer;
+	u32 internal_fw_addrbase = CC33XX_DATA_RAM_BASE_ADDRESS;
+	u32 addr = CC33XX_LOGGER_SDIO_BUFF_ADDR;
+	u32 end_buff_addr = CC33XX_LOGGER_SDIO_BUFF_ADDR +
+				CC33XX_LOGGER_BUFF_OFFSET;
+	u32 available_len;
+	u32 actual_len;
+	u32 clear_addr;
+	size_t len;
+	u32 start_loc;
+
+	buffer = kzalloc(CC33XX_LOGGER_SDIO_BUFF_MAX, GFP_KERNEL);
+	if (!buffer) {
+		cc33xx_error("Fail to allocate fw logger memory");
+		fw_log.actual_buff_size = cpu_to_le32(0);
+		goto out;
+	}
+
+	ret = wlcore_read(wl, addr, buffer, CC33XX_LOGGER_SDIO_BUFF_MAX,
+			  false);
+	if (ret < 0) {
+		cc33xx_error("Fail to read logger buffer, error_id = %d",
+			     ret);
+		fw_log.actual_buff_size = cpu_to_le32(0);
+		goto free_out;
+	}
+
+	memcpy(&fw_log, buffer, sizeof(fw_log));
+
+	if (le32_to_cpu(fw_log.actual_buff_size) == 0)
+		goto free_out;
+
+	actual_len = le32_to_cpu(fw_log.actual_buff_size);
+	start_loc = (le32_to_cpu(fw_log.buff_read_ptr) -
+			internal_fw_addrbase) - addr;
+	end_buff_addr += le32_to_cpu(fw_log.max_buff_size);
+	available_len = end_buff_addr -
+			(le32_to_cpu(fw_log.buff_read_ptr) -
+				 internal_fw_addrbase);
+	actual_len = min(actual_len, available_len);
+	len = actual_len;
+
+	cc33xx_copy_fwlog(wl, &buffer[start_loc], len);
+	clear_addr = addr + start_loc + le32_to_cpu(fw_log.actual_buff_size) +
+			internal_fw_addrbase;
+
+	len = le32_to_cpu(fw_log.actual_buff_size) - len;
+	if (len) {
+		cc33xx_copy_fwlog(wl,
+				  &buffer[CC33XX_LOGGER_BUFF_OFFSET],
+				  len);
+		clear_addr = addr + CC33XX_LOGGER_BUFF_OFFSET + len +
+				internal_fw_addrbase;
+	}
+
+	/* double check that clear address and write pointer are the same */
+	if (clear_addr != le32_to_cpu(fw_log.buff_write_ptr)) {
+		cc33xx_error("Calculate of clear addr Clear = %x, write = %x",
+			     clear_addr, le32_to_cpu(fw_log.buff_write_ptr));
+	}
+
+	/* indicate FW about Clear buffer */
+	//ret = wlcore_write32(wl, addr + CC33XX_LOGGER_READ_POINT_OFFSET,
+	//			     fw_log.buff_write_ptr);
+free_out:
+	kfree(buffer);
+out:
+	return le32_to_cpu(fw_log.actual_buff_size);
+}
+
+void wlcore_event_rssi_trigger(struct cc33xx *wl, s8 *metric_arr)
+{
+	struct cc33xx_vif *wlvif;
+	struct ieee80211_vif *vif;
+	enum nl80211_cqm_rssi_threshold_event event;
+	s8 metric = metric_arr[0];
+
+	cc33xx_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
+
+	/* TODO: check actual multi-role support */
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		if (metric <= wlvif->rssi_thold)
+			event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+		else
+			event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+
+		vif = cc33xx_wlvif_to_vif(wlvif);
+		if (event != wlvif->last_rssi_event)
+			ieee80211_cqm_rssi_notify(vif, event, metric,
+						  GFP_KERNEL);
+		wlvif->last_rssi_event = event;
+	}
+}
+
+static void cc33xx_stop_ba_event(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+	if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
+		u8 hlid = wlvif->sta.hlid;
+		if (!wl->links[hlid].ba_bitmap)
+			return;
+		ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
+					     vif->bss_conf.bssid);
+	} else {
+		u8 hlid;
+		struct cc33xx_link *lnk;
+		for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
+				 wl->num_links) {
+			lnk = &wl->links[hlid];
+			if (!lnk->ba_bitmap)
+				continue;
+
+			ieee80211_stop_rx_ba_session(vif,
+						     lnk->ba_bitmap,
+						     lnk->addr);
+		}
+	}
+}
+
+void wlcore_event_soft_gemini_sense(struct cc33xx *wl, u8 enable)
+{
+	struct cc33xx_vif *wlvif;
+
+	if (enable) {
+		set_bit(CC33XX_FLAG_SOFT_GEMINI, &wl->flags);
+	} else {
+		clear_bit(CC33XX_FLAG_SOFT_GEMINI, &wl->flags);
+		cc33xx_for_each_wlvif_sta(wl, wlvif) {
+			cc33xx_recalc_rx_streaming(wl, wlvif);
+		}
+	}
+}
+
+void wlcore_event_sched_scan_completed(struct cc33xx *wl,
+				       u8 status)
+{
+	cc33xx_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
+		     status);
+
+	if (wl->mac80211_scan_stopped){
+		wl->mac80211_scan_stopped = false;
+	}
+	else{
+		if (wl->sched_vif) {
+			ieee80211_sched_scan_stopped(wl->hw);
+			wl->sched_vif = NULL;
+		}
+	}
+	
+}
+
+void wlcore_event_ba_rx_constraint(struct cc33xx *wl,
+				   unsigned long roles_bitmap,
+				   unsigned long allowed_bitmap)
+{
+	struct cc33xx_vif *wlvif;
+
+	cc33xx_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
+		     __func__, roles_bitmap, allowed_bitmap);
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		if (wlvif->role_id == CC33XX_INVALID_ROLE_ID ||
+		    !test_bit(wlvif->role_id , &roles_bitmap))
+			continue;
+
+		wlvif->ba_allowed = !!test_bit(wlvif->role_id,
+					       &allowed_bitmap);
+		if (!wlvif->ba_allowed)
+			cc33xx_stop_ba_event(wl, wlvif);
+	}
+}
+
+void wlcore_event_channel_switch(struct cc33xx *wl,
+				 unsigned long roles_bitmap,
+				 bool success)
+{
+	struct cc33xx_vif *wlvif;
+	struct ieee80211_vif *vif;
+
+	cc33xx_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
+		     __func__, roles_bitmap, success);
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		if (wlvif->role_id == CC33XX_INVALID_ROLE_ID ||
+		    !test_bit(wlvif->role_id , &roles_bitmap))
+			continue;
+
+		if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
+					&wlvif->flags))
+			continue;
+
+		vif = cc33xx_wlvif_to_vif(wlvif);
+
+		if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
+			ieee80211_chswitch_done(vif, success);
+			cancel_delayed_work(&wlvif->channel_switch_work);
+		} else {
+			set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags);
+			ieee80211_csa_finish(vif);
+		}
+	}
+}
+
+void wlcore_event_dummy_packet(struct cc33xx *wl)
+{
+	if (wl->plt) {
+		cc33xx_info("Got DUMMY_PACKET event in PLT mode.  FW bug, ignoring.");
+		return;
+	}
+
+	cc33xx_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
+	cc33xx_tx_dummy_packet(wl);
+}
+
+static void wlcore_disconnect_sta(struct cc33xx *wl, unsigned long sta_bitmap)
+{
+	u32 num_packets = wl->conf.host_conf.tx.max_tx_retries;
+	struct cc33xx_vif *wlvif;
+	struct ieee80211_vif *vif;
+	struct ieee80211_sta *sta;
+	const u8 *addr;
+	int h;
+
+	for_each_set_bit(h, &sta_bitmap, wl->num_links) {
+		bool found = false;
+		/* find the ap vif connected to this sta */
+		cc33xx_for_each_wlvif_ap(wl, wlvif) {
+			if (!test_bit(h, wlvif->ap.sta_hlid_map))
+				continue;
+			found = true;
+			break;
+		}
+		if (!found)
+			continue;
+
+		vif = cc33xx_wlvif_to_vif(wlvif);
+		addr = wl->links[h].addr;
+
+		rcu_read_lock();
+		sta = ieee80211_find_sta(vif, addr);
+		if (sta) {
+			cc33xx_debug(DEBUG_EVENT, "remove sta %d", h);
+			ieee80211_report_low_ack(sta, num_packets);
+		}
+		rcu_read_unlock();
+	}
+}
+
+void wlcore_event_max_tx_failure(struct cc33xx *wl, unsigned long sta_bitmap)
+{
+	cc33xx_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
+	wlcore_disconnect_sta(wl, sta_bitmap);
+}
+
+void wlcore_event_inactive_sta(struct cc33xx *wl, unsigned long sta_bitmap)
+{
+	cc33xx_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
+	wlcore_disconnect_sta(wl, sta_bitmap);
+}
+
+void wlcore_event_roc_complete(struct cc33xx *wl)
+{
+	cc33xx_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
+	if (wl->roc_vif)
+		ieee80211_ready_on_channel(wl->hw);
+}
+
+void wlcore_event_beacon_loss(struct cc33xx *wl, unsigned long roles_bitmap)
+{
+	/*
+	 * We are HW_MONITOR device. On beacon loss - queue
+	 * connection loss work. Cancel it on REGAINED event.
+	 */
+	struct cc33xx_vif *wlvif;
+	struct ieee80211_vif *vif;
+	int delay = wl->conf.host_conf.conn.synch_fail_thold *
+				wl->conf.host_conf.conn.bss_lose_timeout;
+
+	cc33xx_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
+
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		if (wlvif->role_id == CC33XX_INVALID_ROLE_ID ||
+		    !test_bit(wlvif->role_id , &roles_bitmap))
+			continue;
+
+		vif = cc33xx_wlvif_to_vif(wlvif);
+
+		/* don't attempt roaming in case of p2p */
+		if (wlvif->p2p) {
+			ieee80211_connection_loss(vif);
+			continue;
+		}
+
+		/*
+		 * if the work is already queued, it should take place.
+		 * We don't want to delay the connection loss
+		 * indication any more.
+		 */
+		ieee80211_queue_delayed_work(wl->hw,
+					     &wlvif->connection_loss_work,
+					     msecs_to_jiffies(delay));
+
+		ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL);
+	}
+}
+
+int cc33xx_event_unmask(struct cc33xx *wl)
+{
+	int ret;
+
+	cc33xx_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask);
+	ret = cc33xx_acx_event_mbox_mask(wl, ~(wl->event_mask));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
diff --git drivers/net/wireless/ti/cc33xx/event.h drivers/net/wireless/ti/cc33xx/event.h
new file mode 100644
index 000000000000..5b22cff49141
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/event.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __EVENT_H__
+#define __EVENT_H__
+
+/*
+ * Mbox events
+ *
+ * The event mechanism is based on a pair of event buffers (buffers A and
+ * B) at fixed locations in the target's memory. The host processes one
+ * buffer while the other buffer continues to collect events. If the host
+ * is not processing events, an interrupt is issued to signal that a buffer
+ * is ready. Once the host is done with processing events from one buffer,
+ * it signals the target (with an ACK interrupt) that the event buffer is
+ * free.
+ */
+
+enum {
+	RSSI_SNR_TRIGGER_0_EVENT_ID              = BIT(0),
+	RSSI_SNR_TRIGGER_1_EVENT_ID              = BIT(1),
+	RSSI_SNR_TRIGGER_2_EVENT_ID              = BIT(2),
+	RSSI_SNR_TRIGGER_3_EVENT_ID              = BIT(3),
+	RSSI_SNR_TRIGGER_4_EVENT_ID              = BIT(4),
+	RSSI_SNR_TRIGGER_5_EVENT_ID              = BIT(5),
+	RSSI_SNR_TRIGGER_6_EVENT_ID              = BIT(6),
+	RSSI_SNR_TRIGGER_7_EVENT_ID              = BIT(7),
+
+	EVENT_MBOX_ALL_EVENT_ID			 = 0x7fffffff,
+};
+
+enum {
+	SCAN_COMPLETE_EVENT_ID                   = BIT(8),
+	RADAR_DETECTED_EVENT_ID                  = BIT(9),
+	CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(10),
+	BSS_LOSS_EVENT_ID                        = BIT(11),
+	MAX_TX_FAILURE_EVENT_ID                  = BIT(12),
+	DUMMY_PACKET_EVENT_ID                    = BIT(13),
+	INACTIVE_STA_EVENT_ID                    = BIT(14),
+	PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(15),
+	PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(16),
+	BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(17),
+	REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(18),
+	DFS_CHANNELS_CONFIG_COMPLETE_EVENT       = BIT(19),
+	PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(20),
+	RX_BA_WIN_SIZE_CHANGE_EVENT_ID           = BIT(21),
+	SMART_CONFIG_SYNC_EVENT_ID               = BIT(22),
+	SMART_CONFIG_DECODE_EVENT_ID             = BIT(23),
+	TIME_SYNC_EVENT_ID                       = BIT(24),
+	FW_LOGGER_INDICATION			= BIT(25),
+};
+
+/* events the driver might want to wait for */
+enum wlcore_wait_event {
+	WLCORE_EVENT_ROLE_STOP_COMPLETE,
+	WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+	WLCORE_EVENT_DFS_CONFIG_COMPLETE
+};
+
+enum {
+	EVENT_ENTER_POWER_SAVE_FAIL = 0,
+	EVENT_ENTER_POWER_SAVE_SUCCESS,
+};
+
+
+#define NUM_OF_RSSI_SNR_TRIGGERS 8
+
+struct fw_logger_information {
+	__le32 max_buff_size;
+	__le32 actual_buff_size;
+	__le32 num_trace_drop;
+	__le32 buff_read_ptr;
+	__le32 buff_write_ptr;
+} __packed;
+
+struct cc33xx;
+
+int cc33xx_event_unmask(struct cc33xx *wl);
+
+void wlcore_event_soft_gemini_sense(struct cc33xx *wl, u8 enable);
+void wlcore_event_sched_scan_completed(struct cc33xx *wl,
+				       u8 status);
+void wlcore_event_ba_rx_constraint(struct cc33xx *wl,
+				   unsigned long roles_bitmap,
+				   unsigned long allowed_bitmap);
+void wlcore_event_channel_switch(struct cc33xx *wl,
+				 unsigned long roles_bitmap,
+				 bool success);
+void wlcore_event_beacon_loss(struct cc33xx *wl, unsigned long roles_bitmap);
+void wlcore_event_dummy_packet(struct cc33xx *wl);
+void wlcore_event_max_tx_failure(struct cc33xx *wl, unsigned long sta_bitmap);
+void wlcore_event_inactive_sta(struct cc33xx *wl, unsigned long sta_bitmap);
+void wlcore_event_roc_complete(struct cc33xx *wl);
+void wlcore_event_rssi_trigger(struct cc33xx *wl, s8 *metric_arr);
+int  wlcore_event_fw_logger(struct cc33xx *wl);
+
+int cc33xx_wait_for_event(struct cc33xx *wl, enum wlcore_wait_event event,
+			  bool *timeout);
+
+void deffer_event(struct cc33xx *wl, const void *event_payload, size_t event_length);
+void process_deferred_events(struct cc33xx *wl);
+void flush_deferred_event_list(struct cc33xx *wl);
+#endif
diff --git drivers/net/wireless/ti/cc33xx/ini.h drivers/net/wireless/ti/cc33xx/ini.h
new file mode 100644
index 000000000000..4379aa25e0e0
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/ini.h
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of wl1271
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __INI_H__
+#define __INI_H__
+
+#define GENERAL_SETTINGS_DRPW_LPD 0xc0
+#define SCRATCH_ENABLE_LPD        BIT(25)
+
+#define WL1271_INI_MAX_SMART_REFLEX_PARAM 16
+
+struct wl1271_ini_general_params {
+	u8 ref_clock;
+	u8 settling_time;
+	u8 clk_valid_on_wakeup;
+	u8 dc2dc_mode;
+	u8 dual_mode_select;
+	u8 tx_bip_fem_auto_detect;
+	u8 tx_bip_fem_manufacturer;
+	u8 general_settings;
+	u8 sr_state;
+	u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+	u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+	u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+} __packed;
+
+#define WL128X_INI_MAX_SETTINGS_PARAM 4
+
+struct wl128x_ini_general_params {
+	u8 ref_clock;
+	u8 settling_time;
+	u8 clk_valid_on_wakeup;
+	u8 tcxo_ref_clock;
+	u8 tcxo_settling_time;
+	u8 tcxo_valid_on_wakeup;
+	u8 tcxo_ldo_voltage;
+	u8 xtal_itrim_val;
+	u8 platform_conf;
+	u8 dual_mode_select;
+	u8 tx_bip_fem_auto_detect;
+	u8 tx_bip_fem_manufacturer;
+	u8 general_settings[WL128X_INI_MAX_SETTINGS_PARAM];
+	u8 sr_state;
+	u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+	u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+	u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+} __packed;
+
+#define WL1271_INI_RSSI_PROCESS_COMPENS_SIZE 15
+
+struct wl1271_ini_band_params_2 {
+	u8 rx_trace_insertion_loss;
+	u8 tx_trace_loss;
+	u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
+} __packed;
+
+#define WL1271_INI_CHANNEL_COUNT_2 14
+
+struct wl128x_ini_band_params_2 {
+	u8 rx_trace_insertion_loss;
+	u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_2];
+	u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
+} __packed;
+
+#define WL1271_INI_RATE_GROUP_COUNT 6
+
+struct wl1271_ini_fem_params_2 {
+	__le16 tx_bip_ref_pd_voltage;
+	u8 tx_bip_ref_power;
+	u8 tx_bip_ref_offset;
+	u8 tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2];
+	u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2];
+	u8 tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_ibias[WL1271_INI_RATE_GROUP_COUNT];
+	u8 rx_fem_insertion_loss;
+	u8 degraded_low_to_normal_thr;
+	u8 normal_to_degraded_high_thr;
+} __packed;
+
+#define WL128X_INI_RATE_GROUP_COUNT 7
+/* low and high temperatures */
+#define WL128X_INI_PD_VS_TEMPERATURE_RANGES 2
+
+struct wl128x_ini_fem_params_2 {
+	__le16 tx_bip_ref_pd_voltage;
+	u8 tx_bip_ref_power;
+	u8 tx_bip_ref_offset;
+	u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2];
+	u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2];
+	u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT + 1];
+	u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_2];
+	u8 tx_pd_vs_temperature[WL128X_INI_PD_VS_TEMPERATURE_RANGES];
+	u8 rx_fem_insertion_loss;
+	u8 degraded_low_to_normal_thr;
+	u8 normal_to_degraded_high_thr;
+} __packed;
+
+#define WL1271_INI_CHANNEL_COUNT_5 35
+#define WL1271_INI_SUB_BAND_COUNT_5 7
+
+struct wl1271_ini_band_params_5 {
+	u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_trace_loss[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
+} __packed;
+
+struct wl128x_ini_band_params_5 {
+	u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_5];
+	u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
+} __packed;
+
+struct wl1271_ini_fem_params_5 {
+	__le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5];
+	u8 tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT];
+	u8 tx_ibias[WL1271_INI_RATE_GROUP_COUNT];
+	u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 degraded_low_to_normal_thr;
+	u8 normal_to_degraded_high_thr;
+} __packed;
+
+struct wl128x_ini_fem_params_5 {
+	__le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5];
+	u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT];
+	u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_5];
+	u8 tx_pd_vs_temperature[WL1271_INI_SUB_BAND_COUNT_5 *
+		WL128X_INI_PD_VS_TEMPERATURE_RANGES];
+	u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
+	u8 degraded_low_to_normal_thr;
+	u8 normal_to_degraded_high_thr;
+} __packed;
+
+/* NVS data structure */
+#define WL1271_INI_NVS_SECTION_SIZE		     468
+
+/* We have four FEM module types: 0-RFMD, 1-TQS, 2-SKW, 3-TQS_HP */
+#define WL1271_INI_FEM_MODULE_COUNT                  4
+
+/*
+ * In NVS we only store two FEM module entries -
+ *	  FEM modules 0,2,3 are stored in entry 0
+ *	  FEM module 1 is stored in entry 1
+ */
+#define WL12XX_NVS_FEM_MODULE_COUNT                  2
+
+#define WL12XX_FEM_TO_NVS_ENTRY(ini_fem_module)      \
+	((ini_fem_module) == 1 ? 1 : 0)
+
+#define WL1271_INI_LEGACY_NVS_FILE_SIZE              800
+
+struct wl1271_nvs_file {
+	/* NVS section - must be first! */
+	u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
+
+	/* INI section */
+	struct wl1271_ini_general_params general_params;
+	u8 padding1;
+	struct wl1271_ini_band_params_2 stat_radio_params_2;
+	u8 padding2;
+	struct {
+		struct wl1271_ini_fem_params_2 params;
+		u8 padding;
+	} dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT];
+	struct wl1271_ini_band_params_5 stat_radio_params_5;
+	u8 padding3;
+	struct {
+		struct wl1271_ini_fem_params_5 params;
+		u8 padding;
+	} dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT];
+} __packed;
+
+struct wl128x_nvs_file {
+	/* NVS section - must be first! */
+	u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
+
+	/* INI section */
+	struct wl128x_ini_general_params general_params;
+	u8 fem_vendor_and_options;
+	struct wl128x_ini_band_params_2 stat_radio_params_2;
+	u8 padding2;
+	struct {
+		struct wl128x_ini_fem_params_2 params;
+		u8 padding;
+	} dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT];
+	struct wl128x_ini_band_params_5 stat_radio_params_5;
+	u8 padding3;
+	struct {
+		struct wl128x_ini_fem_params_5 params;
+		u8 padding;
+	} dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT];
+} __packed;
+#endif
diff --git drivers/net/wireless/ti/cc33xx/init.c drivers/net/wireless/ti/cc33xx/init.c
new file mode 100644
index 000000000000..f99e8f8ade5c
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/init.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "debug.h"
+#include "init.h"
+#include "cc33xx_80211.h"
+#include "acx.h"
+#include "cmd.h"
+#include "tx.h"
+#include "io.h"
+
+#define PG2_CHIP_VERSION    2
+
+static int cc33xx_ap_init_deauth_template(struct cc33xx *wl,
+					  struct cc33xx_vif *wlvif)
+{
+	struct cc33xx_disconn_template *tmpl;
+	int ret;
+	u32 rate;
+
+	tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL);
+	if (!tmpl) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+					     IEEE80211_STYPE_DEAUTH);
+
+	rate = cc33xx_tx_min_rate_get(wl, wlvif->basic_rate_set);
+	ret = cc33xx_cmd_template_set(wl, wlvif->role_id,
+				      CMD_TEMPL_DEAUTH_AP,
+				      tmpl, sizeof(*tmpl), 0, rate);
+
+out:
+	kfree(tmpl);
+	return ret;
+}
+
+static int cc33xx_ap_init_null_template(struct cc33xx *wl,
+					struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct ieee80211_hdr_3addr *nullfunc;
+	int ret;
+	u32 rate;
+
+	nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL);
+	if (!nullfunc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+					      IEEE80211_STYPE_NULLFUNC |
+					      IEEE80211_FCTL_FROMDS);
+
+	/* nullfunc->addr1 is filled by FW */
+
+	memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
+	memcpy(nullfunc->addr3, vif->addr, ETH_ALEN);
+
+	rate = cc33xx_tx_min_rate_get(wl, wlvif->basic_rate_set);
+	ret = cc33xx_cmd_template_set(wl, wlvif->role_id,
+				      CMD_TEMPL_NULL_DATA, nullfunc,
+				      sizeof(*nullfunc), 0, rate);
+
+out:
+	kfree(nullfunc);
+	return ret;
+}
+
+static int cc33xx_ap_init_qos_null_template(struct cc33xx *wl,
+					    struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct ieee80211_qos_hdr *qosnull;
+	int ret;
+	u32 rate;
+
+	qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL);
+	if (!qosnull) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+					     IEEE80211_STYPE_QOS_NULLFUNC |
+					     IEEE80211_FCTL_FROMDS);
+
+	/* qosnull->addr1 is filled by FW */
+
+	memcpy(qosnull->addr2, vif->addr, ETH_ALEN);
+	memcpy(qosnull->addr3, vif->addr, ETH_ALEN);
+
+	rate = cc33xx_tx_min_rate_get(wl, wlvif->basic_rate_set);
+	ret = cc33xx_cmd_template_set(wl, wlvif->role_id,
+				      CMD_TEMPL_QOS_NULL_DATA, qosnull,
+				      sizeof(*qosnull), 0, rate);
+
+out:
+	kfree(qosnull);
+	return ret;
+}
+
+static int cc33xx_init_phy_vif_config(struct cc33xx *wl,
+					    struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	ret = cc33xx_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME);
+	if (ret < 0)
+		return ret;
+
+	ret = cc33xx_acx_service_period_timeout(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	ret = cc33xx_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cc33xx_init_sta_beacon_filter(struct cc33xx *wl,
+					 struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	ret = cc33xx_acx_beacon_filter_table(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	/* disable beacon filtering until we get the first beacon */
+	ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, false);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int cc33xx_init_energy_detection(struct cc33xx *wl)
+{
+	int ret;
+
+	ret = cc33xx_acx_cca_threshold(wl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cc33xx_init_beacon_broadcast(struct cc33xx *wl,
+					struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	ret = cc33xx_acx_bcn_dtim_options(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/* generic sta initialization (non vif-specific) */
+int cc33xx_sta_hw_init(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	/* PS config */
+	ret = cc33xx_acx_config_ps(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/* generic ap initialization (non vif-specific) */
+static int cc33xx_ap_hw_init(struct cc33xx *wl)
+{
+	int ret;
+	/* configure AP sleep, if enabled */
+	ret = cc33xx_acx_ap_sleep(wl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int cc33xx_ap_init_templates(struct cc33xx *wl, struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+
+	ret = cc33xx_ap_init_deauth_template(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	ret = cc33xx_ap_init_null_template(wl, vif);
+	if (ret < 0)
+		return ret;
+
+	ret = cc33xx_ap_init_qos_null_template(wl, vif);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * when operating as AP we want to receive external beacons for
+	 * configuring ERP protection.
+	 */
+	ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, false);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cc33xx_ap_hw_init_post_mem(struct cc33xx *wl,
+				      struct ieee80211_vif *vif)
+{
+	return cc33xx_ap_init_templates(wl, vif);
+}
+
+
+
+static int cc33xx_set_ba_policies(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	/* Reset the BA RX indicators */
+	wlvif->ba_allowed = true;
+	wl->ba_rx_session_count = 0;
+
+	/* BA is supported in STA/AP modes */
+	if (wlvif->bss_type != BSS_TYPE_AP_BSS &&
+	    wlvif->bss_type != BSS_TYPE_STA_BSS) {
+		wlvif->ba_support = false;
+		return 0;
+	}
+
+	wlvif->ba_support = true;
+
+	/* 802.11n initiator BA session setting */
+	return cc33xx_acx_set_ba_initiator_policy(wl, wlvif);
+}
+
+/* Applies when MAC address is other than 0x0.
+ * Routine for actual search in file 
+ * data_ptr returned contains the pointer to entry. */
+static bool find_calibration_entry(u8 *id,
+				  u8 **data_ptr,
+				  u8 *stop_address)
+{
+	u8 default_mac_address[ETH_ALEN] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+	struct calibration_header *calibration_header;
+	struct calibration_header_fw *calibration_header_fw;
+	u8 *default_calibration = NULL;
+	int compare_result = 0;
+	bool mac_match = false;
+	bool valid_data = false;
+
+	while (*data_ptr < stop_address)
+	{
+		// Cast to a struct for convenient fields reading
+		calibration_header = (struct calibration_header *)(*data_ptr);
+		calibration_header_fw = &(calibration_header->cal_header_fw);
+
+		if (le16_to_cpu(calibration_header->static_pattern) != 0x7F7F) {
+		cc33xx_debug(DEBUG_BOOT, "Problem with sync pattern, read: %x",
+					 le16_to_cpu(calibration_header->static_pattern));
+		break;
+		}
+
+		// Compare with actual mac address
+		compare_result = memcmp(calibration_header_fw->chip_id, id,
+					ETH_ALEN);
+		if (0 == compare_result) {
+			mac_match = true;
+			*data_ptr = (u8 *)calibration_header;
+			break;
+		}
+
+		// Compare with default mac address, if it's found save it for
+		// later if necessary (if no calibration for id is found)
+		compare_result = memcmp(calibration_header_fw->chip_id,
+					default_mac_address,
+					ETH_ALEN);
+		if (0 == compare_result)
+			default_calibration = (u8 *)calibration_header;
+
+		// advance ptr by specified payload length to next entry in file
+		*data_ptr = (u8 *)((u32)calibration_header
+				+ sizeof(struct calibration_header)
+				+ le16_to_cpu(calibration_header_fw->length));
+	}
+
+	if (false == mac_match) {
+		if (NULL != default_calibration) {
+			cc33xx_warning("No calibration for device address, "
+			               "using default calibration" 
+			       	       "(labeled mac FF:FF:FF:FF:FF:FF)");
+			// Take default calibration
+			*data_ptr = default_calibration;
+			valid_data = true;
+		} else {
+			cc33xx_debug(DEBUG_BOOT,"Can't find device's static" 
+				    " calibration data in calibration file and" 
+				    " cannot find default calibration");
+			valid_data = false;
+		}
+	} else {
+		valid_data = true;
+		cc33xx_debug(DEBUG_BOOT, "Calibration MAC address match!");
+	}
+	return valid_data;
+}
+
+int download_static_calibration_data(struct cc33xx *wl)
+{
+	int ret;
+	const struct firmware *fw = NULL;
+	const char *calibration_file = "ti-connectivity/static_calibration.bin";
+	u8 *mac_address;
+	bool valid_calibration_data = false;
+	bool file_loaded;
+	u8 *file_ptr = NULL;
+	u8 *calibration_entry_ptr = NULL;
+	struct calibration_file_header *file_header = NULL;
+	u8 *stop_search_address;
+
+	if(wl->pg_version >= PG2_CHIP_VERSION)
+	{
+		cc33xx_debug(DEBUG_BOOT, "Chip is PG2, No static calibration needed");
+		return 1;
+	}
+		
+
+	ret = request_firmware(&fw, calibration_file, wl->dev);
+	if (ret < 0) {
+		cc33xx_warning("Could not get firmware %s: %d,"
+				" proceeding with no calibration",
+				calibration_file, ret);
+		valid_calibration_data = false;
+		file_loaded = false;
+		ret = 0; /* Don't kill driver over this */
+		goto out;
+	} else {
+		file_loaded = true;
+	}
+	file_ptr = (u8 *)fw->data;
+
+    	file_header = (struct calibration_file_header *)file_ptr;
+	cc33xx_debug(DEBUG_BOOT, "Parsing static calibration file version: %d, "
+			 	 "payload struct ver: %d, entries count: %d, "
+				 "file size: %d",
+			 	 file_header->file_version, 
+				 file_header->payload_struct_version,
+			 	 le16_to_cpu(file_header->entries_count),
+				 fw->size);
+    
+	/* Limit the search to the file's length and skip 4 file header bytes */
+	mac_address = (u8 *)wl->efuse_mac_address;
+	calibration_entry_ptr = file_ptr
+			       	+ sizeof(struct calibration_file_header);
+	stop_search_address = file_ptr + fw->size;
+	valid_calibration_data = find_calibration_entry(mac_address,
+			       				&calibration_entry_ptr,
+			       				stop_search_address);
+
+out:
+	ret = cc33xx_acx_static_calibration_configure(wl,
+						      file_header,
+						      calibration_entry_ptr,
+						      valid_calibration_data);
+	if (file_loaded)
+		release_firmware(fw);
+
+	return ret;
+}
+
+/* vif-specifc initialization */
+static int cc33xx_init_sta_role(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+	
+	ret = cc33xx_acx_group_address_tbl(wl, wlvif, true, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	/* Initialize connection monitoring thresholds */
+	ret = cc33xx_acx_conn_monit_params(wl, wlvif, false);
+	if (ret < 0)
+		return ret;
+
+	/* Beacon filtering */
+	ret = cc33xx_init_sta_beacon_filter(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	/* Beacons and broadcast settings */
+	ret = cc33xx_init_beacon_broadcast(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	/* Configure rssi/snr averaging weights */
+	ret = cc33xx_acx_rssi_snr_avg_weights(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+
+/* vif-specific initialization */
+static int cc33xx_init_ap_role(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	ret = cc33xx_acx_ap_max_tx_retry(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	/* initialize Tx power */
+	ret = cc33xx_acx_tx_power(wl, wlvif, wlvif->power_level);
+	if (ret < 0)
+		return ret;
+
+	if (wl->radar_debug_mode)
+		wlcore_cmd_generic_cfg(wl, wlvif,
+				       WLCORE_CFG_FEATURE_RADAR_DEBUG,
+				       wl->radar_debug_mode, 0);
+
+	return 0;
+}
+
+int cc33xx_init_vif_specific(struct cc33xx *wl, struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct conf_tx_ac_category *conf_ac;
+	struct conf_tx_tid *conf_tid;
+	struct conf_tx_ac_category ac_conf[4];
+	struct conf_tx_tid tid_conf[8];
+	struct conf_tx_ac_category* p_wl_host_ac_conf = &wl->conf.host_conf.tx.ac_conf0;
+	struct conf_tx_tid* p_wl_host_tid_conf = &wl->conf.host_conf.tx.tid_conf0;
+	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+	u8 ps_scheme = wl->conf.mac.ps_scheme;
+	int ret, i;
+	
+	/* consider all existing roles before configuring psm. */
+
+	if (wl->ap_count == 0 && is_ap) { /* first AP */
+		ret = cc33xx_acx_sleep_auth(wl, CC33XX_PSM_ELP);
+		if (ret < 0)
+			return ret;
+
+		/* unmask ap events */
+		wl->event_mask |= wl->ap_event_mask;
+		ret = cc33xx_event_unmask(wl);
+		if (ret < 0)
+			return ret;
+	/* first STA, no APs */
+	} else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) {
+		u8 sta_auth = wl->conf.host_conf.conn.sta_sleep_auth;
+		/* Configure for power according to debugfs */
+		if (sta_auth != CC33XX_PSM_ILLEGAL)
+			ret = cc33xx_acx_sleep_auth(wl, sta_auth);
+		/* Configure for ELP power saving */
+		else
+			ret = cc33xx_acx_sleep_auth(wl, CC33XX_PSM_ELP);
+
+		if (ret < 0)
+			return ret;
+	}
+
+
+
+	/* Mode specific init */
+	if (is_ap) {
+		ret = cc33xx_ap_hw_init(wl);
+		if (ret < 0)
+			return ret;
+
+		ret = cc33xx_init_ap_role(wl, wlvif);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = cc33xx_sta_hw_init(wl, wlvif);
+		if (ret < 0)
+			return ret;
+
+		ret = cc33xx_init_sta_role(wl, wlvif);
+		if (ret < 0)
+			return ret;
+	}
+
+	cc33xx_init_phy_vif_config(wl, wlvif);
+
+	/* Default TID/AC configuration */
+	BUG_ON(wl->conf.host_conf.tx.tid_conf_count != wl->conf.host_conf.tx.ac_conf_count);
+	memcpy(ac_conf,p_wl_host_ac_conf,4*sizeof(struct conf_tx_ac_category));
+	memcpy(tid_conf,p_wl_host_tid_conf,8*sizeof(struct conf_tx_tid));
+
+	for (i = 0; i < wl->conf.host_conf.tx.tid_conf_count; i++) {
+		conf_ac =  &ac_conf[i];
+		conf_tid = &tid_conf[i];
+
+		/* If no ps poll is used, send legacy ps scheme in cmd */
+		if (ps_scheme == PS_SCHEME_NOPSPOLL)
+			ps_scheme = PS_SCHEME_LEGACY;
+
+		//TODO: RazB - need to configure MUEDCA
+        	ret = cc33xx_tx_param_cfg(wl, wlvif, conf_ac->ac,
+        	            conf_ac->cw_min, conf_ac->cw_max,
+        	            conf_ac->aifsn, conf_ac->tx_op_limit, false,
+        	            ps_scheme, conf_ac->is_mu_edca,
+			    conf_ac->mu_edca_aifs, conf_ac->mu_edca_ecw_min_max,
+			    conf_ac->mu_edca_timer);
+	
+        	if (ret < 0)
+        	        return ret;
+	}
+
+	/* Configure HW encryption */
+	ret = cc33xx_acx_feature_cfg(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	/* Mode specific init - post mem init */
+	if (is_ap)
+		ret = cc33xx_ap_hw_init_post_mem(wl, vif);
+	else
+
+	if (ret < 0)
+		return ret;
+
+	/* Configure initiator BA sessions policies */
+	ret = cc33xx_set_ba_policies(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int cc33xx_hw_init(struct cc33xx *wl)
+{
+	int ret = 0;
+	ret = cc33xx_acx_init_mem_config(wl);
+	
+	cc33xx_debug(DEBUG_CC33xx, "Skipping cc33xx_hw_init");	
+	cc33xx_debug(DEBUG_TX, "available tx blocks: %d", 16);
+	wl->last_fw_rls_idx = 0;
+	wl->partial_rx.status = CURR_RX_START;
+	return 0;
+}
+
+int cc33xx_download_ini_params_and_wait(struct cc33xx *wl)
+{
+	struct cc33xx_cmd_ini_params_download *cmd;
+	size_t command_size = ALIGN((sizeof(*cmd) + sizeof(wl->conf)),4);
+	int ret;
+
+	if (wl->conf.core.enable_FlowCtrl == 0){
+		cc33xx_warning("flow control disable - BLE will not work");
+	}
+	
+	cc33xx_set_max_buffer_size(wl,INI_MAX_BUFFER_SIZE);
+
+	cc33xx_debug(DEBUG_ACX, "Downloading INI Params and Configurations to FW, INI Bin File Payload Length: %d",sizeof(wl->conf));
+	cmd = kzalloc(command_size, GFP_KERNEL);
+	if (!cmd) {
+		cc33xx_error("INI Params Download: "
+				"process failed due to memory allocation "
+				"failure");
+		cc33xx_set_max_buffer_size(wl,CMD_MAX_BUFFER_SIZE);
+		return -ENOMEM;
+	}
+
+    cmd->length = cpu_to_le32(sizeof(wl->conf));
+	
+	/* copy INI file params payload */
+	memcpy((cmd->payload), &(wl->conf),
+	       sizeof(wl->conf));
+
+
+	ret = cc33xx_cmd_send(wl,CMD_DOWNLOAD_INI_PARAMS,cmd,command_size,0);
+	if (ret < 0)
+		cc33xx_warning("download INI params to FW command sending failed: %d", ret);
+	else
+		cc33xx_debug(DEBUG_BOOT, "INI Params downloaded successfully");	
+	
+
+	cc33xx_set_max_buffer_size(wl,CMD_MAX_BUFFER_SIZE);
+	kfree(cmd);
+	return ret;
+
+}
diff --git drivers/net/wireless/ti/cc33xx/init.h drivers/net/wireless/ti/cc33xx/init.h
new file mode 100644
index 000000000000..4c5583e97f43
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/init.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __INIT_H__
+#define __INIT_H__
+
+#include "wlcore.h"
+
+int cc33xx_init_energy_detection(struct cc33xx *wl);
+int cc33xx_hw_init(struct cc33xx *wl);
+int cc33xx_download_ini_params_and_wait(struct cc33xx *wl);
+int cc33xx_init_vif_specific(struct cc33xx *wl, struct ieee80211_vif *vif);
+int cc33xx_ap_init_templates(struct cc33xx *wl, struct ieee80211_vif *vif);
+int cc33xx_sta_hw_init(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int download_static_calibration_data(struct cc33xx *wl);
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/io.c drivers/net/wireless/ti/cc33xx/io.c
new file mode 100644
index 000000000000..8fd32592633d
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/io.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "cc33xx_80211.h"
+#include "io.h"
+#include "tx.h"
+
+bool cc33xx_set_block_size(struct cc33xx *wl)
+{
+	if (wl->if_ops->set_block_size) {
+		wl->if_ops->set_block_size(wl->dev, CC33XX_BUS_BLOCK_SIZE);
+		cc33xx_debug(DEBUG_CC33xx, 
+			"Set BLKsize to %d", CC33XX_BUS_BLOCK_SIZE);
+		return true;
+	}
+	else
+		cc33xx_debug(DEBUG_CC33xx, "Could not set BLKsize");
+	return false;
+}
+
+void wlcore_disable_interrupts_nosync(struct cc33xx *wl)
+{
+	wl->if_ops->disable_irq(wl->dev);
+}
+
+void wlcore_irq(void *cookie);
+void wlcore_enable_interrupts(struct cc33xx *wl)
+{
+	wl->if_ops->enable_irq(wl->dev);
+
+	printk(KERN_DEBUG "IBI_WA: Read core status");
+	wlcore_irq(wl);
+	printk(KERN_DEBUG "IBI_WA: Core status processed");
+}
+
+void cc33xx_io_reset(struct cc33xx *wl)
+{
+	if (wl->if_ops->reset)
+		wl->if_ops->reset(wl->dev);
+}
+
+void cc33xx_io_init(struct cc33xx *wl)
+{
+	if (wl->if_ops->init)
+		wl->if_ops->init(wl->dev);
+}
diff --git drivers/net/wireless/ti/cc33xx/io.h drivers/net/wireless/ti/cc33xx/io.h
new file mode 100644
index 000000000000..1d5f48d6deda
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/io.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __IO_H__
+#define __IO_H__
+
+#include <linux/irqreturn.h>
+
+struct cc33xx;
+
+void wlcore_disable_interrupts_nosync(struct cc33xx *wl);
+void wlcore_enable_interrupts(struct cc33xx *wl);
+
+void cc33xx_io_reset(struct cc33xx *wl);
+void cc33xx_io_init(struct cc33xx *wl);
+int wlcore_translate_addr(struct cc33xx *wl, int addr);
+
+/* Raw target IO, address is not translated */
+static inline int __must_check wlcore_raw_write(struct cc33xx *wl, int addr,
+						void *buf, size_t len,
+						bool fixed)
+{
+	int ret;
+
+	if (test_bit(CC33XX_FLAG_IO_FAILED, &wl->flags) ||
+	    WARN_ON((test_bit(CC33XX_FLAG_IN_ELP, &wl->flags) &&
+		     addr != HW_ACCESS_ELP_CTRL_REG)))
+		return -EIO;
+
+	ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed);
+	if (ret && wl->state != WLCORE_STATE_OFF)
+		set_bit(CC33XX_FLAG_IO_FAILED, &wl->flags);
+
+	return ret;
+}
+
+static inline int __must_check wlcore_raw_read(struct cc33xx *wl, int addr,
+					       void *buf, size_t len,
+					       bool fixed)
+{
+	int ret;
+
+	if (test_bit(CC33XX_FLAG_IO_FAILED, &wl->flags) ||
+	    WARN_ON((test_bit(CC33XX_FLAG_IN_ELP, &wl->flags) &&
+		     addr != HW_ACCESS_ELP_CTRL_REG)))
+		return -EIO;
+
+	ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed);
+	if (ret && wl->state != WLCORE_STATE_OFF)
+		set_bit(CC33XX_FLAG_IO_FAILED, &wl->flags);
+
+	return ret;
+}
+
+
+static inline int __must_check wlcore_raw_read32(struct cc33xx *wl, int addr,
+						 u32 *val)
+{
+	int ret;
+
+	ret = wlcore_raw_read(wl, addr, wl->buffer_32,
+			      sizeof(*wl->buffer_32), false);
+	if (ret < 0)
+		return ret;
+
+	if (val)
+		*val = le32_to_cpu(*wl->buffer_32);
+
+	return 0;
+}
+
+static inline int __must_check wlcore_raw_write32(struct cc33xx *wl, int addr,
+						  u32 val)
+{
+	*wl->buffer_32 = cpu_to_le32(val);
+	return wlcore_raw_write(wl, addr, wl->buffer_32,
+				sizeof(*wl->buffer_32), false);
+}
+
+static inline int __must_check wlcore_read(struct cc33xx *wl, int addr,
+					   void *buf, size_t len, bool fixed)
+{
+	return wlcore_raw_read(wl, addr, buf, len, fixed);
+}
+
+static inline int __must_check wlcore_write(struct cc33xx *wl, int addr,
+					    void *buf, size_t len, bool fixed)
+{
+	return wlcore_raw_write(wl, addr, buf, len, fixed);
+}
+
+static inline void claim_core_status_lock(struct cc33xx *wl)
+{
+	// When accessing core-status data (read or write) the transport lock
+	// should be held.
+	wl->if_ops->interface_claim(wl->dev);
+}
+
+static inline void release_core_status_lock(struct cc33xx *wl)
+{
+	// After accessing core-status data (read or write) the transport lock
+	// should be released.
+	wl->if_ops->interface_release(wl->dev);
+}
+
+static inline void cc33xx_power_off(struct cc33xx *wl)
+{
+	int ret = 0;
+
+	if (!test_bit(CC33XX_FLAG_GPIO_POWER, &wl->flags))
+		return;
+
+	if (wl->if_ops->power)
+		ret = wl->if_ops->power(wl->dev, false);
+	if (!ret)
+		clear_bit(CC33XX_FLAG_GPIO_POWER, &wl->flags);
+}
+
+static inline int cc33xx_power_on(struct cc33xx *wl)
+{
+	int ret = 0;
+
+	if (wl->if_ops->power)
+		ret = wl->if_ops->power(wl->dev, true);
+	if (ret == 0)
+		set_bit(CC33XX_FLAG_GPIO_POWER, &wl->flags);
+
+	return ret;
+}
+
+bool cc33xx_set_block_size(struct cc33xx *wl);
+
+/* Functions from main.c */
+
+int cc33xx_tx_dummy_packet(struct cc33xx *wl);
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/main.c drivers/net/wireless/ti/cc33xx/main.c
new file mode 100644
index 000000000000..700803672dcd
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/main.c
@@ -0,0 +1,6660 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2011-2013 Texas Instruments Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/gpio.h>
+
+#include "acx.h"
+#include "wlcore.h"
+#include "debug.h"
+#include "cc33xx_80211.h"
+#include "io.h"
+#include "tx.h"
+#include "ps.h"
+#include "init.h"
+#include "debugfs.h"
+#include "testmode.h"
+#include "scan.h"
+#include "sysfs.h"
+#include "../net/mac80211/ieee80211_i.h"
+
+
+#define CC33XX_WAKEUP_TIMEOUT 500
+#define CC33XX_FW_RX_PACKET_RAM (9 * 1024)
+static char *fwlog_param;
+static int fwlog_mem_blocks = -1;
+static int no_recovery     = -1;
+
+static char *ht_mode_param = NULL;
+
+static int num_rx_desc_param = -1;
+
+/* phy paramters */
+
+static int pwr_limit_reference_11_abg_param = -1;
+
+/* HT cap appropriate for wide channels in 2Ghz */
+static struct ieee80211_sta_ht_cap cc33xx_siso40_ht_cap_2ghz = {
+	.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+	       IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
+	       IEEE80211_HT_CAP_GRN_FLD,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(150),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+/* HT cap appropriate for wide channels in 5Ghz */
+static struct ieee80211_sta_ht_cap cc33xx_siso40_ht_cap_5ghz = {
+	.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+	       IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+	       IEEE80211_HT_CAP_GRN_FLD,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(150),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+/* HT cap appropriate for SISO 20 */
+static struct ieee80211_sta_ht_cap cc33xx_siso20_ht_cap = {
+	.cap = IEEE80211_HT_CAP_SGI_20 |     
+	       IEEE80211_HT_CAP_MAX_AMSDU, 
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(72),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+/* HT cap appropriate for MIMO rates in 20mhz channel */
+static struct ieee80211_sta_ht_cap cc33xx_mimo_ht_cap_2ghz = {
+	.cap = IEEE80211_HT_CAP_SGI_20 |
+	       IEEE80211_HT_CAP_GRN_FLD,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(144),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+static const struct ieee80211_iface_limit cc33xx_iface_limits[] = {
+	{
+		.max = 2,
+		.types =  BIT(NL80211_IFTYPE_STATION)
+			| BIT(NL80211_IFTYPE_P2P_CLIENT),
+	},
+	{
+		.max = 1,
+		.types =   BIT(NL80211_IFTYPE_AP)
+			 | BIT(NL80211_IFTYPE_P2P_GO)
+#ifdef CONFIG_MAC80211_MESH
+			 | BIT(NL80211_IFTYPE_MESH_POINT)
+#endif
+	},
+	{
+		.max = 1,
+		.types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+	},
+};
+
+static const struct ieee80211_iface_combination
+cc33xx_iface_combinations[] = {
+	{
+		.max_interfaces = 3,
+		.limits = cc33xx_iface_limits,
+		.n_limits = ARRAY_SIZE(cc33xx_iface_limits),
+		.num_different_channels = 2,
+	}
+};
+
+static const u8 cc33xx_rate_to_idx_2ghz[] = {
+	CONF_HW_RXTX_RATE_UNSUPPORTED,
+	0,              /* RATE_INDEX_1MBPS */
+	1,              /* RATE_INDEX_2MBPS */
+	2,              /* RATE_INDEX_5_5MBPS */
+	3,              /* RATE_INDEX_11MBPS */
+	4,              /* RATE_INDEX_6MBPS */
+	5,              /* RATE_INDEX_9MBPS */
+	6,              /* RATE_INDEX_12MBPS */
+	7,              /* RATE_INDEX_18MBPS */
+	8,              /* RATE_INDEX_24MBPS */
+	9,              /* RATE_INDEX_36MBPS */
+	10,            /* RATE_INDEX_48MBPS */
+	11,            /* RATE_INDEX_54MBPS */
+	0,              /* RATE_INDEX_MCS0 */
+	1,              /* RATE_INDEX_MCS1 */
+	2,              /* RATE_INDEX_MCS2 */
+	3,              /* RATE_INDEX_MCS3 */
+	4,              /* RATE_INDEX_MCS4 */
+	5,              /* RATE_INDEX_MCS5 */
+	6,              /* RATE_INDEX_MCS6 */
+	7               /* RATE_INDEX_MCS7 */
+};
+
+static const u8 cc33xx_rate_to_idx_5ghz[] = {
+	CONF_HW_RXTX_RATE_UNSUPPORTED,
+	CONF_HW_RXTX_RATE_UNSUPPORTED,              /* RATE_INDEX_1MBPS */
+	CONF_HW_RXTX_RATE_UNSUPPORTED,              /* RATE_INDEX_2MBPS */
+	CONF_HW_RXTX_RATE_UNSUPPORTED,              /* RATE_INDEX_5_5MBPS */
+	CONF_HW_RXTX_RATE_UNSUPPORTED,              /* RATE_INDEX_11MBPS */
+	0,              /* RATE_INDEX_6MBPS */
+	1,              /* RATE_INDEX_9MBPS */
+	2,              /* RATE_INDEX_12MBPS */
+	3,              /* RATE_INDEX_18MBPS */
+	4,              /* RATE_INDEX_24MBPS */
+	5,              /* RATE_INDEX_36MBPS */
+	6,              /* RATE_INDEX_48MBPS */
+	7,              /* RATE_INDEX_54MBPS */
+	0,              /* RATE_INDEX_MCS0 */
+	1,              /* RATE_INDEX_MCS1 */
+	2,              /* RATE_INDEX_MCS2 */
+	3,              /* RATE_INDEX_MCS3 */
+	4,              /* RATE_INDEX_MCS4 */
+	5,              /* RATE_INDEX_MCS5 */
+	6,              /* RATE_INDEX_MCS6 */
+	7               /* RATE_INDEX_MCS7 */
+};
+
+static const u8 *cc33xx_band_rate_to_idx[] = {	
+	[NL80211_BAND_2GHZ] = cc33xx_rate_to_idx_2ghz,
+	[NL80211_BAND_5GHZ] = cc33xx_rate_to_idx_5ghz
+};
+
+static const struct cc33xx_clk_cfg cc33xx_clk_table_coex[NUM_CLOCK_CONFIGS] = {
+	[CLOCK_CONFIG_16_2_M]	= { 8,  121, 0, 0, false },
+	[CLOCK_CONFIG_16_368_M]	= { 8,  120, 0, 0, false },
+	[CLOCK_CONFIG_16_8_M]	= { 8,  117, 0, 0, false },
+	[CLOCK_CONFIG_19_2_M]	= { 10, 128, 0, 0, false },
+	[CLOCK_CONFIG_26_M]	= { 11, 104, 0, 0, false },
+	[CLOCK_CONFIG_32_736_M]	= { 8,  120, 0, 0, false },
+	[CLOCK_CONFIG_33_6_M]	= { 8,  117, 0, 0, false },
+	[CLOCK_CONFIG_38_468_M]	= { 10, 128, 0, 0, false },
+	[CLOCK_CONFIG_52_M]	= { 11, 104, 0, 0, false },
+};
+
+static const struct cc33xx_clk_cfg cc33xx_clk_table[NUM_CLOCK_CONFIGS] = {
+	[CLOCK_CONFIG_16_2_M]	= { 7,  104,  801, 4,  true },
+	[CLOCK_CONFIG_16_368_M]	= { 9,  132, 3751, 4,  true },
+	[CLOCK_CONFIG_16_8_M]	= { 7,  100,    0, 0, false },
+	[CLOCK_CONFIG_19_2_M]	= { 8,  100,    0, 0, false },
+	[CLOCK_CONFIG_26_M]	= { 13, 120,    0, 0, false },
+	[CLOCK_CONFIG_32_736_M]	= { 9,  132, 3751, 4,  true },
+	[CLOCK_CONFIG_33_6_M]	= { 7,  100,    0, 0, false },
+	[CLOCK_CONFIG_38_468_M]	= { 8,  100,    0, 0, false },
+	[CLOCK_CONFIG_52_M]	= { 13, 120,    0, 0, false },
+};
+
+/* can't be const, mac80211 writes to this */
+static struct ieee80211_rate cc33xx_rates[] = {
+	{ .bitrate = 10,
+	  .hw_value = CONF_HW_BIT_RATE_1MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
+	{ .bitrate = 20,
+	  .hw_value = CONF_HW_BIT_RATE_2MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
+	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+	  .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
+	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+	  .hw_value = CONF_HW_BIT_RATE_11MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
+	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+	  .hw_value = CONF_HW_BIT_RATE_6MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
+	{ .bitrate = 90,
+	  .hw_value = CONF_HW_BIT_RATE_9MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
+	{ .bitrate = 120,
+	  .hw_value = CONF_HW_BIT_RATE_12MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
+	{ .bitrate = 180,
+	  .hw_value = CONF_HW_BIT_RATE_18MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
+	{ .bitrate = 240,
+	  .hw_value = CONF_HW_BIT_RATE_24MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
+	{ .bitrate = 360,
+	 .hw_value = CONF_HW_BIT_RATE_36MBPS,
+	 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
+	{ .bitrate = 480,
+	  .hw_value = CONF_HW_BIT_RATE_48MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
+	{ .bitrate = 540,
+	  .hw_value = CONF_HW_BIT_RATE_54MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
+};
+
+/* can't be const, mac80211 writes to this */
+static struct ieee80211_channel cc33xx_channels[] = {
+	{ .hw_value = 1, .center_freq = 2412, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 2, .center_freq = 2417, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 3, .center_freq = 2422, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 4, .center_freq = 2427, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 5, .center_freq = 2432, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 6, .center_freq = 2437, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 7, .center_freq = 2442, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 8, .center_freq = 2447, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 9, .center_freq = 2452, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 10, .center_freq = 2457, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 11, .center_freq = 2462, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 12, .center_freq = 2467, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 13, .center_freq = 2472, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 14, .center_freq = 2484, .max_power = CC33XX_MAX_TXPWR },
+};
+
+
+static struct ieee80211_sband_iftype_data iftype_data_2ghz[] = {
+	{
+		.types_mask = BIT(NL80211_IFTYPE_STATION),
+		.he_cap = {
+			.has_he = true,
+			.he_cap_elem = {
+				.mac_cap_info[0] =
+					IEEE80211_HE_MAC_CAP0_HTC_HE,
+				.mac_cap_info[1] =
+					IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
+					IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
+				.mac_cap_info[2] =
+					IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP |
+					IEEE80211_HE_MAC_CAP2_ALL_ACK |
+					IEEE80211_HE_MAC_CAP2_TRS |
+					IEEE80211_HE_MAC_CAP2_BSR |
+					IEEE80211_HE_MAC_CAP2_ACK_EN,
+				.mac_cap_info[3] =
+					IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+					IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS,
+				.mac_cap_info[4] =
+					IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU |
+					IEEE80211_HE_MAC_CAP4_NDP_FB_REP |
+					IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39,
+				.mac_cap_info[5] =
+					IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX,
+				.phy_cap_info[0] = 0,
+				.phy_cap_info[1] =
+					IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+					IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US,
+				.phy_cap_info[2] =
+					IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US,
+				.phy_cap_info[3] =
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM |
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
+				.phy_cap_info[4] =
+					IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+					IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 ,
+				.phy_cap_info[5] =
+					IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+					IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK, 
+				.phy_cap_info[6] =
+					IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU  |
+					IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU  |
+					IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB  |
+					IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+					IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+					IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE,
+				.phy_cap_info[7] =
+					IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI ,
+				.phy_cap_info[8] =
+					IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
+					IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+					IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI,
+				.phy_cap_info[9] =
+					IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
+					IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+					IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
+					IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
+			},
+			/*
+			 * Set default Tx/Rx HE MCS NSS Support field.
+			 * Indicate support for up to 2 spatial streams and all
+			 * MCS, without any special cases
+			 */
+			.he_mcs_nss_supp = {
+				.rx_mcs_80 = cpu_to_le16(0xfffc),
+				.tx_mcs_80 = cpu_to_le16(0xfffc),
+				.rx_mcs_160 = cpu_to_le16(0xffff),
+				.tx_mcs_160 = cpu_to_le16(0xffff),
+				.rx_mcs_80p80 = cpu_to_le16(0xffff),
+				.tx_mcs_80p80 = cpu_to_le16(0xffff),
+			},
+			/*
+			 * Set default PPE thresholds, with PPET16 set to 0,
+			 * PPET8 set to 7
+			 */
+			.ppe_thres = {0xff, 0xff, 0xff, 0xff},
+		},
+	},	
+};
+
+
+
+/* can't be const, mac80211 writes to this */
+static struct ieee80211_supported_band cc33xx_band_2ghz = {
+	.channels = cc33xx_channels,
+	.n_channels = ARRAY_SIZE(cc33xx_channels),
+	.bitrates = cc33xx_rates,
+	.n_bitrates = ARRAY_SIZE(cc33xx_rates),
+	.iftype_data = iftype_data_2ghz,
+	.n_iftype_data = ARRAY_SIZE(iftype_data_2ghz),
+};
+
+/* 5 GHz data rates for cc33xx */
+static struct ieee80211_rate cc33xx_rates_5ghz[] = {
+	{ .bitrate = 60,
+	  .hw_value = CONF_HW_BIT_RATE_6MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
+	{ .bitrate = 90,
+	  .hw_value = CONF_HW_BIT_RATE_9MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
+	{ .bitrate = 120,
+	  .hw_value = CONF_HW_BIT_RATE_12MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
+	{ .bitrate = 180,
+	  .hw_value = CONF_HW_BIT_RATE_18MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
+	{ .bitrate = 240,
+	  .hw_value = CONF_HW_BIT_RATE_24MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
+	{ .bitrate = 360,
+	 .hw_value = CONF_HW_BIT_RATE_36MBPS,
+	 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
+	{ .bitrate = 480,
+	  .hw_value = CONF_HW_BIT_RATE_48MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
+	{ .bitrate = 540,
+	  .hw_value = CONF_HW_BIT_RATE_54MBPS,
+	  .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
+};
+
+/* 5 GHz band channels for cc33xx */
+static struct ieee80211_channel cc33xx_channels_5ghz[] = {
+	{ .hw_value = 8, .center_freq = 5040, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 12, .center_freq = 5060, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 16, .center_freq = 5080, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 34, .center_freq = 5170, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 36, .center_freq = 5180, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 38, .center_freq = 5190, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 40, .center_freq = 5200, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 42, .center_freq = 5210, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 44, .center_freq = 5220, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 46, .center_freq = 5230, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 48, .center_freq = 5240, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 52, .center_freq = 5260, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 56, .center_freq = 5280, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 60, .center_freq = 5300, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 64, .center_freq = 5320, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 100, .center_freq = 5500, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 104, .center_freq = 5520, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 108, .center_freq = 5540, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 112, .center_freq = 5560, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 116, .center_freq = 5580, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 120, .center_freq = 5600, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 124, .center_freq = 5620, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 128, .center_freq = 5640, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 132, .center_freq = 5660, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 136, .center_freq = 5680, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 140, .center_freq = 5700, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 149, .center_freq = 5745, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 153, .center_freq = 5765, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 157, .center_freq = 5785, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 161, .center_freq = 5805, .max_power = CC33XX_MAX_TXPWR },
+	{ .hw_value = 165, .center_freq = 5825, .max_power = CC33XX_MAX_TXPWR },
+};
+static struct ieee80211_sband_iftype_data iftype_data_5ghz[] = {
+	{
+		.types_mask = BIT(NL80211_IFTYPE_STATION),
+		.he_cap = {
+			.has_he = true,
+			.he_cap_elem = {
+				.mac_cap_info[0] =
+					IEEE80211_HE_MAC_CAP0_HTC_HE,
+				.mac_cap_info[1] =
+					IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
+					IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
+				.mac_cap_info[2] =
+					IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP |
+					IEEE80211_HE_MAC_CAP2_ALL_ACK |
+					IEEE80211_HE_MAC_CAP2_TRS |
+					IEEE80211_HE_MAC_CAP2_BSR |
+					IEEE80211_HE_MAC_CAP2_ACK_EN,
+				.mac_cap_info[3] =
+					IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+					IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS,
+				.mac_cap_info[4] =
+					IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU |
+					IEEE80211_HE_MAC_CAP4_NDP_FB_REP |
+					IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39,
+				.mac_cap_info[5] =
+					IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX,
+				.phy_cap_info[0] = 0,
+				.phy_cap_info[1] =
+					IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+					IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US,
+				.phy_cap_info[2] =
+					IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US,
+				.phy_cap_info[3] =
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM |
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+					IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
+				.phy_cap_info[4] =
+					IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+					IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 ,
+				.phy_cap_info[5] =
+					IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+					IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK, 
+				.phy_cap_info[6] =
+					IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU  |
+					IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU  |
+					IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB  |
+					IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+					IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+					IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE,
+				.phy_cap_info[7] =
+					IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI ,
+				.phy_cap_info[8] =
+					IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
+					IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+					IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI,
+				.phy_cap_info[9] =
+					IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
+					IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+					IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
+					IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
+			},
+			/*
+			 * Set default Tx/Rx HE MCS NSS Support field.
+			 * Indicate support for up to 2 spatial streams and all
+			 * MCS, without any special cases
+			 */
+			.he_mcs_nss_supp = {
+				.rx_mcs_80 = cpu_to_le16(0xfffc),
+				.tx_mcs_80 = cpu_to_le16(0xfffc),
+				.rx_mcs_160 = cpu_to_le16(0xffff),
+				.tx_mcs_160 = cpu_to_le16(0xffff),
+				.rx_mcs_80p80 = cpu_to_le16(0xffff),
+				.tx_mcs_80p80 = cpu_to_le16(0xffff),
+			},
+			/*
+			 * Set default PPE thresholds, with PPET16 set to 0,
+			 * PPET8 set to 7
+			 */
+			.ppe_thres = {0xff, 0xff, 0xff, 0xff},
+		},
+	},	
+};
+
+static struct ieee80211_supported_band cc33xx_band_5ghz = {
+	.channels = cc33xx_channels_5ghz,
+	.n_channels = ARRAY_SIZE(cc33xx_channels_5ghz),
+	.bitrates = cc33xx_rates_5ghz,
+	.n_bitrates = ARRAY_SIZE(cc33xx_rates_5ghz),	
+	.vht_cap = {
+		.vht_supported = true,
+		.cap = (IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
+			(1 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT)),
+		.vht_mcs = {
+			.rx_mcs_map = cpu_to_le16(0xfffc),
+			.rx_highest = 7,
+			.tx_mcs_map = cpu_to_le16(0xfffc),
+			.tx_highest = 7,
+		},		
+	},
+	.iftype_data = iftype_data_5ghz,
+	.n_iftype_data = ARRAY_SIZE(iftype_data_5ghz),
+	
+};
+
+
+
+static void __cc33xx_op_remove_interface(struct cc33xx *wl,
+					 struct ieee80211_vif *vif,
+					 bool reset_tx_queues);
+static void cc33xx_turn_off(struct cc33xx *wl);
+static void cc33xx_free_ap_keys(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+static int process_core_status(struct cc33xx *wl, struct core_status *core_status);
+static int cc33xx_setup(struct cc33xx *wl);
+
+static int cc33xx_set_authorized(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+		return -EINVAL;
+
+	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+		return 0;
+
+	if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
+		return 0;
+
+	ret = cc33xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
+	if (ret < 0)
+		return ret;
+
+	cc33xx_info("Association completed.");
+	return 0;
+}
+
+static void cc33xx_reg_notify(struct wiphy *wiphy,
+			      struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct cc33xx *wl = hw->priv;
+
+	/* copy the current dfs region */
+	if (request)
+		wl->dfs_region = request->dfs_region;
+
+	wlcore_regdomain_config(wl);
+}
+
+static int cc33xx_set_rx_streaming(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				   bool enable)
+{
+	int ret = 0;
+
+	/* we should hold wl->mutex */
+	ret = cc33xx_acx_ps_rx_streaming(wl, wlvif, enable);
+	if (ret < 0)
+		goto out;
+
+	if (enable)
+		set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
+	else
+		clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
+out:
+	return ret;
+}
+
+/*
+ * this function is being called when the rx_streaming interval
+ * has beed changed or rx_streaming should be disabled
+ */
+int cc33xx_recalc_rx_streaming(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret = 0;
+	int period = wl->conf.host_conf.rx_streaming.interval;
+
+	/* don't reconfigure if rx_streaming is disabled */
+	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
+		goto out;
+
+	/* reconfigure/disable according to new streaming_period */
+	if (period &&
+	    test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
+	    (wl->conf.host_conf.rx_streaming.always ||
+	     test_bit(CC33XX_FLAG_SOFT_GEMINI, &wl->flags)))
+		ret = cc33xx_set_rx_streaming(wl, wlvif, true);
+	else {
+		ret = cc33xx_set_rx_streaming(wl, wlvif, false);
+		/* don't cancel_work_sync since we might deadlock */
+		del_timer_sync(&wlvif->rx_streaming_timer);
+	}
+out:
+	return ret;
+}
+
+static void cc33xx_rx_streaming_enable_work(struct work_struct *work)
+{
+	int ret;
+	struct cc33xx_vif *wlvif = container_of(work, struct cc33xx_vif,
+						rx_streaming_enable_work);
+	struct cc33xx *wl = wlvif->wl;
+
+	mutex_lock(&wl->mutex);
+
+	if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) ||
+	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
+	    (!wl->conf.host_conf.rx_streaming.always &&
+	     !test_bit(CC33XX_FLAG_SOFT_GEMINI, &wl->flags)))
+		goto out;
+
+	if (!wl->conf.host_conf.rx_streaming.interval)
+		goto out;
+
+	ret = cc33xx_set_rx_streaming(wl, wlvif, true);
+	if (ret < 0)
+		goto out;
+
+	/* stop it after some time of inactivity */
+	mod_timer(&wlvif->rx_streaming_timer,
+		  jiffies + msecs_to_jiffies(wl->conf.host_conf.rx_streaming.duration));
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void cc33xx_rx_streaming_disable_work(struct work_struct *work)
+{
+	int ret;
+	struct cc33xx_vif *wlvif = container_of(work, struct cc33xx_vif,
+						rx_streaming_disable_work);
+	struct cc33xx *wl = wlvif->wl;
+
+	mutex_lock(&wl->mutex);
+
+	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
+		goto out;
+
+	ret = cc33xx_set_rx_streaming(wl, wlvif, false);
+	if (ret)
+		goto out;
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void cc33xx_rx_streaming_timer(struct timer_list *t)
+{
+	struct cc33xx_vif *wlvif = from_timer(wlvif, t, rx_streaming_timer);
+	struct cc33xx *wl = wlvif->wl;
+	ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
+}
+
+/* wl->mutex must be taken */
+void cc33xx_rearm_tx_watchdog_locked(struct cc33xx *wl)
+{
+	/* if the watchdog is not armed, don't do anything */
+	if (wl->tx_allocated_blocks == 0)
+		return;
+
+	cancel_delayed_work(&wl->tx_watchdog_work);
+	ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
+		msecs_to_jiffies(wl->conf.host_conf.tx.tx_watchdog_timeout));
+}
+
+static void cc33xx_sta_rc_update(struct cc33xx *wl,
+				 struct cc33xx_vif *wlvif)
+{
+	bool wide = wlvif->rc_update_bw >= IEEE80211_STA_RX_BW_40;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
+
+	/* sanity */
+	if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+		return;
+
+	/* ignore the change before association */
+	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+		return;
+
+	/*
+	 * If we started out as wide, we can change the operation mode. If we
+	 * thought this was a 20mhz AP, we have to reconnect
+	 */
+	if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
+	    wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
+		cc33xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
+	else
+		ieee80211_connection_loss(cc33xx_wlvif_to_vif(wlvif));
+}
+
+static void wlcore_rc_update_work(struct work_struct *work)
+{
+	int ret;
+	struct cc33xx_vif *wlvif = container_of(work, struct cc33xx_vif,
+						rc_update_work);
+	struct cc33xx *wl = wlvif->wl;
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (ieee80211_vif_is_mesh(vif)) {
+		ret = cc33xx_acx_set_ht_capabilities(wl, &wlvif->rc_ht_cap,
+						     true, wlvif->sta.hlid);
+		if (ret < 0)
+			goto out;
+	} else {
+		cc33xx_sta_rc_update(wl, wlvif);
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void cc33xx_tx_watchdog_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+
+	dwork = to_delayed_work(work);
+	wl = container_of(dwork, struct cc33xx, tx_watchdog_work);
+
+
+	mutex_lock(&wl->mutex);
+
+	// michal temp - ignore watchdog
+	    goto out;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* Tx went out in the meantime - everything is ok */
+	if (unlikely(wl->tx_allocated_blocks == 0))
+		goto out;
+
+	/*
+	 * if a ROC is in progress, we might not have any Tx for a long
+	 * time (e.g. pending Tx on the non-ROC channels)
+	 */
+	if (find_first_bit(wl->roc_map, CC33XX_MAX_ROLES) < CC33XX_MAX_ROLES) {
+		cc33xx_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
+			     wl->conf.host_conf.tx.tx_watchdog_timeout);
+		cc33xx_rearm_tx_watchdog_locked(wl);
+		goto out;
+	}
+
+	/*
+	 * if a scan is in progress, we might not have any Tx for a long
+	 * time
+	 */
+	if (wl->scan.state != CC33XX_SCAN_STATE_IDLE) {
+		cc33xx_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
+			     wl->conf.host_conf.tx.tx_watchdog_timeout);
+		cc33xx_rearm_tx_watchdog_locked(wl);
+		goto out;
+	}
+
+	/*
+	* AP might cache a frame for a long time for a sleeping station,
+	* so rearm the timer if there's an AP interface with stations. If
+	* Tx is genuinely stuck we will most hopefully discover it when all
+	* stations are removed due to inactivity.
+	*/
+	if (wl->active_sta_count) {
+		cc33xx_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
+			     " %d stations",
+			      wl->conf.host_conf.tx.tx_watchdog_timeout,
+			      wl->active_sta_count);
+		cc33xx_rearm_tx_watchdog_locked(wl);
+		goto out;
+	}
+
+	cc33xx_error("Tx stuck (in FW) for %d ms. Starting recovery",
+		     wl->conf.host_conf.tx.tx_watchdog_timeout);
+	cc33xx_queue_recovery_work(wl);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void wlcore_adjust_conf(struct cc33xx *wl)
+{
+
+	if (fwlog_param) {
+		if (!strcmp(fwlog_param, "continuous")) {
+			wl->conf.host_conf.fwlog.mode = CC33XX_FWLOG_CONTINUOUS;
+			wl->conf.host_conf.fwlog.output = CC33XX_FWLOG_OUTPUT_HOST;
+		} else if (!strcmp(fwlog_param, "dbgpins")) {
+			wl->conf.host_conf.fwlog.mode = CC33XX_FWLOG_CONTINUOUS;
+			wl->conf.host_conf.fwlog.output = CC33XX_FWLOG_OUTPUT_DBG_PINS;
+		} else if (!strcmp(fwlog_param, "disable")) {
+			wl->conf.host_conf.fwlog.mem_blocks = 0;
+			wl->conf.host_conf.fwlog.output = CC33XX_FWLOG_OUTPUT_NONE;
+		} else {
+			cc33xx_error("Unknown fwlog parameter %s", fwlog_param);
+		}
+	}
+
+	if (no_recovery != -1)
+		wl->conf.core.no_recovery = (u8) no_recovery;
+}
+
+void cc33xx_flush_deferred_work(struct cc33xx *wl)
+{
+	struct sk_buff *skb;
+
+	/* Pass all received frames to the network stack */
+	while ((skb = skb_dequeue(&wl->deferred_rx_queue)))
+	{
+	    cc33xx_debug(DEBUG_RX, "cc33xx_flush_deferred_work rx skb 0x%p", skb);
+		ieee80211_rx_ni(wl->hw, skb);
+	}
+
+	/* Return sent skbs to the network stack */
+	while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
+		ieee80211_tx_status_ni(wl->hw, skb);
+}
+
+static void cc33xx_netstack_work(struct work_struct *work)
+{
+	struct cc33xx *wl =
+		container_of(work, struct cc33xx, netstack_work);
+
+	do {
+		cc33xx_flush_deferred_work(wl);
+	} while (skb_queue_len(&wl->deferred_rx_queue));
+}
+
+
+
+static int wlcore_irq_locked(struct cc33xx *wl)
+{
+	int ret = 0;
+	struct core_status *core_status_ptr;
+	u8 *rx_buf_ptr;
+	u16 rx_buf_len;
+	size_t read_data_len;
+	const size_t maximum_rx_packet_size = CC33XX_FW_RX_PACKET_RAM;
+	size_t rx_byte_count;
+	struct NAB_rx_header *NAB_rx_header;
+
+	cc33xx_debug(DEBUG_IRQ, "IRQ locked work");
+
+	process_deferred_events(wl);
+
+	cc33xx_debug(DEBUG_IRQ, "IRQ locked work: Taking core-status lock");
+
+	claim_core_status_lock(wl);
+
+	cc33xx_debug(DEBUG_IRQ, "IRQ locked work: Lock taken");
+
+	rx_byte_count = (wl->core_status->rx_status & RX_BYTE_COUNT_MASK);
+	if (rx_byte_count != 0)
+	{
+		const int read_headers_len = sizeof(struct core_status) 
+			+ sizeof(struct NAB_rx_header);
+
+		// Read aggressively as more data might be coming in
+		rx_byte_count *= 2; 
+
+		read_data_len = rx_byte_count + read_headers_len;
+
+		if (wl->max_transaction_len) // Used in SPI interface
+		{
+			const int spi_alignment = sizeof (u32) - 1;
+			read_data_len = __ALIGN_MASK(read_data_len, spi_alignment);
+			read_data_len = min(read_data_len, wl->max_transaction_len);		
+		}
+		else // SDIO
+		{
+			const int sdio_alignment = CC33XX_BUS_BLOCK_SIZE-1;
+			read_data_len = __ALIGN_MASK(read_data_len, sdio_alignment);
+			read_data_len = min(read_data_len, maximum_rx_packet_size);
+		}
+
+		ret = wlcore_raw_read(wl, NAB_DATA_ADDR, 
+					wl->aggr_buf, read_data_len, true);
+		if (ret < 0)
+		{
+			cc33xx_debug(DEBUG_IRQ, "rx read Error response 0x%x", ret);
+			release_core_status_lock(wl);
+			return ret;
+		}
+
+		core_status_ptr = (struct core_status *)
+			((u8 *)wl->aggr_buf + read_data_len - sizeof(struct core_status));
+
+		memcpy(wl->core_status, 
+			core_status_ptr, sizeof(struct core_status));
+
+		cc33xx_debug(DEBUG_IRQ, "IRQ locked work: call process_core_status");
+		process_core_status(wl, wl->core_status);
+
+		cc33xx_debug(DEBUG_IRQ, "IRQ locked work: Releasing core-status lock");
+		release_core_status_lock(wl);
+
+		cc33xx_debug(DEBUG_IRQ, "read rx data 0x%x", ret);
+		NAB_rx_header = (struct NAB_rx_header *)wl->aggr_buf;
+		rx_buf_len = NAB_rx_header->len - 8; // michal fix
+		if (rx_buf_len != 0)
+		{
+			rx_buf_ptr = (u8 *)wl->aggr_buf + sizeof(struct NAB_rx_header);
+			cc33xx_debug(DEBUG_IRQ,"calling rx code!");
+			wlcore_rx(wl, rx_buf_ptr, rx_buf_len);
+			cc33xx_debug(DEBUG_IRQ,"finished rx code!");
+		}
+		else
+		{
+			cc33xx_error("Rx buffer length is 0");
+			cc33xx_queue_recovery_work(wl);
+		}
+	}
+	else
+	{
+		cc33xx_debug(DEBUG_IRQ, 
+			"IRQ locked work: No rx data, releasing core-status lock");
+		release_core_status_lock(wl);
+	}
+
+	cc33xx_tx_immediate_complete(wl);
+
+	return ret;
+}
+
+static int read_core_status(struct cc33xx *wl, struct core_status *core_status)
+{
+	cc33xx_debug(DEBUG_CORE_STATUS, "Reading core status");
+
+	return wlcore_raw_read(wl, NAB_STATUS_ADDR, core_status, 
+				sizeof *core_status, false);
+}
+
+static int parse_control_message(struct cc33xx *wl, 
+				const u8 *buffer, size_t buffer_length)
+{
+	u8 *const end_of_payload = (u8 *const) buffer + buffer_length;
+	u8 *const start_of_payload = (u8 *const) buffer;
+	struct control_info_descriptor *control_info_descriptor;
+	const u8 *event_data, *cmd_result_data;
+	int ctrl_info_type, ctrl_info_length;
+
+	while(buffer < end_of_payload){
+		control_info_descriptor = 
+			(struct control_info_descriptor *) buffer;
+
+		ctrl_info_type = le16_to_cpu(control_info_descriptor->type);
+		ctrl_info_length = le16_to_cpu(control_info_descriptor->length);
+
+		cc33xx_debug(DEBUG_CMD, "Processing message type %d, len %d", 
+			ctrl_info_type, ctrl_info_length);
+
+		switch (ctrl_info_type){
+
+		case CTRL_MSG_EVENT:
+			event_data = buffer + sizeof *control_info_descriptor; 
+			
+			deffer_event(wl, event_data, ctrl_info_length);
+			break;
+
+		case CTRL_MSG_COMMND_COMPLETE:
+			cmd_result_data = 
+				buffer + sizeof *control_info_descriptor;
+
+			if (ctrl_info_length > sizeof wl->command_result){
+											
+				print_hex_dump(KERN_DEBUG, "message dump:",
+					DUMP_PREFIX_OFFSET, 16, 1,
+					cmd_result_data, ctrl_info_length, false);
+
+				WARN(1, "Error device response exceeds result "
+					"buffer size");
+
+				goto message_parse_error;
+			}
+
+			memcpy(wl->command_result, cmd_result_data, ctrl_info_length);
+				
+			wl->result_length = ctrl_info_length;
+
+			complete(&wl->command_complete);
+			break;
+
+		default:
+			print_hex_dump(KERN_DEBUG, "message dump:",
+				DUMP_PREFIX_OFFSET, 16, 1,
+				start_of_payload, buffer_length, false);
+
+			WARN(1, "Error processing device message @ offset %x",
+				(size_t)(buffer-start_of_payload));
+
+			goto message_parse_error;
+		}
+
+		buffer += sizeof *control_info_descriptor;
+		buffer += ctrl_info_length;
+	}
+
+	return 0;
+
+message_parse_error:
+	return -EIO;
+}
+
+static int read_control_message(struct cc33xx *wl, u8 *read_buffer, 
+				size_t buffer_size)
+{
+	int ret;
+	size_t device_message_size;
+	struct NAB_header *nab_header;
+
+	cc33xx_debug(DEBUG_CMD, "Reading control info");
+
+	ret = wlcore_raw_read(wl, NAB_CONTROL_ADDR, read_buffer, 
+				buffer_size, false);
+
+	if (ret < 0){
+		cc33xx_debug(DEBUG_CMD, "control read Error response 0x%x", ret);
+		return ret;
+	}
+
+	nab_header = (struct NAB_header*) read_buffer;
+
+	if (nab_header->sync_pattern != DEVICE_SYNC_PATTERN){
+		cc33xx_error("Wrong device sync pattern: 0x%x", 
+			nab_header->sync_pattern);
+		return -EIO;
+	}
+
+	device_message_size = 	sizeof *nab_header + 
+				NAB_EXTRA_BYTES + 
+				nab_header->len;
+
+	if (device_message_size > buffer_size){
+		cc33xx_error("Invalid NAB length field: %x", nab_header->len);
+		return -EIO;
+	}
+
+	return nab_header->len;
+}
+
+static int process_event_and_cmd_result(struct cc33xx *wl, 
+					struct core_status *core_status)
+{
+	int ret; 
+	u8 *read_buffer, *message;
+	const size_t buffer_size = CC33XX_CMD_MAX_SIZE;
+	size_t message_length;
+	struct core_status *new_core_status;
+	__le32 previous_hint;
+
+	read_buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!read_buffer)
+		return -ENOMEM;	
+
+	ret = read_control_message(wl, read_buffer, buffer_size);
+	if (ret < 0)
+		goto out;
+
+	message_length = ret - NAB_EXTRA_BYTES;
+	message = read_buffer + sizeof (struct NAB_header) + NAB_EXTRA_BYTES;
+	ret = parse_control_message(wl, message, message_length);
+	if (ret < 0)
+		goto out;
+
+	/* Each read transaction always carries an updated core status */
+	previous_hint = core_status->host_interrupt_status;
+	new_core_status = (struct core_status*) 
+		(read_buffer + buffer_size - sizeof (struct core_status));
+	memcpy(core_status, new_core_status, sizeof *core_status);
+	/* Host interrupt filed is clear-on-read and we do not want
+	   to overrun previously unhandled bits. */
+	core_status->host_interrupt_status |= previous_hint;
+
+out:
+	kfree(read_buffer);
+	return ret; 
+}
+
+static int verify_padding(struct core_status *core_status)
+{
+	int i;
+	const u32 valid_padding = 0x55555555;
+
+	for (i=0; i<ARRAY_SIZE(core_status->block_pad); i++){
+		if (core_status->block_pad[i] != valid_padding){
+			cc33xx_error("Error in core status padding:");
+			print_hex_dump(KERN_DEBUG, "",
+				DUMP_PREFIX_OFFSET, 16, 1,
+				core_status, 
+				sizeof *core_status, false);
+			return -1;
+		}
+	}
+	
+	return 0;
+}
+
+static int process_core_status(struct cc33xx *wl,  
+				struct core_status *core_status)
+{
+	bool 	core_status_idle;
+	u32	shadow_host_interrupt_status; 
+	int 	ret;
+
+	do{
+		core_status_idle = true;
+
+		shadow_host_interrupt_status =
+			core_status->host_interrupt_status;
+
+		/* Interrupts are aggregated (ORed) in this filed with each
+		   read operation from the device. */
+		core_status->host_interrupt_status=0;
+
+		cc33xx_debug(DEBUG_IRQ, 
+			"HINT_STATUS: 0x%x, TSF: 0x%x, rx status: 0x%x",
+			shadow_host_interrupt_status,
+			core_status->tsf,
+			core_status->rx_status);
+
+		if (shadow_host_interrupt_status & HINT_COMMAND_COMPLETE){
+			ret = process_event_and_cmd_result(wl, core_status);
+			if (ret < 0){
+				memset(core_status, 0, sizeof *core_status);
+				return ret;
+			}
+			core_status_idle = false;
+		}
+
+		if ((core_status->rx_status & RX_BYTE_COUNT_MASK) != 0){
+			cc33xx_debug(DEBUG_RX, 
+				"Rx data pending, triggering deferred work"); 
+			queue_work(wl->freezable_wq, &wl->irq_deferred_work);
+		}
+
+		if (core_status->fwInfo.txResultQueueIndex 
+						!= wl->last_fw_rls_idx){
+			cc33xx_debug(DEBUG_TX,
+			"Tx new result, triggering deferred work");
+			queue_work(wl->freezable_wq, &wl->irq_deferred_work);
+		}
+		
+		if (shadow_host_interrupt_status &  HINT_NEW_TX_RESULT){
+			cc33xx_debug(DEBUG_TX,
+			"Tx complete, triggering deferred work");
+			queue_work(wl->freezable_wq, &wl->irq_deferred_work);
+		}
+
+		if (shadow_host_interrupt_status & BOOT_TIME_INTERRUPTS){
+			cc33xx_handle_boot_irqs(wl, 
+						shadow_host_interrupt_status);
+		}
+
+		if (shadow_host_interrupt_status & HINT_GENERAL_ERROR){
+			cc33xx_error("FW is stuck, triggering recovery");
+			cc33xx_queue_recovery_work(wl);
+		}
+
+	}while (!core_status_idle);
+
+	return 0;
+}
+
+
+void wlcore_irq(void *cookie)
+{
+	struct cc33xx *wl = cookie;
+	int ret;
+
+	cc33xx_debug(DEBUG_IRQ, "wlcore_irq invoked");
+	claim_core_status_lock(wl);
+	cc33xx_debug(DEBUG_IRQ, "wlcore_irq: Core-status locked");
+
+
+	ret = read_core_status(wl, wl->core_status);
+	if (unlikely(ret < 0)){
+		cc33xx_error("IO error during core status read");
+		cc33xx_queue_recovery_work(wl);
+		goto out;
+	}
+
+	ret = verify_padding(wl->core_status);
+	if (unlikely(ret<0)){
+		cc33xx_queue_recovery_work(wl);
+		goto out;
+	}
+
+	process_core_status(wl, wl->core_status);	
+
+out:
+	cc33xx_debug(DEBUG_IRQ, "wlcore_irq: Releasing core-status");
+	release_core_status_lock(wl);
+}
+
+struct vif_counter_data {
+	u8 counter;
+
+	struct ieee80211_vif *cur_vif;
+	bool cur_vif_running;
+};
+
+static void cc33xx_vif_count_iter(void *data, u8 *mac,
+				  struct ieee80211_vif *vif)
+{
+	struct vif_counter_data *counter = data;
+
+	counter->counter++;
+	if (counter->cur_vif == vif)
+		counter->cur_vif_running = true;
+}
+
+/* caller must not hold wl->mutex, as it might deadlock */
+static void cc33xx_get_vif_count(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *cur_vif,
+			       struct vif_counter_data *data)
+{
+	memset(data, 0, sizeof(*data));
+	data->cur_vif = cur_vif;
+
+	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+					    cc33xx_vif_count_iter, data);
+}
+
+void cc33xx_queue_recovery_work(struct cc33xx *wl)
+{
+	/* Avoid a recursive recovery */
+	if (wl->state == WLCORE_STATE_ON) {
+		
+		wl->state = WLCORE_STATE_RESTARTING;
+		set_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+		ieee80211_queue_work(wl->hw, &wl->recovery_work);
+	}
+}
+
+size_t cc33xx_copy_fwlog(struct cc33xx *wl, u8 *memblock, size_t maxlen)
+{
+	size_t len;
+
+	/* Make sure we have enough room */
+	len = min_t(size_t, maxlen, PAGE_SIZE - wl->fwlog_size);
+
+	/* Fill the FW log file, consumed by the sysfs fwlog entry */
+	memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
+	wl->fwlog_size += len;
+
+	return len;
+}
+
+static void wlcore_save_freed_pkts(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				   u8 hlid, struct ieee80211_sta *sta)
+{
+	struct cc33xx_station *wl_sta;
+	u32 sqn_recovery_padding = CC33XX_TX_SQN_POST_RECOVERY_PADDING;
+
+	wl_sta = (void *)sta->drv_priv;
+	wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
+
+	/*
+	 * increment the initial seq number on recovery to account for
+	 * transmitted packets that we haven't yet got in the FW status
+	 */
+	if (wlvif->encryption_type == KEY_GEM)
+		sqn_recovery_padding = CC33XX_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
+	if (test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+		wl_sta->total_freed_pkts += sqn_recovery_padding;
+}
+
+static void wlcore_save_freed_pkts_addr(struct cc33xx *wl,
+					struct cc33xx_vif *wlvif,
+					u8 hlid, const u8 *addr)
+{
+	struct ieee80211_sta *sta;
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+	if (WARN_ON(hlid == CC33XX_INVALID_LINK_ID ||
+		    is_zero_ether_addr(addr)))
+		return;
+
+	rcu_read_lock();
+	sta = ieee80211_find_sta(vif, addr);
+	if (sta)
+		wlcore_save_freed_pkts(wl, wlvif, hlid, sta);
+	rcu_read_unlock();
+}
+
+static void cc33xx_recovery_work(struct work_struct *work)
+{
+	struct cc33xx *wl = container_of(work, struct cc33xx, recovery_work);
+	struct cc33xx_vif *wlvif;
+	struct ieee80211_vif *vif;
+
+	cc33xx_notice("Recovery work");
+
+	if (wl->conf.core.no_recovery) {
+		cc33xx_info("Recovery disabled by configuration, driver will not restart.");
+		return;
+	}
+
+	if (test_bit(CC33XX_FLAG_DRIVER_REMOVED, &wl->flags)){
+		cc33xx_info("Driver being removed, recovery disabled");
+		return;
+	}
+
+	wl->state = WLCORE_STATE_RESTARTING;
+	set_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+	
+	mutex_lock(&wl->mutex);
+	while (!list_empty(&wl->wlvif_list)) {
+		wlvif = list_first_entry(&wl->wlvif_list,
+				       struct cc33xx_vif, list);
+		vif = cc33xx_wlvif_to_vif(wlvif);
+
+		if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+			ieee80211_connection_loss(vif);
+
+		__cc33xx_op_remove_interface(wl, vif, false);
+	}
+	mutex_unlock(&wl->mutex);
+
+	cc33xx_turn_off(wl);
+	msleep(500);
+
+	mutex_lock(&wl->mutex);
+	cc33xx_init_fw(wl);
+	mutex_unlock(&wl->mutex);
+
+	ieee80211_restart_hw(wl->hw);
+
+	mutex_lock(&wl->mutex);
+	clear_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+	mutex_unlock(&wl->mutex);
+}
+
+static void irq_deferred_work(struct work_struct *work)
+{
+	int ret;
+	unsigned long flags;
+	struct cc33xx *wl =
+		container_of(work, struct cc33xx, irq_deferred_work);
+
+	cc33xx_debug(DEBUG_IRQ,"Starting IRQ deffered work");
+
+	mutex_lock(&wl->mutex);
+
+	cc33xx_debug(DEBUG_IRQ,"Starting IRQ deffered work after mutex");
+
+	ret = wlcore_irq_locked(wl);
+	if (ret)
+		cc33xx_queue_recovery_work(wl);
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	/* In case TX was not handled here, queue TX work */
+	clear_bit(CC33XX_FLAG_TX_PENDING, &wl->flags);
+	if (!test_bit(CC33XX_FLAG_FW_TX_BUSY, &wl->flags) &&
+	    cc33xx_tx_total_queue_count(wl) > 0)
+		ieee80211_queue_work(wl->hw, &wl->tx_work);
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	cc33xx_debug(DEBUG_IRQ,"Finish IRQ deffered work. going to release semaphore");
+
+	mutex_unlock(&wl->mutex);
+}
+
+static void irq_wrapper(struct platform_device *pdev)
+{
+	struct cc33xx *wl = platform_get_drvdata(pdev);
+
+	cc33xx_debug(DEBUG_IRQ, "irq_wrapper entry");
+
+	wlcore_irq(wl);
+}
+
+ int cc33xx_plt_init(struct cc33xx *wl)
+{
+	/* PLT init: Role enable + Role start + plt Init  */
+	int ret=0;
+
+	/* Role enable */
+	u8  returned_role_id = CC33XX_INVALID_ROLE_ID;
+	u8 bcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+	
+	ret = cc33xx_cmd_role_enable(wl, bcast_addr, 
+					ROLE_TRANSCEIVER, &returned_role_id);
+	if(ret < 0)
+	{
+		cc33xx_info("PLT init Role Enable FAILED! , PLT roleID is: %u ", 
+				returned_role_id);
+		goto out;
+	}
+	
+	ret = cc33xx_cmd_role_start_transceiver(wl, returned_role_id);
+	if(ret < 0)
+	{
+		cc33xx_info("PLT init Role Start FAILED! , PLT roleID is: %u ", 
+				returned_role_id);
+		cc33xx_cmd_role_disable(wl, &returned_role_id);
+		goto out;
+	}
+
+	wl->plt_role_id = returned_role_id;
+	ret = cc33xx_cmd_plt_enable(wl, returned_role_id);
+	
+	if(ret >= 0)
+	{
+		cc33xx_info("PLT init Role Start succeed!, PLT roleID is: %u ", 
+				returned_role_id);
+	}
+	else
+	{
+		cc33xx_info("PLT init Role Start FAILED! , PLT roleID is: %u ", 
+				returned_role_id);
+	}
+	
+out:
+	return ret;
+}
+
+int cc33xx_plt_start(struct cc33xx *wl, const enum plt_mode plt_mode)
+{
+	int ret;
+
+	mutex_lock(&wl->mutex);
+
+	if(plt_mode == PLT_ON && wl->plt_mode == PLT_ON)
+	{
+		cc33xx_error("PLT already on");
+		ret = 0;
+		goto out;
+	}
+
+	cc33xx_notice("PLT start");
+
+	if (plt_mode != PLT_CHIP_AWAKE) {
+		ret = cc33xx_plt_init(wl);
+		if (ret < 0){
+			cc33xx_error("PLT start failed");
+			goto out;
+		}
+	}
+
+	/* Indicate to lower levels that we are now in PLT mode */
+	wl->plt = true;
+	wl->plt_mode = plt_mode;
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+int cc33xx_plt_stop(struct cc33xx *wl)
+{
+	int ret = 0;
+
+	cc33xx_notice("PLT stop");
+
+	ret = cc33xx_cmd_role_stop_transceiver(wl);
+	if(ret < 0)
+		goto out;
+
+	ret = cc33xx_cmd_role_disable(wl, &(wl->plt_role_id));
+	if(ret < 0)
+		goto out;
+	else
+		cc33xx_cmd_plt_disable(wl);
+
+	cc33xx_flush_deferred_work(wl);
+	
+	flush_deferred_event_list(wl);
+
+	mutex_lock(&wl->mutex);
+	wl->plt = false;
+	wl->plt_mode = PLT_OFF;
+	wl->rx_counter = 0;
+	mutex_unlock(&wl->mutex);
+
+out:
+	return ret;
+}
+
+static void cc33xx_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct cc33xx *wl = hw->priv;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_vif *vif = info->control.vif;
+	struct cc33xx_vif *wlvif = NULL;
+	unsigned long flags;
+	int q, mapping;
+	u8 hlid;
+
+	if (!vif) {
+		cc33xx_debug(DEBUG_TX, "DROP skb with no vif");
+		ieee80211_free_txskb(hw, skb);
+		return;
+	}
+
+	wlvif = cc33xx_vif_to_data(vif);
+	mapping = skb_get_queue_mapping(skb);
+	q = cc33xx_tx_get_queue(mapping);
+
+	hlid = cc33xx_tx_get_hlid(wl, wlvif, skb, control->sta);
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+
+	/*
+	 * drop the packet if the link is invalid or the queue is stopped
+	 * for any reason but watermark. Watermark is a "soft"-stop so we
+	 * allow these packets through.
+	 */
+
+	if (hlid == CC33XX_INVALID_LINK_ID ||
+	    (!test_bit(hlid, wlvif->links_map)) ||
+	     (wlcore_is_queue_stopped_locked(wl, wlvif, q) &&
+	      !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
+			WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
+		cc33xx_debug(DEBUG_TX, "DROP skb hlid %d q %d ", hlid, q);
+		ieee80211_free_txskb(hw, skb);
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_TX, "queue skb hlid %d q %d len %d %p",
+		     hlid, q, skb->len, skb);
+	skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
+
+	wl->tx_queue_count[q]++;
+	wlvif->tx_queue_count[q]++;
+
+	/*
+	 * The workqueue is slow to process the tx_queue and we need stop
+	 * the queue here, otherwise the queue will get too long.
+	 */
+	if (wlvif->tx_queue_count[q] >= CC33XX_TX_QUEUE_HIGH_WATERMARK &&
+	    !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
+					WLCORE_QUEUE_STOP_REASON_WATERMARK)) {
+		cc33xx_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
+		wlcore_stop_queue_locked(wl, wlvif, q,
+					 WLCORE_QUEUE_STOP_REASON_WATERMARK);
+	}
+
+	/*
+	 * The chip specific setup must run before the first TX packet -
+	 * before that, the tx_work will not be initialized!
+	 */
+	cc33xx_debug(DEBUG_TX, "TX Call queue work");
+	if (!test_bit(CC33XX_FLAG_FW_TX_BUSY, &wl->flags) &&
+	    !test_bit(CC33XX_FLAG_TX_PENDING, &wl->flags))
+	{
+	    cc33xx_debug(DEBUG_TX, "trigger tx thread!");
+		ieee80211_queue_work(wl->hw, &wl->tx_work);
+	}
+	else
+	{
+	    cc33xx_debug(DEBUG_TX, "dont trigger tx thread! wl->flags 0x%lx",wl->flags);
+	}
+
+out:
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+int cc33xx_tx_dummy_packet(struct cc33xx *wl)
+{
+	unsigned long flags;
+	int q;
+
+	/* no need to queue a new dummy packet if one is already pending */
+	if (test_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
+		return 0;
+
+	q = cc33xx_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	set_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
+	wl->tx_queue_count[q]++;
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	/* The FW is low on RX memory blocks, so send the dummy packet asap */
+	if (!test_bit(CC33XX_FLAG_FW_TX_BUSY, &wl->flags))
+		return wlcore_tx_work_locked(wl);
+
+	/*
+	 * If the FW TX is busy, TX work will be scheduled by the threaded
+	 * interrupt handler function
+	 */
+	return 0;
+}
+
+/*
+ * The size of the dummy packet should be at least 1400 bytes. However, in
+ * order to minimize the number of bus transactions, aligning it to 512 bytes
+ * boundaries could be beneficial, performance wise
+ */
+#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
+
+static struct sk_buff *cc33xx_alloc_dummy_packet(struct cc33xx *wl)
+{
+	struct sk_buff *skb;
+	struct ieee80211_hdr_3addr *hdr;
+	unsigned int dummy_packet_size;
+
+	dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
+			    sizeof(struct cc33xx_tx_hw_descr) - sizeof(*hdr);
+
+	skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
+	if (!skb) {
+		cc33xx_warning("Failed to allocate a dummy packet skb");
+		return NULL;
+	}
+
+	skb_reserve(skb, sizeof(struct cc33xx_tx_hw_descr));
+
+	hdr = skb_put_zero(skb, sizeof(*hdr));
+	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+					 IEEE80211_STYPE_NULLFUNC |
+					 IEEE80211_FCTL_TODS);
+
+	skb_put_zero(skb, dummy_packet_size);
+
+	/* Dummy packets require the TID to be management */
+	skb->priority = CC33XX_TID_MGMT;
+
+	/* Initialize all fields that might be used */
+	skb_set_queue_mapping(skb, 0);
+	memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
+
+	return skb;
+}
+
+
+static int
+cc33xx_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p)
+{
+	int num_fields = 0, in_field = 0, fields_size = 0;
+	int i, pattern_len = 0;
+
+	if (!p->mask) {
+		cc33xx_warning("No mask in WoWLAN pattern");
+		return -EINVAL;
+	}
+
+	/*
+	 * The pattern is broken up into segments of bytes at different offsets
+	 * that need to be checked by the FW filter. Each segment is called
+	 * a field in the FW API. We verify that the total number of fields
+	 * required for this pattern won't exceed FW limits (8)
+	 * as well as the total fields buffer won't exceed the FW limit.
+	 * Note that if there's a pattern which crosses Ethernet/IP header
+	 * boundary a new field is required.
+	 */
+	for (i = 0; i < p->pattern_len; i++) {
+		if (test_bit(i, (unsigned long *)p->mask)) {
+			if (!in_field) {
+				in_field = 1;
+				pattern_len = 1;
+			} else {
+				if (i == CC33XX_RX_FILTER_ETH_HEADER_SIZE) {
+					num_fields++;
+					fields_size += pattern_len +
+						RX_FILTER_FIELD_OVERHEAD;
+					pattern_len = 1;
+				} else
+					pattern_len++;
+			}
+		} else {
+			if (in_field) {
+				in_field = 0;
+				fields_size += pattern_len +
+					RX_FILTER_FIELD_OVERHEAD;
+				num_fields++;
+			}
+		}
+	}
+
+	if (in_field) {
+		fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD;
+		num_fields++;
+	}
+
+	if (num_fields > CC33XX_RX_FILTER_MAX_FIELDS) {
+		cc33xx_warning("RX Filter too complex. Too many segments");
+		return -EINVAL;
+	}
+
+	if (fields_size > CC33XX_RX_FILTER_MAX_FIELDS_SIZE) {
+		cc33xx_warning("RX filter pattern is too big");
+		return -E2BIG;
+	}
+
+	return 0;
+}
+
+struct cc33xx_rx_filter *cc33xx_rx_filter_alloc(void)
+{
+	return kzalloc(sizeof(struct cc33xx_rx_filter), GFP_KERNEL);
+}
+
+void cc33xx_rx_filter_free(struct cc33xx_rx_filter *filter)
+{
+	int i;
+
+	if (filter == NULL)
+		return;
+
+	for (i = 0; i < filter->num_fields; i++)
+		kfree(filter->fields[i].pattern);
+
+	kfree(filter);
+}
+
+int cc33xx_rx_filter_alloc_field(struct cc33xx_rx_filter *filter,
+				 u16 offset, u8 flags,
+				 const u8 *pattern, u8 len)
+{
+	struct cc33xx_rx_filter_field *field;
+
+	if (filter->num_fields == CC33XX_RX_FILTER_MAX_FIELDS) {
+		cc33xx_warning("Max fields per RX filter. can't alloc another");
+		return -EINVAL;
+	}
+
+	field = &filter->fields[filter->num_fields];
+
+	field->pattern = kzalloc(len, GFP_KERNEL);
+	if (!field->pattern) {
+		cc33xx_warning("Failed to allocate RX filter pattern");
+		return -ENOMEM;
+	}
+
+	filter->num_fields++;
+
+	field->offset = cpu_to_le16(offset);
+	field->flags = flags;
+	field->len = len;
+	memcpy(field->pattern, pattern, len);
+
+	return 0;
+}
+
+int cc33xx_rx_filter_get_fields_size(struct cc33xx_rx_filter *filter)
+{
+	int i, fields_size = 0;
+
+	for (i = 0; i < filter->num_fields; i++)
+		fields_size += filter->fields[i].len +
+			sizeof(struct cc33xx_rx_filter_field) -
+			sizeof(u8 *);
+
+	return fields_size;
+}
+
+void cc33xx_rx_filter_flatten_fields(struct cc33xx_rx_filter *filter,
+				    u8 *buf)
+{
+	int i;
+	struct cc33xx_rx_filter_field *field;
+
+	for (i = 0; i < filter->num_fields; i++) {
+		field = (struct cc33xx_rx_filter_field *)buf;
+
+		field->offset = filter->fields[i].offset;
+		field->flags = filter->fields[i].flags;
+		field->len = filter->fields[i].len;
+
+		memcpy(&field->pattern, filter->fields[i].pattern, field->len);
+		buf += sizeof(struct cc33xx_rx_filter_field) -
+			sizeof(u8 *) + field->len;
+	}
+}
+
+/*
+ * Allocates an RX filter returned through f
+ * which needs to be freed using rx_filter_free()
+ */
+static int
+cc33xx_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p,
+					   struct cc33xx_rx_filter **f)
+{
+	int i, j, ret = 0;
+	struct cc33xx_rx_filter *filter;
+	u16 offset;
+	u8 flags, len;
+
+	filter = cc33xx_rx_filter_alloc();
+	if (!filter) {
+		cc33xx_warning("Failed to alloc rx filter");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	i = 0;
+	while (i < p->pattern_len) {
+		if (!test_bit(i, (unsigned long *)p->mask)) {
+			i++;
+			continue;
+		}
+
+		for (j = i; j < p->pattern_len; j++) {
+			if (!test_bit(j, (unsigned long *)p->mask))
+				break;
+
+			if (i < CC33XX_RX_FILTER_ETH_HEADER_SIZE &&
+			    j >= CC33XX_RX_FILTER_ETH_HEADER_SIZE)
+				break;
+		}
+
+		if (i < CC33XX_RX_FILTER_ETH_HEADER_SIZE) {
+			offset = i;
+			flags = CC33XX_RX_FILTER_FLAG_ETHERNET_HEADER;
+		} else {
+			offset = i - CC33XX_RX_FILTER_ETH_HEADER_SIZE;
+			flags = CC33XX_RX_FILTER_FLAG_IP_HEADER;
+		}
+
+		len = j - i;
+
+		ret = cc33xx_rx_filter_alloc_field(filter,
+						   offset,
+						   flags,
+						   &p->pattern[i], len);
+		if (ret)
+			goto err;
+
+		i = j;
+	}
+
+	filter->action = FILTER_SIGNAL;
+
+	*f = filter;
+	return 0;
+
+err:
+	cc33xx_rx_filter_free(filter);
+	*f = NULL;
+
+	return ret;
+}
+
+static int cc33xx_configure_wowlan(struct cc33xx *wl,
+				   struct cfg80211_wowlan *wow)
+{
+	int i, ret;
+
+	if (!wow || wow->any || !wow->n_patterns) {
+		ret = cc33xx_acx_default_rx_filter_enable(wl, 0,
+							  FILTER_SIGNAL);
+		if (ret)
+			goto out;
+
+		ret = cc33xx_rx_filter_clear_all(wl);
+		if (ret)
+			goto out;
+
+		return 0;
+	}
+
+	if (WARN_ON(wow->n_patterns > CC33XX_MAX_RX_FILTERS))
+		return -EINVAL;
+
+	/* Validate all incoming patterns before clearing current FW state */
+	for (i = 0; i < wow->n_patterns; i++) {
+		ret = cc33xx_validate_wowlan_pattern(&wow->patterns[i]);
+		if (ret) {
+			cc33xx_warning("Bad wowlan pattern %d", i);
+			return ret;
+		}
+	}
+
+	ret = cc33xx_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
+	if (ret)
+		goto out;
+
+	ret = cc33xx_rx_filter_clear_all(wl);
+	if (ret)
+		goto out;
+
+	/* Translate WoWLAN patterns into filters */
+	for (i = 0; i < wow->n_patterns; i++) {
+		struct cfg80211_pkt_pattern *p;
+		struct cc33xx_rx_filter *filter = NULL;
+
+		p = &wow->patterns[i];
+
+		ret = cc33xx_convert_wowlan_pattern_to_rx_filter(p, &filter);
+		if (ret) {
+			cc33xx_warning("Failed to create an RX filter from "
+				       "wowlan pattern %d", i);
+			goto out;
+		}
+
+		ret = cc33xx_rx_filter_enable(wl, i, 1, filter);
+
+		cc33xx_rx_filter_free(filter);
+		if (ret)
+			goto out;
+	}
+
+	ret = cc33xx_acx_default_rx_filter_enable(wl, 1, FILTER_DROP);
+
+out:
+	return ret;
+}
+
+static int cc33xx_configure_suspend_sta(struct cc33xx *wl,
+					struct cc33xx_vif *wlvif,
+					struct cfg80211_wowlan *wow)
+{
+	int ret = 0;
+
+	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+		goto out;
+
+	ret = cc33xx_configure_wowlan(wl, wow);
+	if (ret < 0)
+		goto out;
+
+	if ((wl->conf.core.suspend_wake_up_event ==
+	     wl->conf.core.wake_up_event) &&
+	    (wl->conf.core.suspend_listen_interval ==
+	     wl->conf.core.listen_interval))
+		goto out;
+
+	ret = cc33xx_acx_wake_up_conditions(wl, wlvif,
+				    wl->conf.core.suspend_wake_up_event,
+				    wl->conf.core.suspend_listen_interval);
+
+	if (ret < 0)
+		cc33xx_error("suspend: set wake up conditions failed: %d", ret);
+out:
+	return ret;
+
+}
+
+static int cc33xx_configure_suspend_ap(struct cc33xx *wl,
+					struct cc33xx_vif *wlvif,
+					struct cfg80211_wowlan *wow)
+{
+	int ret = 0;
+
+	if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
+		goto out;
+
+	ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, true);
+	if (ret < 0)
+		goto out;
+
+	ret = cc33xx_configure_wowlan(wl, wow);
+	if (ret < 0)
+		goto out;
+
+out:
+	return ret;
+
+}
+
+static int cc33xx_configure_suspend(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif,
+				    struct cfg80211_wowlan *wow)
+{
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+		return cc33xx_configure_suspend_sta(wl, wlvif, wow);
+	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+		return cc33xx_configure_suspend_ap(wl, wlvif, wow);
+	return 0;
+}
+
+static void cc33xx_configure_resume(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret = 0;
+	bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
+	bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+
+	if ((!is_ap) && (!is_sta))
+		return;
+
+	if ((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ||
+	    (is_ap && !test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)))
+		return;
+
+	cc33xx_configure_wowlan(wl, NULL);
+
+	if (is_sta) {
+		if ((wl->conf.core.suspend_wake_up_event ==
+		     wl->conf.core.wake_up_event) &&
+		    (wl->conf.core.suspend_listen_interval ==
+		     wl->conf.core.listen_interval))
+			return;
+
+		ret = cc33xx_acx_wake_up_conditions(wl, wlvif,
+				    wl->conf.core.wake_up_event,
+				    wl->conf.core.listen_interval);
+
+		if (ret < 0)
+			cc33xx_error("resume: wake up conditions failed: %d",
+				     ret);
+
+	} else if (is_ap) {
+		ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, false);
+	}
+}
+
+static int __maybe_unused cc33xx_op_suspend(struct ieee80211_hw *hw,
+					    struct cfg80211_wowlan *wow)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif;
+	unsigned long flags;
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
+	WARN_ON(!wow);
+
+	/* we want to perform the recovery before suspending */
+	if (test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
+		cc33xx_warning("postponing suspend to perform recovery");
+		return -EBUSY;
+	}
+
+	cc33xx_tx_flush(wl);
+
+	mutex_lock(&wl->mutex);
+
+	wl->keep_device_power = true;
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		if (wlcore_is_p2p_mgmt(wlvif))
+			continue;
+
+		ret = cc33xx_configure_suspend(wl, wlvif, wow);
+		if (ret < 0) {
+			mutex_unlock(&wl->mutex);
+			cc33xx_warning("couldn't prepare device to suspend");
+			return ret;
+		}
+	}
+
+	/* disable fast link flow control notifications from FW */
+	ret = cc33xx_acx_interrupt_notify_config(wl, false);
+	if (ret < 0)
+		goto out;
+
+	/* if filtering is enabled, configure the FW to drop all RX BA frames */
+	ret = cc33xx_acx_rx_ba_filter(wl,
+				     !!wl->conf.host_conf.conn.suspend_rx_ba_activity);
+	if (ret < 0)
+		goto out;
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	if (ret < 0) {
+		cc33xx_warning("couldn't prepare device to suspend");
+		return ret;
+	}
+
+	/* flush any remaining work */
+	cc33xx_debug(DEBUG_MAC80211, "flushing remaining works");
+
+	flush_work(&wl->tx_work);
+
+	/*
+	 * Cancel the watchdog even if above tx_flush failed. We will detect
+	 * it on resume anyway.
+	 */
+	cancel_delayed_work(&wl->tx_watchdog_work);
+
+	/*
+	 * set suspended flag to avoid triggering a new threaded_irq
+	 * work.
+	 */
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	set_bit(CC33XX_FLAG_SUSPENDED, &wl->flags);
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	return 0;
+}
+
+static int __maybe_unused cc33xx_op_resume(struct ieee80211_hw *hw)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif;
+	unsigned long flags;
+	bool run_irq_work = false, pending_recovery;
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
+		     wl->keep_device_power);
+	WARN_ON(!wl->keep_device_power);
+
+	/*
+	 * re-enable irq_work enqueuing, and call irq_work directly if
+	 * there is a pending work.
+	 */
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	clear_bit(CC33XX_FLAG_SUSPENDED, &wl->flags);
+	if (test_and_clear_bit(CC33XX_FLAG_PENDING_WORK, &wl->flags))
+		run_irq_work = true;
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	mutex_lock(&wl->mutex);
+
+	/* test the recovery flag before calling any SDIO functions */
+	pending_recovery = test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS,
+				    &wl->flags);
+
+	if (run_irq_work) {
+		cc33xx_debug(DEBUG_MAC80211,
+			     "run postponed irq_work directly");
+
+		/* don't talk to the HW if recovery is pending */
+		if (!pending_recovery) {
+			ret = wlcore_irq_locked(wl);
+			if (ret)
+				cc33xx_queue_recovery_work(wl);
+		}
+
+		wlcore_enable_interrupts(wl);
+	}
+
+	if (pending_recovery) {
+		cc33xx_warning("queuing forgotten recovery on resume");
+		ieee80211_queue_work(wl->hw, &wl->recovery_work);
+		goto out;
+	}
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		if (wlcore_is_p2p_mgmt(wlvif))
+			continue;
+
+		cc33xx_configure_resume(wl, wlvif);
+	}
+
+	ret = cc33xx_acx_interrupt_notify_config(wl, true);
+	if (ret < 0)
+		goto out;
+
+	/* if filtering is enabled, configure the FW to drop all RX BA frames */
+	ret = cc33xx_acx_rx_ba_filter(wl, false);
+	if (ret < 0)
+		goto out;
+
+out:
+	wl->keep_device_power = false;
+
+	/*
+	 * Set a flag to re-init the watchdog on the first Tx after resume.
+	 * That way we avoid possible conditions where Tx-complete interrupts
+	 * fail to arrive and we perform a spurious recovery.
+	 */
+	set_bit(CC33XX_FLAG_REINIT_TX_WDOG, &wl->flags);
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static int cc33xx_op_start(struct ieee80211_hw *hw)
+{
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 start");
+
+	/*
+	 * We have to delay the booting of the hardware because
+	 * we need to know the local MAC address before downloading and
+	 * initializing the firmware. The MAC address cannot be changed
+	 * after boot, and without the proper MAC address, the firmware
+	 * will not function properly.
+	 *
+	 * The MAC address is first known when the corresponding interface
+	 * is added. That is where we will initialize the hardware.
+	 */
+
+	return 0;
+}
+
+static void cc33xx_turn_off(struct cc33xx *wl)
+{
+	int i;
+
+	if (wl->state == WLCORE_STATE_OFF) {
+		if (test_and_clear_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS,
+					&wl->flags))
+			wlcore_enable_interrupts(wl);
+
+		return;
+	}
+
+	cc33xx_debug(DEBUG_BOOT, "Turning off");
+
+	mutex_lock(&wl->mutex);
+
+	/*
+	 * this must be before the cancel_work calls below, so that the work
+	 * functions don't perform further work.
+	 */
+	wl->state = WLCORE_STATE_OFF;
+
+	/*
+	 * Use the nosync variant to disable interrupts, so the mutex could be
+	 * held while doing so without deadlocking.
+	 */
+	wlcore_disable_interrupts_nosync(wl);
+
+	mutex_unlock(&wl->mutex);
+
+	if (!test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+		cancel_work_sync(&wl->recovery_work);
+	cc33xx_flush_deferred_work(wl);
+	cancel_delayed_work_sync(&wl->scan_complete_work);
+	cancel_work_sync(&wl->netstack_work);
+	cancel_work_sync(&wl->tx_work);
+	cancel_work_sync(&wl->irq_deferred_work);
+	cancel_delayed_work_sync(&wl->tx_watchdog_work);
+
+	/* let's notify MAC80211 about the remaining pending TX frames */
+	mutex_lock(&wl->mutex);
+	cc33xx_tx_reset(wl);
+
+	cc33xx_power_off(wl);
+
+	wl->band = NL80211_BAND_2GHZ;
+
+	wl->rx_counter = 0;
+	wl->power_level = CC33XX_MAX_TXPWR;
+	wl->tx_blocks_available = 0;
+	wl->tx_allocated_blocks = 0;
+	
+	wl->ap_fw_ps_map = 0;
+	wl->ap_ps_map = 0;
+	wl->sleep_auth = CC33XX_PSM_ILLEGAL;
+	memset(wl->roles_map, 0, sizeof(wl->roles_map));
+	memset(wl->links_map, 0, sizeof(wl->links_map));
+	memset(wl->roc_map, 0, sizeof(wl->roc_map));
+	memset(wl->session_ids, 0, sizeof(wl->session_ids));
+	memset(wl->rx_filter_enabled, 0, sizeof(wl->rx_filter_enabled));
+	wl->active_sta_count = 0;
+	wl->active_link_count = 0;
+	wl->ble_enable = 0;
+
+	/* The system link is always allocated */
+	wl->links[CC33XX_SYSTEM_HLID].allocated_pkts = 0;
+	wl->links[CC33XX_SYSTEM_HLID].prev_freed_pkts = 0;
+	__set_bit(CC33XX_SYSTEM_HLID, wl->links_map);
+
+	/*
+	 * this is performed after the cancel_work calls and the associated
+	 * mutex_lock, so that cc33xx_op_add_interface does not accidentally
+	 * get executed before all these vars have been reset.
+	 */
+	wl->flags = 0;
+
+ 
+
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		
+		wl->tx_allocated_pkts[i] = 0;
+	}
+
+	cc33xx_debugfs_reset(wl);
+
+	kfree(wl->target_mem_map);
+	wl->target_mem_map = NULL;
+
+	/*
+	 * FW channels must be re-calibrated after recovery,
+	 * save current Reg-Domain channel configuration and clear it.
+	 */
+	memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last,
+	       sizeof(wl->reg_ch_conf_pending));
+	memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
+
+	mutex_unlock(&wl->mutex);
+}
+
+static void cc33xx_op_stop(struct ieee80211_hw *hw)
+{
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 stop");
+	return;
+}
+
+static void wlcore_channel_switch_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+	struct ieee80211_vif *vif;
+	struct cc33xx_vif *wlvif;
+
+	dwork = to_delayed_work(work);
+	wlvif = container_of(dwork, struct cc33xx_vif, channel_switch_work);
+	wl = wlvif->wl;
+
+	cc33xx_info("channel switch failed (role_id: %d).", wlvif->role_id);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* check the channel switch is still ongoing */
+	if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
+		goto out;
+
+	vif = cc33xx_wlvif_to_vif(wlvif);
+	ieee80211_chswitch_done(vif, false);
+
+	cc33xx_cmd_stop_channel_switch(wl, wlvif);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void wlcore_connection_loss_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+	struct ieee80211_vif *vif;
+	struct cc33xx_vif *wlvif;
+
+	dwork = to_delayed_work(work);
+	wlvif = container_of(dwork, struct cc33xx_vif, connection_loss_work);
+	wl = wlvif->wl;
+
+	cc33xx_info("Connection loss work (role_id: %d).", wlvif->role_id);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* Call mac80211 connection loss */
+	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+		goto out;
+
+	vif = cc33xx_wlvif_to_vif(wlvif);
+	ieee80211_connection_loss(vif);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void wlcore_pending_auth_complete_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+	struct cc33xx_vif *wlvif;
+	unsigned long time_spare;
+
+	dwork = to_delayed_work(work);
+	wlvif = container_of(dwork, struct cc33xx_vif,
+			     pending_auth_complete_work);
+	wl = wlvif->wl;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/*
+	 * Make sure a second really passed since the last auth reply. Maybe
+	 * a second auth reply arrived while we were stuck on the mutex.
+	 * Check for a little less than the timeout to protect from scheduler
+	 * irregularities.
+	 */
+	time_spare = jiffies +
+			msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50);
+	if (!time_after(time_spare, wlvif->pending_auth_reply_time))
+		goto out;
+
+	/* cancel the ROC if active */
+	cc33xx_debug(DEBUG_CMD, "pending_auth t/o expired - cancel ROC if active");
+
+	wlcore_update_inconn_sta(wl, wlvif, NULL, false);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void cc33xx_roc_timeout_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+	struct cc33xx_vif *wlvif;
+	unsigned long time_spare;
+
+	dwork = to_delayed_work(work);
+	wlvif = container_of(dwork, struct cc33xx_vif,
+			     roc_timeout_work);
+	wl = wlvif->wl;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/*
+	 * Make sure that requested timeout really passed. Maybe
+	 * a association completed and croc arrived while we were stuck on the mutex.
+	 * Check for a little less than the timeout to protect from scheduler
+	 * irregularities.
+	 */
+	time_spare = jiffies +
+			msecs_to_jiffies(CC33xx_PEND_ROC_COMPLETE_TIMEOUT - 50);
+	if (!time_after(time_spare, wlvif->pending_auth_reply_time))
+		goto out;
+
+	/* cancel the ROC if active */
+	cc33xx_debug(DEBUG_CMD, "Waiting for CROC Timeout has expired -> cancel ROC if exist");
+
+	if (test_bit(wlvif->role_id, wl->roc_map))
+		cc33xx_croc(wl, wlvif->role_id);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static int cc33xx_allocate_rate_policy(struct cc33xx *wl, u8 *idx)
+{
+	u8 policy = find_first_zero_bit(wl->rate_policies_map,
+					CC33XX_MAX_RATE_POLICIES);
+	if (policy >= CC33XX_MAX_RATE_POLICIES)
+		return -EBUSY;
+
+	__set_bit(policy, wl->rate_policies_map);
+	*idx = policy;
+	return 0;
+}
+
+static void cc33xx_free_rate_policy(struct cc33xx *wl, u8 *idx)
+{
+	if (WARN_ON(*idx >= CC33XX_MAX_RATE_POLICIES))
+		return;
+
+	__clear_bit(*idx, wl->rate_policies_map);
+	*idx = CC33XX_MAX_RATE_POLICIES;
+}
+
+static u8 cc33xx_get_role_type(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+	switch (wlvif->bss_type) {
+	case BSS_TYPE_AP_BSS:
+		if (wlvif->p2p)
+			return CC33XX_ROLE_P2P_GO;
+		else if (ieee80211_vif_is_mesh(vif))
+			return CC33XX_ROLE_MESH_POINT;
+		else
+			return CC33XX_ROLE_AP;
+
+	case BSS_TYPE_STA_BSS:
+		if (wlvif->p2p)
+			return CC33XX_ROLE_P2P_CL;
+		else
+			return CC33XX_ROLE_STA;
+
+	case BSS_TYPE_IBSS:
+		return CC33XX_ROLE_IBSS;
+
+	default:
+		cc33xx_error("invalid bss_type: %d", wlvif->bss_type);
+	}
+	return CC33XX_INVALID_ROLE_TYPE;
+}
+
+static int cc33xx_init_vif_data(struct cc33xx *wl, struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int i;
+
+	/* clear everything but the persistent data */
+	memset(wlvif, 0, offsetof(struct cc33xx_vif, persistent));
+
+	switch (ieee80211_vif_type_p2p(vif)) {
+	case NL80211_IFTYPE_P2P_CLIENT:
+		wlvif->p2p = 1;
+		/* fall-through */
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_P2P_DEVICE:
+		wlvif->bss_type = BSS_TYPE_STA_BSS;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		wlvif->bss_type = BSS_TYPE_IBSS;
+		break;
+	case NL80211_IFTYPE_P2P_GO:
+		wlvif->p2p = 1;
+		/* fall-through */
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_MESH_POINT:
+		wlvif->bss_type = BSS_TYPE_AP_BSS;
+		break;
+	default:
+		wlvif->bss_type = MAX_BSS_TYPE;
+		return -EOPNOTSUPP;
+	}
+
+	wlvif->role_id = CC33XX_INVALID_ROLE_ID;
+	wlvif->dev_role_id = CC33XX_INVALID_ROLE_ID;
+	wlvif->dev_hlid = CC33XX_INVALID_LINK_ID;
+
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+	    wlvif->bss_type == BSS_TYPE_IBSS) {
+		/* init sta/ibss data */
+		wlvif->sta.hlid = CC33XX_INVALID_LINK_ID;
+		cc33xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
+		cc33xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
+		cc33xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
+		wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
+		wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
+		wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
+	} else {
+		/* init ap data */
+		wlvif->ap.bcast_hlid = CC33XX_INVALID_LINK_ID;
+		wlvif->ap.global_hlid = CC33XX_INVALID_LINK_ID;
+		cc33xx_allocate_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
+		cc33xx_allocate_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
+		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
+			cc33xx_allocate_rate_policy(wl,
+						&wlvif->ap.ucast_rate_idx[i]);
+		wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
+		/*
+		 * TODO: check if basic_rate shouldn't be
+		 * cc33xx_tx_min_rate_get(wl, wlvif->basic_rate_set);
+		 * instead (the same thing for STA above).
+		*/
+		wlvif->basic_rate = CONF_TX_ENABLED_RATES;
+		/* TODO: this seems to be used only for STA, check it */
+		wlvif->rate_set = CONF_TX_ENABLED_RATES;
+	}
+
+	wlvif->bitrate_masks[NL80211_BAND_2GHZ] = wl->conf.host_conf.tx.basic_rate;
+	wlvif->bitrate_masks[NL80211_BAND_5GHZ] = wl->conf.host_conf.tx.basic_rate_5;
+	wlvif->beacon_int = CC33XX_DEFAULT_BEACON_INT;
+
+	/*
+	 * mac80211 configures some values globally, while we treat them
+	 * per-interface. thus, on init, we have to copy them from wl
+	 */
+	wlvif->band = wl->band;
+	wlvif->power_level = wl->power_level;
+
+	INIT_WORK(&wlvif->rx_streaming_enable_work,
+		  cc33xx_rx_streaming_enable_work);
+	INIT_WORK(&wlvif->rx_streaming_disable_work,
+		  cc33xx_rx_streaming_disable_work);
+	INIT_WORK(&wlvif->rc_update_work, wlcore_rc_update_work);
+	INIT_DELAYED_WORK(&wlvif->channel_switch_work,
+			  wlcore_channel_switch_work);
+	INIT_DELAYED_WORK(&wlvif->connection_loss_work,
+			  wlcore_connection_loss_work);
+	INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work,
+			  wlcore_pending_auth_complete_work);
+	INIT_DELAYED_WORK(&wlvif->roc_timeout_work,
+			  cc33xx_roc_timeout_work);
+	INIT_LIST_HEAD(&wlvif->list);
+
+	timer_setup(&wlvif->rx_streaming_timer, cc33xx_rx_streaming_timer, 0);
+	return 0;
+}
+
+static bool cc33xx_dev_role_started(struct cc33xx_vif *wlvif)
+{
+	return wlvif->dev_hlid != CC33XX_INVALID_LINK_ID;
+}
+
+struct wlcore_hw_queue_iter_data {
+	unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)];
+	/* current vif */
+	struct ieee80211_vif *vif;
+	/* is the current vif among those iterated */
+	bool cur_running;
+};
+
+static void wlcore_hw_queue_iter(void *data, u8 *mac,
+				 struct ieee80211_vif *vif)
+{
+	struct wlcore_hw_queue_iter_data *iter_data = data;
+
+	if (vif->type == NL80211_IFTYPE_P2P_DEVICE ||
+	    WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE))
+		return;
+
+	if (iter_data->cur_running || vif == iter_data->vif) {
+		iter_data->cur_running = true;
+		return;
+	}
+
+	__set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map);
+}
+
+static int wlcore_allocate_hw_queue_base(struct cc33xx *wl,
+					 struct cc33xx_vif *wlvif)
+{
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	struct wlcore_hw_queue_iter_data iter_data = {};
+	int i, q_base;
+
+	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+		vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+		return 0;
+	}
+
+	iter_data.vif = vif;
+
+	/* mark all bits taken by active interfaces */
+	ieee80211_iterate_active_interfaces_atomic(wl->hw,
+					IEEE80211_IFACE_ITER_RESUME_ALL,
+					wlcore_hw_queue_iter, &iter_data);
+
+	/* the current vif is already running in mac80211 (resume/recovery) */
+	if (iter_data.cur_running) {
+		wlvif->hw_queue_base = vif->hw_queue[0];
+		cc33xx_debug(DEBUG_MAC80211,
+			     "using pre-allocated hw queue base %d",
+			     wlvif->hw_queue_base);
+
+		/* interface type might have changed type */
+		goto adjust_cab_queue;
+	}
+
+	q_base = find_first_zero_bit(iter_data.hw_queue_map,
+				     WLCORE_NUM_MAC_ADDRESSES);
+	if (q_base >= WLCORE_NUM_MAC_ADDRESSES)
+		return -EBUSY;
+
+	wlvif->hw_queue_base = q_base * NUM_TX_QUEUES;
+	cc33xx_debug(DEBUG_MAC80211, "allocating hw queue base: %d",
+		     wlvif->hw_queue_base);
+
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0;
+		/* register hw queues in mac80211 */
+		vif->hw_queue[i] = wlvif->hw_queue_base + i;
+	}
+
+adjust_cab_queue:
+	/* the last places are reserved for cab queues per interface */
+	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+		vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES +
+				 wlvif->hw_queue_base / NUM_TX_QUEUES;
+	else
+		vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+	return 0;
+}
+
+static int cc33xx_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct vif_counter_data vif_count;
+	int ret = 0;
+	u8 role_type;
+
+	if (wl->plt) {
+		cc33xx_error("Adding Interface not allowed while in PLT mode");
+		return -EBUSY;
+	}
+
+	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+			     IEEE80211_VIF_SUPPORTS_UAPSD |
+			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
+		     ieee80211_vif_type_p2p(vif), vif->addr);
+
+	cc33xx_get_vif_count(hw, vif, &vif_count);
+
+	mutex_lock(&wl->mutex);
+
+	/*
+	 * in some very corner case HW recovery scenarios its possible to
+	 * get here before __cc33xx_op_remove_interface is complete, so
+	 * opt out if that is the case.
+	 */
+	if (test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
+	    test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+
+	ret = cc33xx_init_vif_data(wl, vif);
+	if (ret < 0)
+		goto out;
+
+	wlvif->wl = wl;
+	role_type = cc33xx_get_role_type(wl, wlvif);
+	if (role_type == CC33XX_INVALID_ROLE_TYPE) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = wlcore_allocate_hw_queue_base(wl, wlvif);
+	if (ret < 0)
+		goto out;
+
+	if (!wlcore_is_p2p_mgmt(wlvif)) {
+		ret = cc33xx_cmd_role_enable(wl, vif->addr,
+					     role_type, &wlvif->role_id);
+		if (ret < 0)
+			goto out;
+
+		ret = cc33xx_init_vif_specific(wl, vif);
+		if (ret < 0)
+			goto out;
+
+	} else {
+		ret = cc33xx_cmd_role_enable(wl, vif->addr, CC33XX_ROLE_DEVICE,
+					     &wlvif->dev_role_id);
+		if (ret < 0)
+			goto out;
+
+		/* needed mainly for configuring rate policies */
+		ret = cc33xx_sta_hw_init(wl, wlvif);
+		if (ret < 0)
+			goto out;
+	}
+
+	list_add(&wlvif->list, &wl->wlvif_list);
+	set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
+
+	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+		wl->ap_count++;
+	else
+		wl->sta_count++;
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static void __cc33xx_op_remove_interface(struct cc33xx *wl,
+					 struct ieee80211_vif *vif,
+					 bool reset_tx_queues)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	int i, ret;
+	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 remove interface %d", vif->type);
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 rm: name1=%s, name2=%s, name3=%s",
+		     sdata->name, sdata->dev->name, sdata->wdev.netdev->name);
+
+	if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+		return;
+
+	/* because of hardware recovery, we may get here twice */
+	if (wl->state == WLCORE_STATE_OFF)
+		return;
+
+	cc33xx_info("down");
+
+	if (wl->scan.state != CC33XX_SCAN_STATE_IDLE &&
+	    wl->scan_wlvif == wlvif) {
+		struct cfg80211_scan_info info = {
+			.aborted = true,
+		};
+
+		/*
+		 * Rearm the tx watchdog just before idling scan. This
+		 * prevents just-finished scans from triggering the watchdog
+		 */
+		cc33xx_rearm_tx_watchdog_locked(wl);
+
+		wl->scan.state = CC33XX_SCAN_STATE_IDLE;
+		memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+		wl->scan_wlvif = NULL;
+		wl->scan.req = NULL;
+		ieee80211_scan_completed(wl->hw, &info);
+	}
+
+	if (wl->sched_vif == wlvif)
+		wl->sched_vif = NULL;
+
+	if (wl->roc_vif == vif) {
+		wl->roc_vif = NULL;
+		ieee80211_remain_on_channel_expired(wl->hw);
+	}
+
+	if (!test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
+		/* disable active roles */
+
+		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+		    wlvif->bss_type == BSS_TYPE_IBSS) {
+			if (cc33xx_dev_role_started(wlvif))
+				cc33xx_stop_dev(wl, wlvif);
+		}
+
+		if (!wlcore_is_p2p_mgmt(wlvif)) {
+			ret = cc33xx_cmd_role_disable(wl, &wlvif->role_id);
+			if (ret < 0)
+				goto deinit;
+		} else {
+			ret = cc33xx_cmd_role_disable(wl, &wlvif->dev_role_id);
+			if (ret < 0)
+				goto deinit;
+		}
+	}
+deinit:
+	cc33xx_tx_reset_wlvif(wl, wlvif);
+
+	/* clear all hlids (except system_hlid) */
+	wlvif->dev_hlid = CC33XX_INVALID_LINK_ID;
+
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+	    wlvif->bss_type == BSS_TYPE_IBSS) {
+		wlvif->sta.hlid = CC33XX_INVALID_LINK_ID;
+		cc33xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx);
+		cc33xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx);
+		cc33xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
+	} else {
+		wlvif->ap.bcast_hlid = CC33XX_INVALID_LINK_ID;
+		wlvif->ap.global_hlid = CC33XX_INVALID_LINK_ID;
+		cc33xx_free_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
+		cc33xx_free_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
+		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
+			cc33xx_free_rate_policy(wl,
+						&wlvif->ap.ucast_rate_idx[i]);
+		cc33xx_free_ap_keys(wl, wlvif);
+	}
+
+	dev_kfree_skb(wlvif->probereq);
+	wlvif->probereq = NULL;
+	if (wl->last_wlvif == wlvif)
+		wl->last_wlvif = NULL;
+	list_del(&wlvif->list);
+	memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
+	wlvif->role_id = CC33XX_INVALID_ROLE_ID;
+	wlvif->dev_role_id = CC33XX_INVALID_ROLE_ID;
+
+	if (is_ap)
+		wl->ap_count--;
+	else
+		wl->sta_count--;
+
+	/*
+	 * Last AP, have more stations. Configure sleep auth according to STA.
+	 * Don't do thin on unintended recovery.
+	 */
+	if (test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+		goto unlock;
+
+	if (wl->ap_count == 0 && is_ap) {
+		/* mask ap events */
+		wl->event_mask &= ~wl->ap_event_mask;
+		cc33xx_event_unmask(wl);
+	}
+
+	if (wl->ap_count == 0 && is_ap && wl->sta_count) {
+		u8 sta_auth = wl->conf.host_conf.conn.sta_sleep_auth;
+		/* Configure for power according to debugfs */
+		if (sta_auth != CC33XX_PSM_ILLEGAL)
+			cc33xx_acx_sleep_auth(wl, sta_auth);
+		/* Configure for ELP power saving */
+		else
+			cc33xx_acx_sleep_auth(wl, CC33XX_PSM_ELP);
+	}
+
+unlock:
+	mutex_unlock(&wl->mutex);
+
+	del_timer_sync(&wlvif->rx_streaming_timer);
+	cancel_work_sync(&wlvif->rx_streaming_enable_work);
+	cancel_work_sync(&wlvif->rx_streaming_disable_work);
+	cancel_work_sync(&wlvif->rc_update_work);
+	cancel_delayed_work_sync(&wlvif->connection_loss_work);
+	cancel_delayed_work_sync(&wlvif->channel_switch_work);
+	cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
+	cancel_delayed_work_sync(&wlvif->roc_timeout_work);
+
+	mutex_lock(&wl->mutex);
+}
+
+static void cc33xx_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct cc33xx_vif *iter;
+	struct vif_counter_data vif_count;
+
+	cc33xx_get_vif_count(hw, vif, &vif_count);
+	mutex_lock(&wl->mutex);
+
+	if (wl->state == WLCORE_STATE_OFF ||
+	    !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+		goto out;
+
+	/*
+	 * wl->vif can be null here if someone shuts down the interface
+	 * just when hardware recovery has been started.
+	 */
+	cc33xx_for_each_wlvif(wl, iter) {
+		if (iter != wlvif)
+			continue;
+
+		__cc33xx_op_remove_interface(wl, vif, true);
+		break;
+	}
+	WARN_ON(iter != wlvif);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static int cc33xx_op_change_interface(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      enum nl80211_iftype new_type, bool p2p)
+{
+	struct cc33xx *wl = hw->priv;
+	int ret;
+
+	set_bit(CC33XX_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
+	cc33xx_op_remove_interface(hw, vif);
+
+	vif->type = new_type;
+	vif->p2p = p2p;
+	ret = cc33xx_op_add_interface(hw, vif);
+
+	clear_bit(CC33XX_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
+	return ret;
+}
+
+static int wlcore_join(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+	bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
+
+	/*
+	 * One of the side effects of the JOIN command is that is clears
+	 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
+	 * to a WPA/WPA2 access point will therefore kill the data-path.
+	 * Currently the only valid scenario for JOIN during association
+	 * is on roaming, in which case we will also be given new keys.
+	 * Keep the below message for now, unless it starts bothering
+	 * users who really like to roam a lot :)
+	 */
+	if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+		cc33xx_info("JOIN while associated.");
+
+	/* clear encryption type */
+	wlvif->encryption_type = KEY_NONE;
+
+	if (is_ibss)
+		ret = cc33xx_cmd_role_start_ibss(wl, wlvif);
+	else {
+		if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
+			/*
+			 * TODO: this is an ugly workaround for wl12xx fw
+			 * bug - we are not able to tx/rx after the first
+			 * start_sta, so make dummy start+stop calls,
+			 * and then call start_sta again.
+			 * this should be fixed in the fw.
+			 */
+			cc33xx_cmd_role_start_sta(wl, wlvif);
+			cc33xx_cmd_role_stop_sta(wl, wlvif);
+		}
+
+		ret = cc33xx_cmd_role_start_sta(wl, wlvif);
+	}
+
+	return ret;
+}
+
+static int cc33xx_ssid_set(struct cc33xx_vif *wlvif, struct sk_buff *skb,
+			    int offset)
+{
+	u8 ssid_len;
+	const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
+					 skb->len - offset);
+
+	if (!ptr) {
+		cc33xx_error("No SSID in IEs!");
+		return -ENOENT;
+	}
+
+	ssid_len = ptr[1];
+	if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+		cc33xx_error("SSID is too long!");
+		return -EINVAL;
+	}
+
+	wlvif->ssid_len = ssid_len;
+	memcpy(wlvif->ssid, ptr+2, ssid_len);
+	return 0;
+}
+
+static int wlcore_set_ssid(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+	struct sk_buff *skb;
+	int ieoffset;
+
+	/* we currently only support setting the ssid from the ap probe req */
+	if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+		return -EINVAL;
+
+	skb = ieee80211_ap_probereq_get(wl->hw, vif);
+	if (!skb)
+		return -EINVAL;
+
+	ieoffset = offsetof(struct ieee80211_mgmt,
+			    u.probe_req.variable);
+	cc33xx_ssid_set(wlvif, skb, ieoffset);
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+static int wlcore_set_assoc(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    struct ieee80211_bss_conf *bss_conf,struct ieee80211_sta *sta,
+			    struct ieee80211_vif *vif, u32 sta_rate_set)
+{
+	int ret;
+
+	wlvif->aid = vif->cfg.aid;
+	wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
+	wlvif->beacon_int = bss_conf->beacon_int;
+	wlvif->wmm_enabled = bss_conf->qos;
+
+	wlvif->nontransmitted = bss_conf->nontransmitted;
+	cc33xx_debug(DEBUG_MAC80211,
+	     "set_assoc mbssid params: nonTxbssid: %d, idx: %d, max_ind: %d, trans_bssid: %pM, ema_ap: %d",
+	     bss_conf->nontransmitted,
+	     bss_conf->bssid_index,
+	     bss_conf->bssid_indicator,
+	     bss_conf->transmitter_bssid,
+	     bss_conf->ema_ap);
+	wlvif->bssid_index = bss_conf->bssid_index;
+	wlvif->bssid_indicator = bss_conf->bssid_indicator;
+	memcpy(wlvif->transmitter_bssid, 
+		bss_conf->transmitter_bssid, 
+		ETH_ALEN);
+
+	set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
+	
+	ret = cc33xx_assoc_info_cfg(wl, wlvif, sta,wlvif->aid);
+	if (ret < 0)
+		return ret;
+	if (sta_rate_set) {
+		wlvif->rate_set =
+			cc33xx_tx_enabled_rates_get(wl,
+						    sta_rate_set,
+						    wlvif->band);
+	}
+	return ret;
+}
+
+static int wlcore_unset_assoc(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+	bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+
+	/* make sure we are connected (sta) joined */
+	if (sta &&
+	    !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+		return false;
+
+	/* make sure we are joined (ibss) */
+	if (!sta &&
+	    test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
+		return false;
+
+	if (sta) {
+		/* use defaults when not associated */
+		wlvif->aid = 0;
+
+		/* free probe-request template */
+		dev_kfree_skb(wlvif->probereq);
+		wlvif->probereq = NULL;
+
+		/* disable connection monitor features */
+		ret = cc33xx_acx_conn_monit_params(wl, wlvif, false);
+		if (ret < 0)
+			return ret;
+
+		/* disable beacon filtering */
+		ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, false);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
+		struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+		cc33xx_cmd_stop_channel_switch(wl, wlvif);
+		ieee80211_chswitch_done(vif, false);
+		cancel_delayed_work(&wlvif->channel_switch_work);
+	}
+
+	return 0;
+}
+
+static void cc33xx_set_band_rate(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
+	wlvif->rate_set = wlvif->basic_rate_set;
+}
+
+static void cc33xx_sta_handle_idle(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				   bool idle)
+{
+	bool cur_idle = !test_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+
+	if (idle == cur_idle)
+		return;
+
+	if (idle) {
+		clear_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+	} else {
+		/* The current firmware only supports sched_scan in idle */
+		if (wl->sched_vif == wlvif)
+			cc33xx_scan_sched_scan_stop(wl, wlvif);
+
+		set_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+	}
+}
+
+static int cc33xx_config_vif(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			     struct ieee80211_conf *conf, u64 changed)
+{
+	int ret;
+
+
+	if (wlcore_is_p2p_mgmt(wlvif))
+		return 0;
+
+	if ((conf->power_level != wlvif->power_level) &&
+	    (changed & IEEE80211_CONF_CHANGE_POWER)) {
+		ret = cc33xx_acx_tx_power(wl, wlvif, conf->power_level);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int cc33xx_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif;
+	struct ieee80211_conf *conf = &hw->conf;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
+		     " changed 0x%x",
+		     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
+		     conf->power_level,
+		     conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
+			 changed);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* configure each interface */
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		ret = cc33xx_config_vif(wl, wlvif, conf, changed);
+		if (ret < 0)
+			goto out;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+struct cc33xx_filter_params {
+	bool enabled;
+	int mc_list_length;
+	u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
+};
+
+static u64 cc33xx_op_prepare_multicast(struct ieee80211_hw *hw,
+				       struct netdev_hw_addr_list *mc_list)
+{
+	struct cc33xx_filter_params *fp;
+	struct netdev_hw_addr *ha;
+
+	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
+	if (!fp) {
+		cc33xx_error("Out of memory setting filters.");
+		return 0;
+	}
+
+	/* update multicast filtering parameters */
+	fp->mc_list_length = 0;
+	if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
+		fp->enabled = false;
+		cc33xx_debug(DEBUG_MAC80211, "mac80211 prepare multicast: too many addresses received, disable multicast filtering");
+	} else {
+		fp->enabled = true;
+		netdev_hw_addr_list_for_each(ha, mc_list) {
+			memcpy(fp->mc_list[fp->mc_list_length],
+					ha->addr, ETH_ALEN);
+			fp->mc_list_length++;
+		}
+	}
+
+	return (u64)(unsigned long)fp;
+}
+
+#define CC33XX_SUPPORTED_FILTERS (FIF_ALLMULTI /*| \
+				  FIF_FCSFAIL | \
+				  FIF_BCN_PRBRESP_PROMISC | \
+				  FIF_CONTROL | \
+				  FIF_OTHER_BSS*/)
+
+static void cc33xx_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed,
+				       unsigned int *total, u64 multicast)
+{
+	struct cc33xx_filter_params *fp = (void *)(unsigned long)multicast;
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 configure filter, FIF_ALLMULTI = %d", *total & FIF_ALLMULTI);
+
+	mutex_lock(&wl->mutex);
+
+	*total &= CC33XX_SUPPORTED_FILTERS;
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (!fp) {
+		cc33xx_acx_group_address_tbl(wl, wlvif, false, NULL, 0);
+	} else if (*total & FIF_ALLMULTI || fp->enabled == false) {
+		cc33xx_acx_group_address_tbl(wl, wlvif, false, NULL, 0);
+	} else {
+		cc33xx_acx_group_address_tbl(wl, wlvif, true, fp->mc_list, fp->mc_list_length);
+	}
+
+out:
+		mutex_unlock(&wl->mutex);
+		kfree(fp);
+}
+
+static int cc33xx_record_ap_key(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				u8 id, u8 key_type, u8 key_size,
+				const u8 *key, u8 hlid, u32 tx_seq_32,
+				u16 tx_seq_16)
+{
+	struct cc33xx_ap_key *ap_key;
+	int i;
+
+	cc33xx_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
+
+	if (key_size > MAX_KEY_SIZE)
+		return -EINVAL;
+
+	/*
+	 * Find next free entry in ap_keys. Also check we are not replacing
+	 * an existing key.
+	 */
+	for (i = 0; i < MAX_NUM_KEYS; i++) {
+		if (wlvif->ap.recorded_keys[i] == NULL)
+			break;
+
+		if (wlvif->ap.recorded_keys[i]->id == id) {
+			cc33xx_warning("trying to record key replacement");
+			return -EINVAL;
+		}
+	}
+
+	if (i == MAX_NUM_KEYS)
+		return -EBUSY;
+
+	ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
+	if (!ap_key)
+		return -ENOMEM;
+
+	ap_key->id = id;
+	ap_key->key_type = key_type;
+	ap_key->key_size = key_size;
+	memcpy(ap_key->key, key, key_size);
+	ap_key->hlid = hlid;
+	ap_key->tx_seq_32 = tx_seq_32;
+	ap_key->tx_seq_16 = tx_seq_16;
+
+	wlvif->ap.recorded_keys[i] = ap_key;
+	return 0;
+}
+
+static void cc33xx_free_ap_keys(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int i;
+
+	for (i = 0; i < MAX_NUM_KEYS; i++) {
+		kfree(wlvif->ap.recorded_keys[i]);
+		wlvif->ap.recorded_keys[i] = NULL;
+	}
+}
+
+static int cc33xx_ap_init_hwenc(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int i, ret = 0;
+	struct cc33xx_ap_key *key;
+	bool wep_key_added = false;
+
+	for (i = 0; i < MAX_NUM_KEYS; i++) {
+		u8 hlid;
+		if (wlvif->ap.recorded_keys[i] == NULL)
+			break;
+
+		key = wlvif->ap.recorded_keys[i];
+		hlid = key->hlid;
+		if (hlid == CC33XX_INVALID_LINK_ID)
+			hlid = wlvif->ap.bcast_hlid;
+
+		ret = cc33xx_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
+					    key->id, key->key_type,
+					    key->key_size, key->key,
+					    hlid, key->tx_seq_32,
+					    key->tx_seq_16);
+		if (ret < 0)
+			goto out;
+
+		if (key->key_type == KEY_WEP)
+			wep_key_added = true;
+	}
+
+	if (wep_key_added) {
+		ret = cc33xx_cmd_set_default_wep_key(wl, wlvif->default_key,
+						     wlvif->ap.bcast_hlid);
+		if (ret < 0)
+			goto out;
+	}
+
+out:
+	cc33xx_free_ap_keys(wl, wlvif);
+	return ret;
+}
+
+static int cc33xx_config_key(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       u16 action, u8 id, u8 key_type,
+		       u8 key_size, const u8 *key, u32 tx_seq_32,
+		       u16 tx_seq_16, struct ieee80211_sta *sta)
+{
+	int ret;
+	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+
+	if (is_ap) {
+		struct cc33xx_station *wl_sta;
+		u8 hlid;
+
+		if (sta) {
+			wl_sta = (struct cc33xx_station *)sta->drv_priv;
+			hlid = wl_sta->hlid;
+		} else {
+			hlid = wlvif->ap.bcast_hlid;
+		}
+
+		if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+			/*
+			 * We do not support removing keys after AP shutdown.
+			 * Pretend we do to make mac80211 happy.
+			 */
+			if (action != KEY_ADD_OR_REPLACE)
+				return 0;
+
+			ret = cc33xx_record_ap_key(wl, wlvif, id,
+					     key_type, key_size,
+					     key, hlid, tx_seq_32,
+					     tx_seq_16);
+		} else {
+			ret = cc33xx_cmd_set_ap_key(wl, wlvif, action,
+					     id, key_type, key_size,
+					     key, hlid, tx_seq_32,
+					     tx_seq_16);
+		}
+
+		if (ret < 0)
+			return ret;
+	} else {
+		const u8 *addr;
+		static const u8 bcast_addr[ETH_ALEN] = {
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+		};
+
+		addr = sta ? sta->addr : bcast_addr;
+
+		if (is_zero_ether_addr(addr)) {
+			/* We dont support TX only encryption */
+			return -EOPNOTSUPP;
+		}
+
+		/* The cc33xx does not allow to remove unicast keys - they
+		   will be cleared automatically on next CMD_JOIN. Ignore the
+		   request silently, as we dont want the mac80211 to emit
+		   an error message. */
+		if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
+			return 0;
+
+		/* don't remove key if hlid was already deleted */
+		if (action == KEY_REMOVE &&
+		    wlvif->sta.hlid == CC33XX_INVALID_LINK_ID)
+			return 0;
+
+		ret = cc33xx_cmd_set_sta_key(wl, wlvif, action,
+					     id, key_type, key_size,
+					     key, addr, tx_seq_32,
+					     tx_seq_16);
+		if (ret < 0)
+			return ret;
+
+	}
+
+	return 0;
+}
+
+static int cc33xx_set_host_cfg_bitmap(struct cc33xx *wl, u32 extra_mem_blk)
+{
+	int ret;
+	u32 sdio_align_size = 0;
+	u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE |
+			      HOST_IF_CFG_ADD_RX_ALIGNMENT;
+
+	/* Enable Tx SDIO padding */
+	if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) {
+		host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
+		sdio_align_size = CC33XX_BUS_BLOCK_SIZE;
+	}
+
+	/* Enable Rx SDIO padding */
+	if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) {
+		host_cfg_bitmap |= HOST_IF_CFG_RX_PAD_TO_SDIO_BLK;
+		sdio_align_size = CC33XX_BUS_BLOCK_SIZE;
+	}
+
+	ret = cc33xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap,
+					    sdio_align_size, extra_mem_blk,
+					    CC33XX_HOST_IF_LEN_SIZE_FIELD);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cc33xx_set_key(struct cc33xx *wl, enum set_key_cmd cmd,
+			  struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta,
+			  struct ieee80211_key_conf *key_conf)
+{
+	bool change_spare = false, special_enc;
+	int ret;
+
+	cc33xx_debug(DEBUG_CRYPT, "extra spare keys before: %d",
+		     wl->extra_spare_key_count);
+
+	special_enc = key_conf->cipher == CC33XX_CIPHER_SUITE_GEM ||
+		      key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
+
+	ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * when adding the first or removing the last GEM/TKIP key,
+	 * we have to adjust the number of spare blocks.
+	 */
+	if (special_enc) {
+		if (cmd == SET_KEY) {
+			/* first key */
+			change_spare = (wl->extra_spare_key_count == 0);
+			wl->extra_spare_key_count++;
+		} else if (cmd == DISABLE_KEY) {
+			/* last key */
+			change_spare = (wl->extra_spare_key_count == 1);
+			wl->extra_spare_key_count--;
+		}
+	}
+
+	cc33xx_debug(DEBUG_CRYPT, "extra spare keys after: %d",
+		     wl->extra_spare_key_count);
+
+	if (!change_spare)
+		goto out;
+
+	/* key is now set, change the spare blocks */
+	if (wl->extra_spare_key_count)
+		ret = cc33xx_set_host_cfg_bitmap(wl,
+					CC33XX_TX_HW_EXTRA_BLOCK_SPARE);
+	else
+		ret = cc33xx_set_host_cfg_bitmap(wl,
+					CC33XX_TX_HW_BLOCK_SPARE);
+
+out:
+	return ret;
+}
+
+static int cc33xx_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_sta *sta,
+			     struct ieee80211_key_conf *key_conf)
+{
+	struct cc33xx *wl = hw->priv;
+	int ret;
+	bool might_change_spare =
+		key_conf->cipher == CC33XX_CIPHER_SUITE_GEM ||
+		key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
+
+	if (might_change_spare) {
+		/*
+		 * stop the queues and flush to ensure the next packets are
+		 * in sync with FW spare block accounting
+		 */
+		wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+		cc33xx_tx_flush(wl);
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EAGAIN;
+		goto out_wake_queues;
+	}
+
+	ret = cc33xx_set_key(wl, cmd, vif, sta, key_conf);
+
+out_wake_queues:
+	if (might_change_spare)
+		wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+int wlcore_set_key(struct cc33xx *wl, enum set_key_cmd cmd,
+		   struct ieee80211_vif *vif,
+		   struct ieee80211_sta *sta,
+		   struct ieee80211_key_conf *key_conf)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+	u32 tx_seq_32 = 0;
+	u16 tx_seq_16 = 0;
+	u8 key_type;
+	u8 hlid;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 set key");
+
+	cc33xx_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
+	cc33xx_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
+		     key_conf->cipher, key_conf->keyidx,
+		     key_conf->keylen, key_conf->flags);
+	cc33xx_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
+
+	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+		if (sta) {
+			struct cc33xx_station *wl_sta = (void *)sta->drv_priv;
+			hlid = wl_sta->hlid;
+		} else {
+			hlid = wlvif->ap.bcast_hlid;
+		}
+	else
+		hlid = wlvif->sta.hlid;
+
+	if (hlid != CC33XX_INVALID_LINK_ID) {
+		u64 tx_seq = wl->links[hlid].total_freed_pkts;
+		tx_seq_32 = CC33XX_TX_SECURITY_HI32(tx_seq);
+		tx_seq_16 = CC33XX_TX_SECURITY_LO16(tx_seq);
+	}
+
+	switch (key_conf->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		key_type = KEY_WEP;
+
+		key_conf->hw_key_idx = key_conf->keyidx;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		key_type = KEY_TKIP;
+		key_conf->hw_key_idx = key_conf->keyidx;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		key_type = KEY_AES;
+		key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+		key_type = KEY_GCMP128;
+		key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+		break;		
+	case WLAN_CIPHER_SUITE_CCMP_256:
+		key_type = KEY_CCMP256;
+		key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		key_type = KEY_GCMP_256;
+		key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+		break;		
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		key_type = KEY_IGTK;
+		break;
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		key_type = KEY_CMAC_256;
+		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+		key_type =  KEY_GMAC_128;
+		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		key_type =  KEY_GMAC_256;
+		break;
+	case CC33XX_CIPHER_SUITE_GEM:
+		key_type = KEY_GEM;
+		break;
+	default:
+		cc33xx_error("Unknown key algo 0x%x", key_conf->cipher);
+
+		return -EOPNOTSUPP;
+	}
+
+	switch (cmd) {
+	case SET_KEY:
+		ret = cc33xx_config_key(wl, wlvif, KEY_ADD_OR_REPLACE,
+				 key_conf->keyidx, key_type,
+				 key_conf->keylen, key_conf->key,
+				 tx_seq_32, tx_seq_16, sta);
+		if (ret < 0) {
+			cc33xx_error("Could not add or replace key");
+			return ret;
+		}
+
+		/*
+		 * reconfiguring arp response if the unicast (or common)
+		 * encryption key type was changed
+		 */
+		if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+		    (sta || key_type == KEY_WEP) &&
+		    wlvif->encryption_type != key_type) {
+			wlvif->encryption_type = key_type;
+			ret = cc33xx_cmd_build_arp_rsp(wl, wlvif);
+			if (ret < 0) {
+				cc33xx_warning("build arp rsp failed: %d", ret);
+				return ret;
+			}
+		}
+		break;
+
+	case DISABLE_KEY:
+		ret = cc33xx_config_key(wl, wlvif, KEY_REMOVE,
+				     key_conf->keyidx, key_type,
+				     key_conf->keylen, key_conf->key,
+				     0, 0, sta);
+		if (ret < 0) {
+			cc33xx_error("Could not remove key");
+			return ret;
+		}
+		break;
+
+	default:
+		cc33xx_error("Unsupported key cmd 0x%x", cmd);
+		return -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+static void cc33xx_op_set_default_key_idx(struct ieee80211_hw *hw,
+					  struct ieee80211_vif *vif,
+					  int key_idx)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
+		     key_idx);
+
+	/* we don't handle unsetting of default key */
+	if (key_idx == -1)
+		return;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		goto out_unlock;
+	}
+
+	wlvif->default_key = key_idx;
+
+	/* the default WEP key needs to be configured at least once */
+	if (wlvif->encryption_type == KEY_WEP) {
+		cc33xx_cmd_set_default_wep_key(wl,
+				key_idx,
+				wlvif->sta.hlid);
+	}
+
+out_unlock:
+	mutex_unlock(&wl->mutex);
+}
+
+void wlcore_regdomain_config(struct cc33xx *wl)
+{
+	int ret;
+
+	if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+		return;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	//ret = wlcore_cmd_regdomain_config_locked(wl);
+	if (ret < 0) {
+		cc33xx_queue_recovery_work(wl);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static int cc33xx_op_hw_scan(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_scan_request *hw_req)
+{
+	struct cfg80211_scan_request *req = &hw_req->req;
+	struct cc33xx *wl = hw->priv;
+	int ret;
+	u8 *ssid = NULL;
+	size_t len = 0;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 hw scan");
+
+	if (req->n_ssids) {
+		ssid = req->ssids[0].ssid;
+		len = req->ssids[0].ssid_len;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		/*
+		 * We cannot return -EBUSY here because cfg80211 will expect
+		 * a call to ieee80211_scan_completed if we do - in this case
+		 * there won't be any call.
+		 */
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	/* fail if there is any role in ROC */
+	if (find_first_bit(wl->roc_map, CC33XX_MAX_ROLES) < CC33XX_MAX_ROLES) {
+		/* don't allow scanning right now */
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = wlcore_scan(hw->priv, vif, ssid, len, req);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static void cc33xx_op_cancel_hw_scan(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct cfg80211_scan_info info = {
+		.aborted = true,
+	};
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (wl->scan.state == CC33XX_SCAN_STATE_IDLE)
+		goto out;
+
+	if (wl->scan.state != CC33XX_SCAN_STATE_DONE) {
+		ret = cc33xx_scan_stop(wl, wlvif);
+		if (ret < 0)
+			goto out;
+	}
+
+	/*
+	 * Rearm the tx watchdog just before idling scan. This
+	 * prevents just-finished scans from triggering the watchdog
+	 */
+	cc33xx_rearm_tx_watchdog_locked(wl);
+
+	wl->scan.state = CC33XX_SCAN_STATE_IDLE;
+	memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+	wl->scan_wlvif = NULL;
+	wl->scan.req = NULL;
+	ieee80211_scan_completed(wl->hw, &info);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	cancel_delayed_work_sync(&wl->scan_complete_work);
+}
+
+static int cc33xx_op_sched_scan_start(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      struct cfg80211_sched_scan_request *req,
+				      struct ieee80211_scan_ies *ies)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "cc33xx_op_sched_scan_start");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = cc33xx_sched_scan_start(wl, wlvif, req, ies);
+	if (ret < 0)
+		goto out;
+
+	wl->sched_vif = wlvif;
+
+out:
+	mutex_unlock(&wl->mutex);
+	return ret;
+}
+
+static int cc33xx_op_sched_scan_stop(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+
+	cc33xx_debug(DEBUG_MAC80211, "cc33xx_op_sched_scan_stop");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	// command to stop periodic scan was sent from mac80211
+	// mark than stop command is from mac80211 and release 
+	// sched_vif
+	wl->mac80211_scan_stopped = true;
+	wl->sched_vif = NULL;
+	cc33xx_scan_sched_scan_stop(wl, wlvif);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return 0;
+}
+
+static int cc33xx_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct cc33xx *wl = hw->priv;
+	int ret = 0;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = cc33xx_acx_frag_threshold(wl, value);
+	if (ret < 0)
+		cc33xx_warning("cc33xx_op_set_frag_threshold failed: %d", ret);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static int cc33xx_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif;
+	int ret = 0;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		ret = cc33xx_acx_rts_threshold(wl, wlvif, value);
+		if (ret < 0)
+			cc33xx_warning("set rts threshold failed: %d", ret);
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static int cc33xx_bss_erp_info_changed(struct cc33xx *wl,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u64 changed)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret = 0;
+
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		if (bss_conf->use_short_slot)
+			ret = cc33xx_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
+		else
+			ret = cc33xx_acx_slot(wl, wlvif, SLOT_TIME_LONG);
+		if (ret < 0) {
+			cc33xx_warning("Set slot time failed %d", ret);
+			goto out;
+		}
+	}
+
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		if (bss_conf->use_short_preamble)
+			cc33xx_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
+		else
+			cc33xx_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
+	}
+
+	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+		if (bss_conf->use_cts_prot)
+			ret = cc33xx_acx_cts_protect(wl, wlvif,
+						     CTSPROTECT_ENABLE);
+		else
+			ret = cc33xx_acx_cts_protect(wl, wlvif,
+						     CTSPROTECT_DISABLE);
+		if (ret < 0) {
+			cc33xx_warning("Set ctsprotect failed %d", ret);
+			goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int wlcore_set_beacon_template(struct cc33xx *wl,
+				      struct ieee80211_vif *vif,
+				      bool is_ap)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+	int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+	struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif, 0);
+
+	struct cc33xx_cmd_set_beacon_info *cmd;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+    	 if (!cmd) {
+       		ret = -ENOMEM;
+		goto out;
+    	}
+
+	if (!beacon) {
+		ret = -EINVAL;
+		goto end_bcn;
+	}
+
+	cc33xx_debug(DEBUG_MASTER, "beacon updated");
+
+	ret = cc33xx_ssid_set(wlvif, beacon, ieoffset);
+	if (ret < 0) {
+		goto end_bcn;
+	}
+
+	cmd->role_id =  wlvif->role_id;
+	cmd->beacon_len = cpu_to_le16(beacon->len);
+
+	memcpy(cmd->beacon, beacon->data, beacon->len);
+
+	ret = cc33xx_cmd_send(wl, CMD_AP_SET_BEACON_INFO, cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		goto end_bcn;
+	}
+
+end_bcn:
+	dev_kfree_skb(beacon);
+	kfree(cmd);
+out:
+	return ret;
+}
+
+static int cc33xx_bss_beacon_info_changed(struct cc33xx *wl,
+					  struct ieee80211_vif *vif,
+					  struct ieee80211_bss_conf *bss_conf,
+					  u32 changed)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+	int ret = 0;
+
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		cc33xx_debug(DEBUG_MASTER, "beacon interval updated: %d",
+			bss_conf->beacon_int);
+
+		wlvif->beacon_int = bss_conf->beacon_int;
+	}
+
+	if (changed & BSS_CHANGED_BEACON) {
+		ret = wlcore_set_beacon_template(wl, vif, is_ap);
+		if (ret < 0)
+			goto out;
+
+		if (test_and_clear_bit(WLVIF_FLAG_BEACON_DISABLED,
+				       &wlvif->flags)) {
+			ret = cmd_dfs_master_restart(wl, wlvif);
+			if (ret < 0)
+				goto out;
+		}
+	}
+out:
+	if (ret != 0)
+		cc33xx_error("beacon info change failed: %d", ret);
+	return ret;
+}
+
+/* AP mode changes */
+static void cc33xx_bss_info_changed_ap(struct cc33xx *wl,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u64 changed)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret = 0;
+
+	if (changed & BSS_CHANGED_BASIC_RATES) {
+		u32 rates = bss_conf->basic_rates;
+		u32 supported_rates = 0;
+		wlvif->basic_rate_set = cc33xx_tx_enabled_rates_get(wl, rates,
+								 wlvif->band);
+		wlvif->basic_rate = cc33xx_tx_min_rate_get(wl,
+							wlvif->basic_rate_set);
+		
+		supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES ;
+		ret = cc33xx_update_ap_rates(wl,wlvif->role_id,wlvif->basic_rate_set,supported_rates);
+		
+		ret = wlcore_set_beacon_template(wl, vif, true);
+		if (ret < 0)
+			goto out;	
+	}
+
+	ret = cc33xx_bss_beacon_info_changed(wl, vif, bss_conf, changed);
+	if (ret < 0)
+		goto out;
+		
+	if (changed & BSS_CHANGED_BEACON_ENABLED) {
+		if (bss_conf->enable_beacon) {
+			if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+				ret = cc33xx_cmd_role_start_ap(wl, wlvif);
+				if (ret < 0)
+					goto out;
+
+				ret = cc33xx_ap_init_hwenc(wl, wlvif);
+				if (ret < 0)
+					goto out;
+
+				set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
+				cc33xx_debug(DEBUG_AP, "started AP");
+			}
+		} else {
+			if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+				/*
+				 * AP might be in ROC in case we have just
+				 * sent auth reply. handle it.
+				 */
+				if (test_bit(wlvif->role_id, wl->roc_map))
+					cc33xx_croc(wl, wlvif->role_id);
+
+				ret = cc33xx_cmd_role_stop_ap(wl, wlvif);
+				if (ret < 0)
+					goto out;
+
+				clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
+				clear_bit(WLVIF_FLAG_AP_PROBE_RESP_SET,
+					  &wlvif->flags);
+				cc33xx_debug(DEBUG_AP, "stopped AP");
+			}
+		}
+	}
+
+	ret = cc33xx_bss_erp_info_changed(wl, vif, bss_conf, changed);
+	if (ret < 0)
+		goto out;
+
+out:
+	return;
+}
+
+static int wlcore_set_bssid(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    struct ieee80211_bss_conf *bss_conf,
+				struct ieee80211_vif *vif,
+			    u32 sta_rate_set)
+{
+	u32 rates;
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211,
+	     "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x, nontx: %d",
+	     bss_conf->bssid, vif->cfg.aid,
+	     bss_conf->beacon_int,
+	     bss_conf->basic_rates, sta_rate_set,
+	     bss_conf->nontransmitted);
+
+	wlvif->beacon_int = bss_conf->beacon_int;
+	rates = bss_conf->basic_rates;
+	wlvif->basic_rate_set =
+		cc33xx_tx_enabled_rates_get(wl, rates,
+					    wlvif->band);
+	wlvif->basic_rate =
+		cc33xx_tx_min_rate_get(wl,
+				       wlvif->basic_rate_set);
+
+	if (sta_rate_set)
+		wlvif->rate_set =
+			cc33xx_tx_enabled_rates_get(wl,
+						sta_rate_set,
+						wlvif->band);
+
+	wlvif->nontransmitted = bss_conf->nontransmitted;
+	cc33xx_debug(DEBUG_MAC80211,
+	     "changed_mbssid: nonTxbssid: %d, idx: %d, max_ind: %d, trans_bssid: %pM, ema_ap: %d",
+	     bss_conf->nontransmitted,
+	     bss_conf->bssid_index,
+	     bss_conf->bssid_indicator,
+	     bss_conf->transmitter_bssid,
+	     bss_conf->ema_ap);
+	if (bss_conf->nontransmitted)
+	{
+		wlvif->bssid_index = bss_conf->bssid_index;
+		wlvif->bssid_indicator = bss_conf->bssid_indicator;
+		memcpy(wlvif->transmitter_bssid, 
+			bss_conf->transmitter_bssid, 
+			ETH_ALEN);
+	}
+	/* we only support sched_scan while not connected */
+	if (wl->sched_vif == wlvif)
+		cc33xx_scan_sched_scan_stop(wl, wlvif);
+
+	ret = cc33xx_cmd_build_null_data(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	ret = cc33xx_build_qos_null_data(wl, cc33xx_wlvif_to_vif(wlvif));
+	if (ret < 0)
+		return ret;
+
+	wlcore_set_ssid(wl, wlvif);
+
+	set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+
+	return 0;
+}
+
+static int cc33xx_set_peer_cap(struct cc33xx *wl,
+			       struct ieee80211_sta_ht_cap *ht_cap,
+			       struct ieee80211_sta_he_cap *he_cap,
+			       struct cc33xx_vif *wlvif,
+			       bool allow_ht_operation,
+			       u32 rate_set, u8 hlid)
+{
+
+    return cc33xx_acx_set_peer_cap(wl, ht_cap, he_cap, wlvif, allow_ht_operation,
+			   rate_set, hlid);
+}
+
+static int wlcore_clear_bssid(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int ret;
+
+	/* revert back to minimum rates for the current band */
+	cc33xx_set_band_rate(wl, wlvif);
+	wlvif->basic_rate = cc33xx_tx_min_rate_get(wl, wlvif->basic_rate_set);
+
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+	    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
+		ret = cc33xx_cmd_role_stop_sta(wl, wlvif);
+		if (ret < 0)
+			return ret;
+	}
+
+	clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+	return 0;
+}
+/* STA/IBSS mode changes */
+static void cc33xx_bss_info_changed_sta(struct cc33xx *wl,
+					struct ieee80211_vif *vif,
+					struct ieee80211_bss_conf *bss_conf,
+					u64 changed)
+{
+
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	bool do_join = false;
+	bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
+	bool ibss_joined = false;
+	u32 sta_rate_set = 0;
+	int ret;
+	struct ieee80211_sta *sta;
+	bool sta_exists = false;
+	struct ieee80211_sta_ht_cap sta_ht_cap;
+	struct ieee80211_sta_he_cap sta_he_cap;
+	
+	if (is_ibss) {
+		ret = cc33xx_bss_beacon_info_changed(wl, vif, bss_conf,
+						     changed);
+		if (ret < 0)
+			goto out;
+	}
+	
+
+	if (changed & BSS_CHANGED_IBSS) {
+		if (vif->cfg.ibss_joined) {
+			set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
+			ibss_joined = true;
+		} else {
+			wlcore_unset_assoc(wl, wlvif);
+			cc33xx_cmd_role_stop_sta(wl, wlvif);
+		}
+	}
+
+	if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
+		do_join = true;
+
+	/* Need to update the SSID (for filtering etc) */
+	if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
+		do_join = true;
+
+	if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
+		cc33xx_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
+			     bss_conf->enable_beacon ? "enabled" : "disabled");
+
+		do_join = true;
+	}
+
+	if (changed & BSS_CHANGED_IDLE && !is_ibss)
+		cc33xx_sta_handle_idle(wl, wlvif, vif->cfg.idle);
+
+	if (changed & BSS_CHANGED_CQM) {
+		bool enable = false;
+		if (bss_conf->cqm_rssi_thold)
+			enable = true;
+		ret = cc33xx_acx_rssi_snr_trigger(wl, wlvif, enable,
+						  bss_conf->cqm_rssi_thold,
+						  bss_conf->cqm_rssi_hyst);
+		if (ret < 0)
+			goto out;
+		wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
+	}
+
+	if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
+		       BSS_CHANGED_ASSOC)) {
+		rcu_read_lock();
+		sta = ieee80211_find_sta(vif, bss_conf->bssid);
+		if (sta) {
+			u8 *rx_mask = sta->deflink.ht_cap.mcs.rx_mask;
+
+			/* save the supp_rates of the ap */
+			sta_rate_set = sta->deflink.supp_rates[wlvif->band];
+			if (sta->deflink.ht_cap.ht_supported)
+				sta_rate_set |=
+					(rx_mask[0] << HW_HT_RATES_OFFSET) |
+					(rx_mask[1] << HW_MIMO_RATES_OFFSET);
+			sta_ht_cap = sta->deflink.ht_cap;
+			sta_he_cap = sta->deflink.he_cap;
+			sta_exists = true;
+		}
+
+		rcu_read_unlock();
+	}
+
+	if (changed & BSS_CHANGED_BSSID) {
+		if (!is_zero_ether_addr(bss_conf->bssid)) {
+			ret = wlcore_set_bssid(wl, wlvif, bss_conf,
+					       vif, sta_rate_set);
+			if (ret < 0)
+				goto out;
+
+			/* Need to update the BSSID (for filtering etc) */
+			do_join = true;
+		} else {
+			ret = wlcore_clear_bssid(wl, wlvif);
+			if (ret < 0)
+				goto out;
+		}
+	}
+
+	if (changed & BSS_CHANGED_IBSS) {
+		cc33xx_debug(DEBUG_ADHOC, "ibss_joined: %d",
+			     vif->cfg.ibss_joined);
+
+		if (vif->cfg.ibss_joined) {
+			u32 rates = bss_conf->basic_rates;
+			wlvif->basic_rate_set =
+				cc33xx_tx_enabled_rates_get(wl, rates,
+							    wlvif->band);
+			wlvif->basic_rate =
+				cc33xx_tx_min_rate_get(wl,
+						       wlvif->basic_rate_set);
+
+			/* by default, use 11b + OFDM rates */
+			wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
+		}
+	}
+
+	if ((changed & BSS_CHANGED_BEACON_INFO) && bss_conf->dtim_period) {
+		/* enable beacon filtering */
+		ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, true);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = cc33xx_bss_erp_info_changed(wl, vif, bss_conf, changed);
+	if (ret < 0)
+		goto out;
+
+	if (do_join) {
+		ret = wlcore_join(wl, wlvif);
+		if (ret < 0) {
+			cc33xx_warning("cmd join failed %d", ret);
+			goto out;
+		}
+	}
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		if (vif->cfg.assoc) {
+			ret = wlcore_set_assoc(wl, wlvif, bss_conf,sta, vif,
+					       sta_rate_set);
+			if (ret < 0)
+				goto out;
+
+			if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
+				cc33xx_set_authorized(wl, wlvif);
+			if (sta) {
+				struct cc33xx_vif *wlvif_itr;
+				u8 he_count = 0;
+
+				wlvif->sta_has_he = sta->deflink.he_cap.has_he;
+
+				if (sta->deflink.he_cap.has_he) {
+					cc33xx_info("HE Enabled");		
+				} else {
+					cc33xx_info("HE Disabled");
+				}
+
+				cc33xx_for_each_wlvif_sta(wl, wlvif_itr) {
+					//check for all valid link id's
+					if (wlvif_itr->role_id != 0xFF) {
+						if (wlvif_itr->sta_has_he)
+							he_count++;
+					}
+				}
+				/* There can't be two stations connected with HE supported links*/
+				if (he_count > 1)
+					cc33xx_error("WARNING: Both station interfaces has HE enabled!");
+			}
+			
+		} else {
+			wlcore_unset_assoc(wl, wlvif);
+		}
+	}
+
+	if (changed & BSS_CHANGED_PS) {
+		if ((vif->cfg.ps) &&
+		    test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
+		    !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+			int ps_mode;
+			char *ps_mode_str;
+
+			if (wl->conf.host_conf.conn.forced_ps) {
+				ps_mode = STATION_POWER_SAVE_MODE;
+				ps_mode_str = "forced";
+			} else {
+				ps_mode = STATION_AUTO_PS_MODE;
+				ps_mode_str = "auto";
+			}
+
+			cc33xx_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
+
+			ret = cc33xx_ps_set_mode(wl, wlvif, ps_mode);
+			if (ret < 0)
+				cc33xx_warning("enter %s ps failed %d",
+					       ps_mode_str, ret);
+		} else if (!vif->cfg.ps &&
+			   test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+			cc33xx_debug(DEBUG_PSM, "auto ps disabled");
+
+			ret = cc33xx_ps_set_mode(wl, wlvif,
+						 STATION_ACTIVE_MODE);
+			if (ret < 0)
+				cc33xx_warning("exit auto ps failed %d", ret);
+		}
+	}
+
+	/* Handle new association with HT. Do this after join. */
+	if (sta_exists) {
+		bool enabled =
+			bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+		cc33xx_debug(DEBUG_CMD, " +++Debug wlcore_hw_set_peer_cap %x", wlvif->rate_set);
+		ret = cc33xx_set_peer_cap(	wl,
+						&sta_ht_cap,
+						&sta_he_cap,
+						wlvif,
+						enabled,
+						wlvif->rate_set,
+						wlvif->sta.hlid);
+		if (ret < 0) {
+			cc33xx_warning("Set ht cap failed %d", ret);
+			goto out;
+
+		}
+
+		if (enabled) {
+			ret = cc33xx_acx_set_ht_information(wl, wlvif,
+						bss_conf->ht_operation_mode,
+						bss_conf->he_oper.params, 
+						bss_conf->he_oper.nss_set);
+			if (ret < 0) {
+				cc33xx_warning("Set ht information failed %d",
+					       ret);
+				goto out;
+			}
+		}
+	}
+
+	/* Handle arp filtering. Done after join. */
+	if ((changed & BSS_CHANGED_ARP_FILTER) ||
+	    (!is_ibss && (changed & BSS_CHANGED_QOS))) {
+		__be32 addr = vif->cfg.arp_addr_list[0];
+		wlvif->sta.qos = bss_conf->qos;
+		WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
+
+		if (vif->cfg.arp_addr_cnt == 1 && vif->cfg.assoc) {
+			wlvif->ip_addr = addr;
+			/*
+			 * The template should have been configured only upon
+			 * association. however, it seems that the correct ip
+			 * isn't being set (when sending), so we have to
+			 * reconfigure the template upon every ip change.
+			 */
+			ret = cc33xx_cmd_build_arp_rsp(wl, wlvif);
+			if (ret < 0) {
+				cc33xx_warning("build arp rsp failed: %d", ret);
+				goto out;
+			}
+
+			ret = cc33x_acx_arp_ip_filter(wl, wlvif,
+				(ACX_ARP_FILTER_ARP_FILTERING |
+				 ACX_ARP_FILTER_AUTO_ARP),
+				addr);
+		} else {
+			wlvif->ip_addr = 0;
+			ret = cc33x_acx_arp_ip_filter(wl, wlvif, 0, addr);
+		}
+
+		if (ret < 0)
+			goto out;
+	}
+
+out:
+	return;
+}
+
+static void cc33xx_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u64 changed)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+	int ret, set_power;
+
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
+		     wlvif->role_id, (int)changed);
+
+	/*
+	 * make sure to cancel pending disconnections if our association
+	 * state changed
+	 */
+	if (!is_ap && (changed & BSS_CHANGED_ASSOC))
+		cancel_delayed_work_sync(&wlvif->connection_loss_work);
+
+	if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
+	    !bss_conf->enable_beacon)
+		cc33xx_tx_flush(wl);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+		goto out;
+
+	
+	if ((changed & BSS_CHANGED_TXPOWER) &&
+	    (bss_conf->txpower != wlvif->power_level)) {
+		/* bss_conf->txpower is initialized with a default value,
+	   	meaning the power has not been set and should be ignored, use
+		max value instead */
+		set_power = (bss_conf->txpower == INT_MIN) ? CC33XX_MAX_TXPWR :
+							     bss_conf->txpower;
+		ret = cc33xx_acx_tx_power(wl, wlvif, set_power);
+		
+		if (ret < 0)
+			goto out;
+	}
+
+	if (is_ap)
+		cc33xx_bss_info_changed_ap(wl, vif, bss_conf, changed);
+	else
+		cc33xx_bss_info_changed_sta(wl, vif, bss_conf, changed);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static int cc33xx_op_add_chanctx(struct ieee80211_hw *hw,
+				 struct ieee80211_chanctx_conf *ctx)
+{
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
+		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+		     cfg80211_get_chandef_type(&ctx->def));
+	return 0;
+}
+
+static void cc33xx_op_remove_chanctx(struct ieee80211_hw *hw,
+				     struct ieee80211_chanctx_conf *ctx)
+{
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
+		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+		     cfg80211_get_chandef_type(&ctx->def));
+}
+
+static void cc33xx_op_change_chanctx(struct ieee80211_hw *hw,
+				     struct ieee80211_chanctx_conf *ctx,
+				     u32 changed)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif;
+	int channel = ieee80211_frequency_to_channel(
+		ctx->def.chan->center_freq);
+
+	cc33xx_debug(DEBUG_MAC80211,
+		     "mac80211 change chanctx %d (type %d) changed 0x%x",
+		     channel, cfg80211_get_chandef_type(&ctx->def), changed);
+
+	mutex_lock(&wl->mutex);
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+		rcu_read_lock();
+		if (rcu_access_pointer(vif->bss_conf.chanctx_conf) != ctx) {
+			rcu_read_unlock();
+			continue;
+		}
+		rcu_read_unlock();
+
+		/* start radar if needed */
+		if (changed & IEEE80211_CHANCTX_CHANGE_RADAR &&
+		    wlvif->bss_type == BSS_TYPE_AP_BSS &&
+		    ctx->radar_enabled && !wlvif->radar_enabled &&
+		    ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
+			cc33xx_debug(DEBUG_MAC80211, "Start radar detection");
+			cmd_set_cac(wl, wlvif, true);
+			wlvif->radar_enabled = true;
+		}
+	}
+
+	mutex_unlock(&wl->mutex);
+}
+
+static int cc33xx_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+					struct ieee80211_vif *vif,
+					struct ieee80211_bss_conf *link_conf,
+					struct ieee80211_chanctx_conf *ctx)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int channel = ieee80211_frequency_to_channel(
+		ctx->def.chan->center_freq);
+
+	cc33xx_debug(DEBUG_MAC80211,
+		     "mac80211 assign chanctx (role %d) %d (type %d)"
+		     "(radar %d dfs_state %d)",
+		     wlvif->role_id, channel,
+		     cfg80211_get_chandef_type(&ctx->def),
+		     ctx->radar_enabled, ctx->def.chan->dfs_state);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+		goto out;
+
+	wlvif->band = ctx->def.chan->band;
+	wlvif->channel = channel;
+	wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
+
+	/* update default rates according to the band */
+	cc33xx_set_band_rate(wl, wlvif);
+
+	if (ctx->radar_enabled &&
+	    ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
+		cc33xx_debug(DEBUG_MAC80211, "Start radar detection");
+		cmd_set_cac(wl, wlvif, true);
+		wlvif->radar_enabled = true;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return 0;
+}
+
+static void cc33xx_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
+					   struct ieee80211_vif *vif,
+					   struct ieee80211_bss_conf *link_conf,
+					   struct ieee80211_chanctx_conf *ctx)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+
+	cc33xx_debug(DEBUG_MAC80211,
+		     "mac80211 unassign chanctx (role %d) %d (type %d)",
+		     wlvif->role_id,
+		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+		     cfg80211_get_chandef_type(&ctx->def));
+
+	cc33xx_tx_flush(wl);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+		goto out;
+
+	if (wlvif->radar_enabled) {
+		cc33xx_debug(DEBUG_MAC80211, "Stop radar detection");
+		cmd_set_cac(wl, wlvif, false);
+		wlvif->radar_enabled = false;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static int __wlcore_switch_vif_chan(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif,
+				    struct ieee80211_chanctx_conf *new_ctx)
+{
+	int channel = ieee80211_frequency_to_channel(
+		new_ctx->def.chan->center_freq);
+
+	cc33xx_debug(DEBUG_MAC80211,
+		     "switch vif (role %d) %d -> %d chan_type: %d",
+		     wlvif->role_id, wlvif->channel, channel,
+		     cfg80211_get_chandef_type(&new_ctx->def));
+
+	if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+		return 0;
+
+	WARN_ON(!test_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags));
+
+	if (wlvif->radar_enabled) {
+		cc33xx_debug(DEBUG_MAC80211, "Stop radar detection");
+		cmd_set_cac(wl, wlvif, false);
+		wlvif->radar_enabled = false;
+	}
+
+	wlvif->band = new_ctx->def.chan->band;
+	wlvif->channel = channel;
+	wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def);
+
+	/* start radar if needed */
+	if (new_ctx->radar_enabled) {
+		cc33xx_debug(DEBUG_MAC80211, "Start radar detection");
+		cmd_set_cac(wl, wlvif, true);
+		wlvif->radar_enabled = true;
+	}
+
+	return 0;
+}
+
+static int
+cc33xx_op_switch_vif_chanctx(struct ieee80211_hw *hw,
+			     struct ieee80211_vif_chanctx_switch *vifs,
+			     int n_vifs,
+			     enum ieee80211_chanctx_switch_mode mode)
+{
+	struct cc33xx *wl = hw->priv;
+	int i, ret;
+
+	cc33xx_debug(DEBUG_MAC80211,
+		     "mac80211 switch chanctx n_vifs %d mode %d",
+		     n_vifs, mode);
+
+	mutex_lock(&wl->mutex);
+
+	for (i = 0; i < n_vifs; i++) {
+		struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vifs[i].vif);
+
+		ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx);
+		if (ret)
+			goto out;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return 0;
+}
+
+static int cc33xx_op_conf_tx(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif, unsigned int link_id, u16 queue,
+			     const struct ieee80211_tx_queue_params *params)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	u8 ps_scheme;
+	int ret = 0;
+
+	if (wlcore_is_p2p_mgmt(wlvif))
+		return 0;
+
+	mutex_lock(&wl->mutex);
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
+
+	if (params->uapsd)
+		ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
+	else
+		ps_scheme = CONF_PS_SCHEME_LEGACY;
+
+	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+		goto out;
+
+	/*
+	 * the txop is confed in units of 32us by the mac80211,
+	 * we need us
+	 */
+    ret = cc33xx_tx_param_cfg(wl, wlvif, cc33xx_tx_get_queue(queue),
+		params->cw_min, params->cw_max,
+		params->aifs, params->txop << 5, params->acm,
+		ps_scheme, params->mu_edca, 
+		params->mu_edca_param_rec.aifsn, 
+		params->mu_edca_param_rec.ecw_min_max, 
+		params->mu_edca_param_rec.mu_edca_timer);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static u64 cc33xx_op_get_tsf(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif)
+{
+
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	u64 mactime = ULLONG_MAX;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 get tsf");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	cc33xx_acx_tsf_info(wl, wlvif, &mactime);
+
+out:
+	mutex_unlock(&wl->mutex);
+	return mactime;
+}
+
+static int cc33xx_op_get_survey(struct ieee80211_hw *hw, int idx,
+				struct survey_info *survey)
+{
+	struct ieee80211_conf *conf = &hw->conf;
+
+	if (idx != 0)
+		return -ENOENT;
+
+	survey->channel = conf->chandef.chan;
+	survey->filled = 0;
+	return 0;
+}
+
+static int cc33xx_allocate_sta(struct cc33xx *wl,
+			     struct cc33xx_vif *wlvif,
+			     struct ieee80211_sta *sta)
+{
+	struct cc33xx_station *wl_sta;
+	int ret;
+
+
+	if (wl->active_sta_count >= wl->max_ap_stations) {
+		cc33xx_warning("could not allocate HLID - too much stations");
+		return -EBUSY;
+	}
+
+	wl_sta = (struct cc33xx_station *)sta->drv_priv;
+
+	ret = cc33xx_set_link(wl, wlvif, wl_sta->hlid);
+
+	if (ret < 0) {
+		cc33xx_warning("could not allocate HLID - too many links");
+		return -EBUSY;
+	}
+
+	/* use the previous security seq, if this is a recovery/resume */
+	wl->links[wl_sta->hlid].total_freed_pkts = wl_sta->total_freed_pkts;
+
+	set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
+	memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
+	wl->active_sta_count++;
+	return 0;
+}
+
+void cc33xx_free_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 hlid)
+{
+	if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
+		return;
+
+	clear_bit(hlid, wlvif->ap.sta_hlid_map);
+	__clear_bit(hlid, &wl->ap_ps_map);
+	__clear_bit(hlid, &wl->ap_fw_ps_map);
+
+	/*
+	 * save the last used PN in the private part of iee80211_sta,
+	 * in case of recovery/suspend
+	 */
+	wlcore_save_freed_pkts_addr(wl, wlvif, hlid, wl->links[hlid].addr);
+
+	cc33xx_clear_link(wl, wlvif, &hlid);
+	wl->active_sta_count--;
+
+	/*
+	 * rearm the tx watchdog when the last STA is freed - give the FW a
+	 * chance to return STA-buffered packets before complaining.
+	 */
+	if (wl->active_sta_count == 0)
+		cc33xx_rearm_tx_watchdog_locked(wl);
+}
+
+static int cc33xx_sta_add(struct cc33xx *wl,
+			  struct cc33xx_vif *wlvif,
+			  struct ieee80211_sta *sta)
+{
+	struct cc33xx_station *wl_sta;
+	int ret = 0;
+	u8 hlid;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
+
+	wl_sta = (struct cc33xx_station *)sta->drv_priv;
+	ret = cc33xx_cmd_add_peer(wl, wlvif, sta, &hlid, 0);
+	if (ret < 0)
+		return ret;
+
+	wl_sta->hlid = hlid;
+	ret = cc33xx_allocate_sta(wl, wlvif, sta);
+	
+	return ret;
+}
+
+static int cc33xx_sta_remove(struct cc33xx *wl,
+			     struct cc33xx_vif *wlvif,
+			     struct ieee80211_sta *sta)
+{
+	struct cc33xx_station *wl_sta;
+	int ret = 0, id;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
+
+	wl_sta = (struct cc33xx_station *)sta->drv_priv;
+	id = wl_sta->hlid;
+	if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
+		return -EINVAL;
+
+	ret = cc33xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid);
+	if (ret < 0)
+		return ret;
+
+	cc33xx_free_sta(wl, wlvif, wl_sta->hlid);
+	return ret;
+}
+
+static void wlcore_roc_if_possible(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif)
+{
+	if (find_first_bit(wl->roc_map,
+			   CC33XX_MAX_ROLES) < CC33XX_MAX_ROLES)
+		return;
+
+	if (WARN_ON(wlvif->role_id == CC33XX_INVALID_ROLE_ID))
+		return;
+
+	cc33xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
+}
+
+/*
+ * when wl_sta is NULL, we treat this call as if coming from a
+ * pending auth reply.
+ * wl->mutex must be taken and the FW must be awake when the call
+ * takes place.
+ */
+void wlcore_update_inconn_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      struct cc33xx_station *wl_sta, bool in_conn)
+{
+	cc33xx_debug(DEBUG_CMD, "Enter update_inconn_sta: in_conn=%d count=%d, pending_auth=%d",
+		     in_conn, wlvif->inconn_count, wlvif->ap_pending_auth_reply);
+	if (in_conn) {
+		if (WARN_ON(wl_sta && wl_sta->in_connection))
+			return;
+
+		if (!wlvif->ap_pending_auth_reply &&
+		    !wlvif->inconn_count){
+			wlcore_roc_if_possible(wl, wlvif);
+			if (test_bit(wlvif->role_id, wl->roc_map)){
+				// set timer on croc timeout
+				wlvif->pending_auth_reply_time = jiffies;
+				cancel_delayed_work(&wlvif->roc_timeout_work);
+				cc33xx_debug(DEBUG_AP, "delay queue roc_timeout_work");
+				ieee80211_queue_delayed_work(wl->hw,
+					&wlvif->roc_timeout_work,
+					msecs_to_jiffies(CC33xx_PEND_ROC_COMPLETE_TIMEOUT));
+			}
+		    }
+
+		if (wl_sta) {
+			wl_sta->in_connection = true;
+			wlvif->inconn_count++;
+		} else {
+			wlvif->ap_pending_auth_reply = true;
+		}
+	} else {
+		if (wl_sta && !wl_sta->in_connection)
+			return;
+
+		if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply))
+			return;
+
+		if (WARN_ON(wl_sta && !wlvif->inconn_count))
+			return;
+
+		if (wl_sta) {
+			wl_sta->in_connection = false;
+			wlvif->inconn_count--;
+		} else {
+			wlvif->ap_pending_auth_reply = false;
+		}
+
+		if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply &&
+		    test_bit(wlvif->role_id, wl->roc_map)) {
+			cc33xx_croc(wl, wlvif->role_id);
+			//remove timer for croc t/o
+			cc33xx_debug(DEBUG_AP, "Cancel pending_roc timeout");
+			cancel_delayed_work(&wlvif->roc_timeout_work);
+		    }
+	}
+	cc33xx_debug(DEBUG_CMD, "Exit update_inconn_sta: in_conn=%d count=%d, pending_auth=%d",
+		     in_conn, wlvif->inconn_count, wlvif->ap_pending_auth_reply);
+}
+
+static int cc33xx_update_sta_state(struct cc33xx *wl,
+				   struct cc33xx_vif *wlvif,
+				   struct ieee80211_sta *sta,
+				   enum ieee80211_sta_state old_state,
+				   enum ieee80211_sta_state new_state)
+{
+	struct cc33xx_station *wl_sta;
+	bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
+	bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+	int ret;
+
+	wl_sta = (struct cc33xx_station *)sta->drv_priv;
+
+	/* Add station (AP mode) */
+	if (is_ap &&
+	    old_state == IEEE80211_STA_NOTEXIST &&
+	    new_state == IEEE80211_STA_NONE) {
+		ret = cc33xx_sta_add(wl, wlvif, sta);
+		if (ret)
+			return ret;
+
+		wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
+	}
+
+	/* Remove station (AP mode) */
+	if (is_ap &&
+	    old_state == IEEE80211_STA_NONE &&
+	    new_state == IEEE80211_STA_NOTEXIST) {
+		/* must not fail */
+		cc33xx_sta_remove(wl, wlvif, sta);
+
+		wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
+	}
+
+	/* Authorize station (AP mode) */
+	if (is_ap &&
+	    new_state == IEEE80211_STA_AUTHORIZED) {
+		
+		/* reconfigure peer */
+		ret = cc33xx_cmd_add_peer(wl, wlvif, sta, NULL, true);
+		if (ret < 0)
+			return ret;
+
+		wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
+	}
+
+	/* Authorize station */
+	if (is_sta &&
+	    new_state == IEEE80211_STA_AUTHORIZED) {
+		set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
+		ret = cc33xx_set_authorized(wl, wlvif);
+		if (ret)
+			return ret;
+	}
+
+	if (is_sta &&
+	    old_state == IEEE80211_STA_AUTHORIZED &&
+	    new_state == IEEE80211_STA_ASSOC) {
+		clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
+		clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
+	}
+
+	/* save seq number on disassoc (suspend) */
+	if (is_sta &&
+	    old_state == IEEE80211_STA_ASSOC &&
+	    new_state == IEEE80211_STA_AUTH) {
+		wlcore_save_freed_pkts(wl, wlvif, wlvif->sta.hlid, sta);
+		wlvif->total_freed_pkts = 0;
+	}
+
+	/* restore seq number on assoc (resume) */
+	if (is_sta &&
+	    old_state == IEEE80211_STA_AUTH &&
+	    new_state == IEEE80211_STA_ASSOC) {
+		wlvif->total_freed_pkts = wl_sta->total_freed_pkts;
+	}
+
+	/* clear ROCs on failure or authorization */
+	if (is_sta &&
+	    (new_state == IEEE80211_STA_AUTHORIZED ||
+	     new_state == IEEE80211_STA_NOTEXIST)) {
+		if (test_bit(wlvif->role_id, wl->roc_map))
+			cc33xx_croc(wl, wlvif->role_id);
+	}
+
+	if (is_sta &&
+	    old_state == IEEE80211_STA_NOTEXIST &&
+	    new_state == IEEE80211_STA_NONE) {
+		if (find_first_bit(wl->roc_map,
+				   CC33XX_MAX_ROLES) >= CC33XX_MAX_ROLES) {
+			WARN_ON(wlvif->role_id == CC33XX_INVALID_ROLE_ID);
+			cc33xx_roc(wl, wlvif, wlvif->role_id,
+				   wlvif->band, wlvif->channel);
+		}
+	}
+	return 0;
+}
+
+static int cc33xx_op_sta_state(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       struct ieee80211_sta *sta,
+			       enum ieee80211_sta_state old_state,
+			       enum ieee80211_sta_state new_state)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d",
+		     sta->aid, old_state, new_state);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = cc33xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
+
+out:
+	mutex_unlock(&wl->mutex);
+	if (new_state < old_state)
+		return 0;
+	return ret;
+}
+
+static int cc33xx_op_ampdu_action(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_ampdu_params *params)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+	u8 hlid, *ba_bitmap;
+	struct ieee80211_sta *sta = params->sta;
+	enum ieee80211_ampdu_mlme_action action = params->action;
+	u16 tid = params->tid;
+	u16 *ssn = &params->ssn;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
+		     tid);
+
+	/* sanity check - the fields in FW are only 8bits wide */
+	if (WARN_ON(tid > 0xFF))
+		return -ENOTSUPP;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
+		hlid = wlvif->sta.hlid;
+	} else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
+		struct cc33xx_station *wl_sta;
+
+		wl_sta = (struct cc33xx_station *)sta->drv_priv;
+		hlid = wl_sta->hlid;
+	} else {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ba_bitmap = &wl->links[hlid].ba_bitmap;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
+		     tid, action);
+
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+		if (!wlvif->ba_support || !wlvif->ba_allowed) {
+			ret = -ENOTSUPP;
+			break;
+		}
+
+		if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) {
+			ret = -EBUSY;
+			cc33xx_error("exceeded max RX BA sessions");
+			break;
+		}
+
+		if (*ba_bitmap & BIT(tid)) {
+			ret = -EINVAL;
+			cc33xx_error("cannot enable RX BA session on active "
+				     "tid: %d", tid);
+			break;
+		}
+
+		ret = cc33xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
+				hlid,
+				params->buf_size);
+
+		if (!ret) {
+			*ba_bitmap |= BIT(tid);
+			wl->ba_rx_session_count++;
+		}
+		break;
+
+	case IEEE80211_AMPDU_RX_STOP:
+		if (!(*ba_bitmap & BIT(tid))) {
+			/*
+			 * this happens on reconfig - so only output a debug
+			 * message for now, and don't fail the function.
+			 */
+			cc33xx_debug(DEBUG_MAC80211,
+				     "no active RX BA session on tid: %d",
+				     tid);
+			ret = 0;
+			break;
+		}
+
+		ret = cc33xx_acx_set_ba_receiver_session(wl, tid, 0, false,
+							 hlid, 0);
+		if (!ret) {
+			*ba_bitmap &= ~BIT(tid);
+			wl->ba_rx_session_count--;
+		}
+		break;
+
+	/*
+	 * The BA initiator session management in FW independently.
+	 * Falling break here on purpose for all TX APDU commands.
+	 */
+	case IEEE80211_AMPDU_TX_START:
+	case IEEE80211_AMPDU_TX_STOP_CONT:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		ret = -EINVAL;
+		break;
+
+	default:
+		cc33xx_error("Incorrect ampdu action id=%x\n", action);
+		ret = -EINVAL;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static int cc33xx_set_bitrate_mask(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif,
+				   const struct cfg80211_bitrate_mask *mask)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct cc33xx *wl = hw->priv;
+	int ret = 0;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
+		mask->control[NL80211_BAND_2GHZ].legacy,
+		mask->control[NL80211_BAND_5GHZ].legacy);
+
+	mutex_lock(&wl->mutex);
+
+#ifdef WL8_ORIGINAL_CODE
+	int i;
+	for (i = 0; i < WLCORE_NUM_BANDS; i++)
+		wlvif->bitrate_masks[i] =
+			cc33xx_tx_enabled_rates_get(wl,
+						    mask->control[i].legacy,
+						    i);
+#else
+	wlvif->bitrate_masks[0] =
+		cc33xx_tx_enabled_rates_get(wl,
+					    mask->control[0].legacy,
+					    0);
+#endif
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+
+		cc33xx_set_band_rate(wl, wlvif);
+		wlvif->basic_rate =
+			cc33xx_tx_min_rate_get(wl, wlvif->basic_rate_set);
+	}
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static void cc33xx_op_channel_switch(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif,
+				     struct ieee80211_channel_switch *ch_switch)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 channel switch");
+
+	cc33xx_tx_flush(wl);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
+		if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+			ieee80211_chswitch_done(vif, false);
+		goto out;
+	} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		goto out;
+	}
+
+	/* TODO: change mac80211 to pass vif as param */
+
+	if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+		unsigned long delay_usec;
+
+		ret = cmd_channel_switch(wl, wlvif, ch_switch);
+		if (ret)
+			goto out;
+
+		set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+
+		/* indicate failure 5 seconds after channel switch time */
+		delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
+			ch_switch->count;
+		ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
+					     usecs_to_jiffies(delay_usec) +
+					     msecs_to_jiffies(5000));
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static const void *wlcore_get_beacon_ie(struct cc33xx *wl,
+					struct cc33xx_vif *wlvif,
+					u8 eid)
+{
+	int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+	struct sk_buff *beacon =
+		ieee80211_beacon_get(wl->hw, cc33xx_wlvif_to_vif(wlvif), 0);
+
+	if (!beacon)
+		return NULL;
+
+	return cfg80211_find_ie(eid,
+				beacon->data + ieoffset,
+				beacon->len - ieoffset);
+}
+
+static int wlcore_get_csa_count(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				u8 *csa_count)
+{
+	const u8 *ie;
+	const struct ieee80211_channel_sw_ie *ie_csa;
+
+	ie = wlcore_get_beacon_ie(wl, wlvif, WLAN_EID_CHANNEL_SWITCH);
+	if (!ie)
+		return -EINVAL;
+
+	ie_csa = (struct ieee80211_channel_sw_ie *)&ie[2];
+	*csa_count = ie_csa->count;
+
+	return 0;
+}
+
+static void cc33xx_op_channel_switch_beacon(struct ieee80211_hw *hw,
+					    struct ieee80211_vif *vif,
+					    struct cfg80211_chan_def *chandef)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct ieee80211_channel_switch ch_switch = {
+		.block_tx = true,
+		.chandef = *chandef,
+	};
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211,
+		     "mac80211 channel switch beacon (role %d)",
+		     wlvif->role_id);
+
+	ret = wlcore_get_csa_count(wl, wlvif, &ch_switch.count);
+	if (ret < 0) {
+		cc33xx_error("error getting beacon (for CSA counter)");
+		return;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = cmd_channel_switch(wl, wlvif, &ch_switch);
+	if (ret)
+		goto out;
+
+	set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static void cc33xx_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+			    u32 queues, bool drop)
+{
+	struct cc33xx *wl = hw->priv;
+
+	cc33xx_tx_flush(wl);
+}
+
+static int cc33xx_op_remain_on_channel(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_channel *chan,
+				       int duration,
+				       enum ieee80211_roc_type type)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	struct cc33xx *wl = hw->priv;
+	int channel, active_roc, ret = 0;
+
+	channel = ieee80211_frequency_to_channel(chan->center_freq);
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 roc %d (role %d)",
+		     channel, wlvif->role_id);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* return EBUSY if we can't ROC right now */
+	active_roc = find_first_bit(wl->roc_map, CC33XX_MAX_ROLES);
+	if (wl->roc_vif || active_roc < CC33XX_MAX_ROLES) {
+		cc33xx_warning("active roc on role %d", active_roc);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	cc33xx_debug(DEBUG_MAC80211, "call cc33xx_start_dev, band = %d,"
+				     " channel = %d", chan->band, channel);
+	ret = cc33xx_start_dev(wl, wlvif, chan->band, channel);
+	if (ret < 0)
+		goto out;
+
+	wl->roc_vif = vif;
+	ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
+				     msecs_to_jiffies(duration));
+
+out:
+	mutex_unlock(&wl->mutex);
+
+// Temporary workaround - ROC complete event from driver,
+// need to be sent from FW
+	//ieee80211_ready_on_channel(wl->hw);
+	return ret;
+}
+
+static int __wlcore_roc_completed(struct cc33xx *wl)
+{
+	struct cc33xx_vif *wlvif;
+	int ret;
+
+	/* already completed */
+	if (unlikely(!wl->roc_vif))
+		return 0;
+
+	wlvif = cc33xx_vif_to_data(wl->roc_vif);
+
+	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+		return -EBUSY;
+
+	ret = cc33xx_stop_dev(wl, wlvif);
+	if (ret < 0)
+		return ret;
+
+	wl->roc_vif = NULL;
+
+	return 0;
+}
+
+static int wlcore_roc_completed(struct cc33xx *wl)
+{
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "roc complete");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = __wlcore_roc_completed(wl);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static void wlcore_roc_complete_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+	int ret;
+
+	dwork = to_delayed_work(work);
+	wl = container_of(dwork, struct cc33xx, roc_complete_work);
+
+	ret = wlcore_roc_completed(wl);
+	if (!ret)
+		ieee80211_remain_on_channel_expired(wl->hw);
+}
+
+static int cc33xx_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
+					      struct ieee80211_vif *vif)
+{
+	struct cc33xx *wl = hw->priv;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 croc");
+
+	/* TODO: per-vif */
+	cc33xx_tx_flush(wl);
+
+	/*
+	 * we can't just flush_work here, because it might deadlock
+	 * (as we might get called from the same workqueue)
+	 */
+	cancel_delayed_work_sync(&wl->roc_complete_work);
+	wlcore_roc_completed(wl);
+
+	return 0;
+}
+
+static void cc33xx_op_sta_rc_update(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_sta *sta,
+				    u32 changed)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 sta_rc_update");
+
+	if (!(changed & IEEE80211_RC_BW_CHANGED))
+		return;
+
+	/* this callback is atomic, so schedule a new work */
+	wlvif->rc_update_bw = sta->deflink.bandwidth;
+	memcpy(&wlvif->rc_ht_cap, &sta->deflink.ht_cap, sizeof(sta->deflink.ht_cap));
+	ieee80211_queue_work(hw, &wlvif->rc_update_work);
+}
+
+static void cc33xx_op_sta_statistics(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif,
+				     struct ieee80211_sta *sta,
+				     struct station_info *sinfo)
+{
+	struct cc33xx *wl = hw->priv;
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	s8 rssi_dbm;
+	int ret;
+
+	cc33xx_debug(DEBUG_MAC80211, "mac80211 get_rssi");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);
+	if (ret < 0)
+		goto out;
+
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
+	sinfo->signal = rssi_dbm;
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static u32 cc33xx_op_get_expected_throughput(struct ieee80211_hw *hw,
+					     struct ieee80211_sta *sta)
+{
+	struct cc33xx_station *wl_sta = (struct cc33xx_station *)sta->drv_priv;
+	struct cc33xx *wl = hw->priv;
+	u8 hlid = wl_sta->hlid;
+
+	/* return in units of Kbps */
+	return (wl->links[hlid].fw_rate_mbps * 1000);
+}
+
+static bool cc33xx_tx_frames_pending(struct ieee80211_hw *hw)
+{
+	struct cc33xx *wl = hw->priv;
+	bool ret = false;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	/* packets are considered pending if in the TX queue or the FW */
+	ret = (cc33xx_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+#undef CONFIG_PM // Not supported yet
+
+static const struct ieee80211_ops cc33xx_ops = {
+	.start = cc33xx_op_start,
+	.stop = cc33xx_op_stop,
+	.add_interface = cc33xx_op_add_interface,
+	.remove_interface = cc33xx_op_remove_interface,
+	.change_interface = cc33xx_op_change_interface,
+#ifdef CONFIG_PM
+	.suspend = cc33xx_op_suspend,
+	.resume = cc33xx_op_resume,
+#endif
+	.config = cc33xx_op_config,
+	.prepare_multicast = cc33xx_op_prepare_multicast,
+	.configure_filter = cc33xx_op_configure_filter,
+	.tx = cc33xx_op_tx,
+	.set_key = cc33xx_op_set_key,
+	.hw_scan = cc33xx_op_hw_scan,
+	.cancel_hw_scan = cc33xx_op_cancel_hw_scan,
+	.sched_scan_start = cc33xx_op_sched_scan_start,
+	.sched_scan_stop = cc33xx_op_sched_scan_stop,
+	.bss_info_changed = cc33xx_op_bss_info_changed,
+	.set_frag_threshold = cc33xx_op_set_frag_threshold,
+	.set_rts_threshold = cc33xx_op_set_rts_threshold,
+	.conf_tx = cc33xx_op_conf_tx,
+	.get_tsf = cc33xx_op_get_tsf,
+	.get_survey = cc33xx_op_get_survey,
+	.sta_state = cc33xx_op_sta_state,
+	.ampdu_action = cc33xx_op_ampdu_action,
+	.tx_frames_pending = cc33xx_tx_frames_pending,
+	.set_bitrate_mask = cc33xx_set_bitrate_mask,
+	.set_default_unicast_key = cc33xx_op_set_default_key_idx,
+	.channel_switch = cc33xx_op_channel_switch,
+	.channel_switch_beacon = cc33xx_op_channel_switch_beacon,
+	.flush = cc33xx_op_flush,
+	.remain_on_channel = cc33xx_op_remain_on_channel,
+	.cancel_remain_on_channel = cc33xx_op_cancel_remain_on_channel,
+	.add_chanctx = cc33xx_op_add_chanctx,
+	.remove_chanctx = cc33xx_op_remove_chanctx,
+	.change_chanctx = cc33xx_op_change_chanctx,
+	.assign_vif_chanctx = cc33xx_op_assign_vif_chanctx,
+	.unassign_vif_chanctx = cc33xx_op_unassign_vif_chanctx,
+	.switch_vif_chanctx = cc33xx_op_switch_vif_chanctx,
+	.sta_rc_update = cc33xx_op_sta_rc_update,
+	.sta_statistics = cc33xx_op_sta_statistics,
+	.get_expected_throughput = cc33xx_op_get_expected_throughput,
+	CFG80211_TESTMODE_CMD(cc33xx_tm_cmd)
+};
+
+
+u8 wlcore_rate_to_idx(struct cc33xx *wl, u8 rate, enum nl80211_band band)
+{
+	u8 idx;
+
+	BUG_ON(band >= 2);
+
+	if (unlikely(rate > wl->hw_tx_rate_tbl_size)) {
+		cc33xx_error("Illegal RX rate from HW: %d", rate);
+		return 0;
+	}
+
+	idx = wl->band_rate_to_idx[band][rate];
+	if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
+		cc33xx_error("Unsupported RX rate from HW: %d", rate);
+		return 0;
+	}
+
+	return idx;
+}
+
+static void cc33xx_derive_mac_addresses(struct cc33xx *wl)
+{
+	const u8 zero_mac[ETH_ALEN] = {0};
+	u8 base_addr[ETH_ALEN];
+	u8 bd_addr[ETH_ALEN];
+	bool use_nvs=false;
+	bool use_efuse=false; 
+	bool use_random=false;
+
+	if (wl->nvs_mac_addr_len != ETH_ALEN){
+		if (unlikely(wl->nvs_mac_addr_len > 0))
+			cc33xx_warning("NVS MAC address present but has a wrong size, ignoring.");
+
+		if (!ether_addr_equal(zero_mac, wl->efuse_mac_address)){
+			use_efuse = true;
+			ether_addr_copy(base_addr, wl->efuse_mac_address);
+			cc33xx_debug(DEBUG_BOOT, "MAC address derived from EFUSE");
+		} else {
+			use_random = true;
+			eth_random_addr(base_addr);
+			cc33xx_warning("No EFUSE / NVS data, "
+				"using random locally administered address."); 
+		}
+	} else {
+		u8 *nvs_addr = wl->nvs_mac_addr;
+		const u8 efuse_magic_addr[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+		const u8 random_magic_addr[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
+
+		// In NVS, addresses 00-00-00-00-00-00 and 00-00-00-00-00-01
+		// have special meaning:
+
+		if (ether_addr_equal(nvs_addr, efuse_magic_addr)){
+			use_efuse = true;
+			ether_addr_copy(base_addr, wl->efuse_mac_address);
+			cc33xx_debug(DEBUG_BOOT, "NVS file selects address from EFUSE");
+
+		} else if (ether_addr_equal(nvs_addr, random_magic_addr)){
+			use_random = true;
+			eth_random_addr(base_addr);
+			cc33xx_debug(DEBUG_BOOT, "NVS file sets random MAC address");
+
+		} else {
+			use_nvs = true;
+			ether_addr_copy(base_addr, nvs_addr);
+			cc33xx_debug(DEBUG_BOOT, "NVS file sets explicit MAC address");
+		}
+	}
+
+	if (use_nvs || use_efuse){
+		u8 oui_laa_bit = BIT(1);
+		u8 oui_multicast_bit = BIT(0);
+
+		base_addr[0] &= ~oui_multicast_bit;
+
+		ether_addr_copy(wl->addresses[0].addr, base_addr);
+		ether_addr_copy(wl->addresses[1].addr, base_addr);
+		ether_addr_copy(wl->addresses[2].addr, base_addr);
+		ether_addr_copy(bd_addr, base_addr);
+
+		wl->addresses[1].addr[0] |= oui_laa_bit;
+		wl->addresses[2].addr[0] |= oui_laa_bit;
+		
+		eth_addr_inc(wl->addresses[2].addr);
+
+		eth_addr_inc(bd_addr);
+
+	} else if (use_random) {
+		ether_addr_copy(wl->addresses[0].addr, base_addr);
+		ether_addr_copy(wl->addresses[1].addr, base_addr);
+		ether_addr_copy(wl->addresses[2].addr, base_addr);
+		ether_addr_copy(bd_addr, base_addr);
+
+		eth_addr_inc(bd_addr);
+
+		eth_addr_inc(wl->addresses[1].addr);
+		eth_addr_inc(wl->addresses[1].addr);
+
+		eth_addr_inc(wl->addresses[2].addr);
+		eth_addr_inc(wl->addresses[2].addr);
+		eth_addr_inc(wl->addresses[2].addr);
+
+	} else {
+		BUG_ON(1);
+	}
+
+	cc33xx_debug(DEBUG_BOOT, "Base MAC address: %pM", wl->addresses[0].addr);
+
+	wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES;
+	wl->hw->wiphy->addresses = wl->addresses;
+
+	cmd_set_bd_addr(wl, bd_addr);
+}
+
+
+static int cc33xx_register_hw(struct cc33xx *wl)
+{	
+	int ret;
+
+	if (wl->mac80211_registered)
+		return 0;
+
+	cc33xx_derive_mac_addresses(wl);
+
+	ret = ieee80211_register_hw(wl->hw);
+	if (ret < 0) {
+		cc33xx_error("unable to register mac80211 hw: %d", ret);
+		goto out;
+	}
+
+	wl->mac80211_registered = true;
+
+	cc33xx_debugfs_init(wl);
+
+out:
+	return ret;
+}
+
+static void cc33xx_unregister_hw(struct cc33xx *wl)
+{
+	if (wl->plt)
+		cc33xx_plt_stop(wl);
+		
+	ieee80211_unregister_hw(wl->hw);
+	wl->mac80211_registered = false;
+
+}
+
+static int cc33xx_init_ieee80211(struct cc33xx *wl)
+{
+	int i;
+
+	if (wl->conf.core.mixed_mode_support) {
+		static const u32 cipher_suites[] = {
+			WLAN_CIPHER_SUITE_CCMP,
+			WLAN_CIPHER_SUITE_AES_CMAC,
+			WLAN_CIPHER_SUITE_TKIP,
+			WLAN_CIPHER_SUITE_GCMP,
+			WLAN_CIPHER_SUITE_GCMP_256,
+			WLAN_CIPHER_SUITE_BIP_GMAC_128,
+			WLAN_CIPHER_SUITE_BIP_GMAC_256,
+		};
+		wl->hw->wiphy->cipher_suites = cipher_suites;
+		wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+	} else {
+		static const u32 cipher_suites[] = {
+			WLAN_CIPHER_SUITE_CCMP,
+			WLAN_CIPHER_SUITE_AES_CMAC,
+			WLAN_CIPHER_SUITE_GCMP,
+			WLAN_CIPHER_SUITE_GCMP_256,
+			WLAN_CIPHER_SUITE_BIP_GMAC_128,
+			WLAN_CIPHER_SUITE_BIP_GMAC_256,
+		};
+		wl->hw->wiphy->cipher_suites = cipher_suites;
+		wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+	}
+
+	/* The tx descriptor buffer */
+	wl->hw->extra_tx_headroom = CC33XX_TX_EXTRA_HEADROOM;
+
+	if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+		wl->hw->extra_tx_headroom += CC33XX_EXTRA_SPACE_TKIP;
+
+	/* unit us */
+	/* FIXME: find a proper value */
+	wl->hw->max_listen_interval = wl->conf.host_conf.conn.max_listen_interval;
+
+	ieee80211_hw_set(wl->hw, SUPPORT_FAST_XMIT);
+	ieee80211_hw_set(wl->hw, CHANCTX_STA_CSA);
+	ieee80211_hw_set(wl->hw, QUEUE_CONTROL);
+	ieee80211_hw_set(wl->hw, TX_AMPDU_SETUP_IN_HW);
+	ieee80211_hw_set(wl->hw, AMPDU_AGGREGATION);
+	ieee80211_hw_set(wl->hw, AP_LINK_PS);
+	ieee80211_hw_set(wl->hw, SPECTRUM_MGMT);
+	ieee80211_hw_set(wl->hw, REPORTS_TX_ACK_STATUS);
+	ieee80211_hw_set(wl->hw, CONNECTION_MONITOR);
+	ieee80211_hw_set(wl->hw, HAS_RATE_CONTROL);
+	ieee80211_hw_set(wl->hw, SUPPORTS_DYNAMIC_PS);
+	ieee80211_hw_set(wl->hw, SIGNAL_DBM);
+	ieee80211_hw_set(wl->hw, SUPPORTS_PS);
+	ieee80211_hw_set(wl->hw, SUPPORTS_TX_FRAG);
+	ieee80211_hw_set(wl->hw, SUPPORTS_MULTI_BSSID); 
+	ieee80211_hw_set(wl->hw, SUPPORTS_AMSDU_IN_AMPDU);
+
+	// wl->hw->wiphy->cipher_suites = cipher_suites;
+	// wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+					 BIT(NL80211_IFTYPE_AP) |
+					 BIT(NL80211_IFTYPE_P2P_DEVICE) |
+					 BIT(NL80211_IFTYPE_P2P_CLIENT) |
+#ifdef CONFIG_MAC80211_MESH
+					 BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+					 BIT(NL80211_IFTYPE_P2P_GO);
+
+	wl->hw->wiphy->max_scan_ssids = 1;
+	wl->hw->wiphy->max_sched_scan_ssids = 16;
+	wl->hw->wiphy->max_match_sets = 16;
+	/*
+	 * Maximum length of elements in scanning probe request templates
+	 * should be the maximum length possible for a template, without
+	 * the IEEE80211 header of the template
+	 */
+	wl->hw->wiphy->max_scan_ie_len = CC33XX_CMD_TEMPL_MAX_SIZE -
+			sizeof(struct ieee80211_header);
+
+	wl->hw->wiphy->max_sched_scan_reqs = 1;
+	wl->hw->wiphy->max_sched_scan_ie_len = CC33XX_CMD_TEMPL_MAX_SIZE -
+		sizeof(struct ieee80211_header);
+
+	wl->hw->wiphy->max_remain_on_channel_duration = 30000;
+
+	wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN;
+
+	/*
+	* clear channel flags from the previous usage
+	* and restore max_power & max_antenna_gain values.
+	*/
+	for (i = 0; i < ARRAY_SIZE(cc33xx_channels); i++) {
+		cc33xx_band_2ghz.channels[i].flags = 0;
+		cc33xx_band_2ghz.channels[i].max_power = CC33XX_MAX_TXPWR;
+		cc33xx_band_2ghz.channels[i].max_antenna_gain = 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cc33xx_channels_5ghz); i++) {
+		cc33xx_band_5ghz.channels[i].flags = 0;
+		cc33xx_band_5ghz.channels[i].max_power = CC33XX_MAX_TXPWR;
+		cc33xx_band_5ghz.channels[i].max_antenna_gain = 0;
+	}
+
+	/* Enable/Disable He based on conf file params */
+	if(!wl->conf.mac.he_enable)
+	{
+		cc33xx_band_2ghz.iftype_data = NULL;
+		cc33xx_band_2ghz.n_iftype_data = 0;
+
+		cc33xx_band_5ghz.iftype_data = NULL;
+		cc33xx_band_5ghz.n_iftype_data = 0;
+	}
+
+
+	/*
+	 * We keep local copies of the band structs because we need to
+	 * modify them on a per-device basis.
+	 */
+	memcpy(&wl->bands[NL80211_BAND_2GHZ], &cc33xx_band_2ghz,
+	       sizeof(cc33xx_band_2ghz));
+	memcpy(&wl->bands[NL80211_BAND_2GHZ].ht_cap,
+	       &wl->ht_cap[NL80211_BAND_2GHZ],
+	       sizeof(*wl->ht_cap));
+
+	memcpy(&wl->bands[NL80211_BAND_5GHZ], &cc33xx_band_5ghz,
+	       sizeof(cc33xx_band_5ghz));
+	memcpy(&wl->bands[NL80211_BAND_5GHZ].ht_cap,
+	       &wl->ht_cap[NL80211_BAND_5GHZ],
+	       sizeof(*wl->ht_cap));
+
+	wl->hw->wiphy->bands[NL80211_BAND_2GHZ] =
+		&wl->bands[NL80211_BAND_2GHZ];
+
+	if(!wl->disable_5g && (wl->conf.core.enable_5ghz))
+		wl->hw->wiphy->bands[NL80211_BAND_5GHZ] =
+			&wl->bands[NL80211_BAND_5GHZ];
+
+	/*
+	 * allow 4 queues per mac address we support +
+	 * 1 cab queue per mac + one global offchannel Tx queue
+	 */
+	wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1;
+
+	/* the last queue is the offchannel queue */
+	wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1;
+	wl->hw->max_rates = 1;
+
+	wl->hw->wiphy->reg_notifier = cc33xx_reg_notify;
+
+	/* allowed interface combinations */
+	wl->hw->wiphy->iface_combinations = wl->iface_combinations;
+	wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
+
+	SET_IEEE80211_DEV(wl->hw, wl->dev);
+
+	wl->hw->sta_data_size = sizeof(struct cc33xx_station);
+	wl->hw->vif_data_size = sizeof(struct cc33xx_vif);
+
+	wl->hw->max_rx_aggregation_subframes = wl->conf.host_conf.ht.rx_ba_win_size;
+
+	/* For all ps schemes don't use UAPSD, except for UAPSD scheme 
+	   As these are the currently supportedd PS schemes, use the default
+	   legacy otherwise */ 
+	if (wl->conf.mac.ps_scheme == PS_SCHEME_UPSD_TRIGGER) {
+		wl->hw->uapsd_queues = IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK;
+	} else if ((wl->conf.mac.ps_scheme != PS_SCHEME_LEGACY) &&
+		   (wl->conf.mac.ps_scheme != PS_SCHEME_NOPSPOLL)) {
+		wl->hw->uapsd_queues = 0;
+		wl->conf.mac.ps_scheme = PS_SCHEME_LEGACY;
+	} else {
+		wl->hw->uapsd_queues = 0;
+	}
+		
+
+	return 0;
+}
+
+#define create_high_prio_freezable_workqueue(name)				\
+	alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND |	\
+			WQ_MEM_RECLAIM | WQ_HIGHPRI, 1, (name))
+
+struct ieee80211_hw *wlcore_alloc_hw(u32 aggr_buf_size)
+{
+	struct ieee80211_hw *hw;
+	struct cc33xx *wl;
+	int i, j, ret;
+	unsigned int order;
+
+	hw = ieee80211_alloc_hw(sizeof(*wl), &cc33xx_ops);
+	if (!hw) {
+		cc33xx_error("could not alloc ieee80211_hw");
+		ret = -ENOMEM;
+		goto err_hw_alloc;
+	}
+
+	wl = hw->priv;
+	memset(wl, 0, sizeof(*wl));
+
+	INIT_LIST_HEAD(&wl->wlvif_list);
+
+	wl->hw = hw;
+
+	/*
+	 * wl->num_links is not configured yet, so just use CC33XX_MAX_LINKS.
+	 * we don't allocate any additional resource here, so that's fine.
+	 */
+	for (i = 0; i < NUM_TX_QUEUES; i++)
+		for (j = 0; j < CC33XX_MAX_LINKS; j++)
+			skb_queue_head_init(&wl->links[j].tx_queue[i]);
+
+	skb_queue_head_init(&wl->deferred_rx_queue);
+	skb_queue_head_init(&wl->deferred_tx_queue);
+
+	init_llist_head(&wl->event_list);
+
+	INIT_WORK(&wl->netstack_work, cc33xx_netstack_work);
+	INIT_WORK(&wl->tx_work, cc33xx_tx_work);
+	INIT_WORK(&wl->recovery_work, cc33xx_recovery_work);
+	INIT_WORK(&wl->irq_deferred_work, irq_deferred_work);
+	INIT_DELAYED_WORK(&wl->scan_complete_work, cc33xx_scan_complete_work);
+	INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
+	INIT_DELAYED_WORK(&wl->tx_watchdog_work, cc33xx_tx_watchdog_work);
+
+	wl->freezable_netstack_wq  = create_freezable_workqueue("cc33xx_netstack_wq");
+
+	wl->freezable_wq =  create_high_prio_freezable_workqueue("cc33xx_wq");
+													
+	if (!wl->freezable_wq || !wl->freezable_netstack_wq) {
+		ret = -ENOMEM;
+		goto err_hw;
+	}
+
+	wl->rx_counter = 0;
+	wl->power_level = CC33XX_MAX_TXPWR;
+	wl->band = NL80211_BAND_2GHZ;
+	wl->flags = 0;
+	wl->sleep_auth = CC33XX_PSM_ILLEGAL;
+	
+	wl->ap_ps_map = 0;
+	wl->ap_fw_ps_map = 0;
+	wl->quirks = 0;
+	wl->active_sta_count = 0;
+	wl->active_link_count = 0;
+	wl->fwlog_size = 0;
+
+	/* The system link is always allocated */
+	__set_bit(CC33XX_SYSTEM_HLID, wl->links_map);
+
+	memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
+	for (i = 0; i < wl->num_tx_desc; i++)
+		wl->tx_frames[i] = NULL;
+
+	spin_lock_init(&wl->wl_lock);
+
+	wl->state = WLCORE_STATE_OFF;
+	mutex_init(&wl->mutex);
+	mutex_init(&wl->flush_mutex);
+	init_completion(&wl->nvs_loading_complete);
+
+	order = get_order(aggr_buf_size);
+	wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
+	if (!wl->aggr_buf) {
+		ret = -ENOMEM;
+		goto err_wq;
+	}
+	wl->aggr_buf_size = aggr_buf_size;
+
+	wl->dummy_packet = cc33xx_alloc_dummy_packet(wl);
+	if (!wl->dummy_packet) {
+		ret = -ENOMEM;
+		goto err_aggr;
+	}
+
+	/* Allocate one page for the FW log */
+	wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
+	if (!wl->fwlog) {
+		ret = -ENOMEM;
+		goto err_dummy_packet;
+	}
+
+	wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL);
+	if (!wl->buffer_32) {
+		ret = -ENOMEM;
+		goto err_fwlog;
+	}
+
+	wl->core_status = kzalloc(sizeof(*wl->core_status), GFP_KERNEL);
+	if (!wl->core_status)
+		goto err_buf32;
+
+
+
+	return hw;
+
+err_buf32:
+	kfree(wl->buffer_32);
+
+err_fwlog:
+	free_page((unsigned long)wl->fwlog);
+
+err_dummy_packet:
+	dev_kfree_skb(wl->dummy_packet);
+
+err_aggr:
+	free_pages((unsigned long)wl->aggr_buf, order);
+
+err_wq:
+	destroy_workqueue(wl->freezable_wq);
+	destroy_workqueue(wl->freezable_netstack_wq);
+
+err_hw:
+	cc33xx_debugfs_exit(wl);
+
+err_hw_alloc:
+
+	return ERR_PTR(ret);
+}
+
+int wlcore_free_hw(struct cc33xx *wl)
+{
+	/* Unblock any fwlog readers */
+	mutex_lock(&wl->mutex);
+	wl->fwlog_size = -1;
+	mutex_unlock(&wl->mutex);
+
+	wlcore_sysfs_free(wl);
+
+	kfree(wl->buffer_32);
+	kfree(wl->core_status);
+	free_page((unsigned long)wl->fwlog);
+	dev_kfree_skb(wl->dummy_packet);
+	free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
+
+	cc33xx_debugfs_exit(wl);
+
+	kfree(wl->nvs_mac_addr);
+	wl->nvs_mac_addr = NULL;
+
+	destroy_workqueue(wl->freezable_wq);
+	destroy_workqueue(wl->freezable_netstack_wq);
+	flush_deferred_event_list(wl);
+
+	ieee80211_free_hw(wl->hw);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support wlcore_wowlan_support = {
+	.flags = WIPHY_WOWLAN_ANY,
+	.n_patterns = CC33XX_MAX_RX_FILTERS,
+	.pattern_min_len = 1,
+	.pattern_max_len = CC33XX_RX_FILTER_MAX_PATTERN_SIZE,
+};
+#endif
+
+
+
+static int cc33xx_identify_chip(struct cc33xx *wl)
+{
+	int ret = 0;
+
+	wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+		      WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
+		      WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
+		      WLCORE_QUIRK_TX_PAD_LAST_FRAME |
+		      WLCORE_QUIRK_REGDOMAIN_CONF |
+		      WLCORE_QUIRK_DUAL_PROBE_TMPL;
+
+
+	wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+	wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+	wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
+	wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
+	wl->max_channels_5 = MAX_CHANNELS_5GHZ;
+	wl->ba_rx_session_count_max = CC33XX_RX_BA_MAX_SESSIONS;
+
+	if (wl->if_ops->get_max_transaction_len)
+		wl->max_transaction_len = 
+			wl->if_ops->get_max_transaction_len(wl->dev);
+	else
+		wl->max_transaction_len = 0;
+
+	return ret;
+}
+
+static int read_version_info(struct cc33xx *wl)
+{
+	int ret;
+
+	cc33xx_info("Wireless driver version %u.%u.%u.%u",
+		MAJOR_VERSION,
+		MINOR_VERSION,
+		API_VERSION,
+		BUILD_VERSION);
+
+	ret = cc33xx_acx_init_get_fw_versions(wl);
+	if(ret < 0){
+		cc33xx_error("Get FW version FAILED!");
+		return ret;
+	}
+
+	cc33xx_info("Wireless firmware version %u.%u.%u.%u",
+		wl->all_versions.fw_ver->major_version, 
+		wl->all_versions.fw_ver->minor_version, 
+		wl->all_versions.fw_ver->api_version, 
+		wl->all_versions.fw_ver->build_version);
+
+	cc33xx_info("Wireless PHY version %u.%u.%u.%u.%u.%u", 
+		wl->all_versions.fw_ver->phy_version[5], 
+		wl->all_versions.fw_ver->phy_version[4],
+		wl->all_versions.fw_ver->phy_version[3], 
+		wl->all_versions.fw_ver->phy_version[2], 
+		wl->all_versions.fw_ver->phy_version[1], 
+		wl->all_versions.fw_ver->phy_version[0]);
+
+	wl->all_versions.driver_ver.major_version = MAJOR_VERSION;
+	wl->all_versions.driver_ver.minor_version = MINOR_VERSION;
+	wl->all_versions.driver_ver.api_version = API_VERSION;
+	wl->all_versions.driver_ver.build_version = BUILD_VERSION;
+
+	return 0;
+}
+
+static void wlcore_nvs_cb(const struct firmware *fw, void *context)
+{
+	struct cc33xx *wl = context;
+	struct platform_device *pdev = wl->pdev;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+#ifdef CONFIG_PM
+	struct resource *res;
+#endif
+
+	int ret;
+
+	if (fw) {
+		wl->nvs_mac_addr = kmemdup(fw->data, fw->size, GFP_KERNEL);
+		if (!wl->nvs_mac_addr) {
+			cc33xx_error("Could not allocate nvs data");
+			goto out;
+		}
+		wl->nvs_mac_addr_len = fw->size;
+	} else if (pdev_data->family->nvs_name) {
+		cc33xx_debug(DEBUG_BOOT, "Could not get nvs file %s",
+			     pdev_data->family->nvs_name);
+		wl->nvs_mac_addr = NULL;
+		wl->nvs_mac_addr_len = 0;
+	} else {
+		wl->nvs_mac_addr = NULL;
+		wl->nvs_mac_addr_len = 0;
+	}
+	
+	ret = cc33xx_setup(wl);
+	if (ret < 0)
+		goto out_free_nvs;
+
+	BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS);
+
+	/* adjust some runtime configuration parameters */
+	wlcore_adjust_conf(wl);
+
+	wl->if_ops = pdev_data->if_ops;
+	wl->if_ops->set_irq_handler(wl->dev, irq_wrapper);
+
+	cc33xx_power_off(wl);
+
+#ifdef CONFIG_PM
+	device_init_wakeup(wl->dev, true);
+
+	if (pdev_data->pwr_in_suspend)
+		wl->hw->wiphy->wowlan = &wlcore_wowlan_support;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res) {
+		wl->wakeirq = res->start;
+		ret = dev_pm_set_dedicated_wake_irq(wl->dev, wl->wakeirq);
+		if (ret)
+			wl->wakeirq = -ENODEV;
+	} else {
+		wl->wakeirq = -ENODEV;
+	}
+#else
+	wl->keep_device_power = true;
+#endif
+
+
+	ret = cc33xx_init_fw(wl);
+	if (ret < 0) {
+		cc33xx_error("FW download failed");
+		cc33xx_power_off(wl);
+		goto out_irq;
+	}
+
+	ret = cc33xx_identify_chip(wl);
+	if (ret < 0)
+		goto out_irq;
+
+	ret = read_version_info(wl);
+	if (ret < 0)
+		goto out_irq;
+
+	ret = cc33xx_init_ieee80211(wl);
+	if (ret)
+		goto out_irq;
+
+	ret = cc33xx_register_hw(wl);
+	if (ret)
+		goto out_irq;
+
+	ret = wlcore_sysfs_init(wl);
+	if (ret)
+		goto out_unreg;
+
+	wl->initialized = true;
+	cc33xx_notice("loaded");
+	goto out;
+
+out_unreg:
+	cc33xx_unregister_hw(wl);
+
+out_irq:
+	if (wl->wakeirq >= 0)
+		dev_pm_clear_wake_irq(wl->dev);
+	device_init_wakeup(wl->dev, false);
+
+out_free_nvs:
+	kfree(wl->nvs_mac_addr);
+
+out:
+	release_firmware(fw);
+	complete_all(&wl->nvs_loading_complete);
+	cc33xx_debug(DEBUG_CC33xx, "wlcore_nvs_cb Complete");	
+}
+
+int wlcore_probe(struct cc33xx *wl, struct platform_device *pdev)
+{
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+	const char *nvs_name;
+	int ret = 0;
+	cc33xx_debug(DEBUG_CC33xx, "Wireless Driver Version %d.%d.%d.%d",
+		MAJOR_VERSION, MINOR_VERSION, API_VERSION, BUILD_VERSION);
+
+
+	if (!pdev_data)
+		return -EINVAL;
+
+	wl->dev = &pdev->dev;
+	wl->pdev = pdev;
+	platform_set_drvdata(pdev, wl);
+
+	if (pdev_data->family && pdev_data->family->nvs_name) {
+		nvs_name = pdev_data->family->nvs_name;
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+					      nvs_name, &pdev->dev, GFP_KERNEL,
+					      wl, wlcore_nvs_cb);
+		if (ret < 0) {
+			cc33xx_error("request_firmware_nowait failed for %s: %d",
+				     nvs_name, ret);
+			complete_all(&wl->nvs_loading_complete);
+		}
+	} else {
+		wlcore_nvs_cb(NULL, wl);
+	}
+
+	return ret;
+}
+
+int wlcore_remove(struct platform_device *pdev)
+{
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+	struct cc33xx *wl = platform_get_drvdata(pdev);
+
+	set_bit(CC33XX_FLAG_DRIVER_REMOVED, &wl->flags);
+
+	wl->dev->driver->pm = NULL;
+
+	if (pdev_data->family && pdev_data->family->nvs_name)
+		wait_for_completion(&wl->nvs_loading_complete);
+		
+	if (!wl->initialized)
+		goto out;
+
+	if (wl->wakeirq >= 0) {
+		dev_pm_clear_wake_irq(wl->dev);
+		wl->wakeirq = -ENODEV;
+	}
+
+	device_init_wakeup(wl->dev, false);
+
+	cc33xx_unregister_hw(wl);
+	cc33xx_turn_off(wl);
+
+out:
+	wlcore_free_hw(wl);
+
+	return 0;
+}
+
+bool cc33xx_is_mimo_supported(struct cc33xx *wl)
+{
+	/* only support MIMO with multiple antennas, and when SISO
+	 * is not forced through config
+	 */
+	return (wl->conf.host_conf.ht.mode != HT_MODE_WIDE) &&
+	       (wl->conf.host_conf.ht.mode != HT_MODE_SISO20);
+}
+
+static int cc33xx_load_ini_bin_file(struct device *dev, struct cc33xx_conf_file *conf,
+				 const char *file)
+{
+	struct cc33xx_conf_file *conf_file;
+	const struct firmware *fw;
+	int ret;
+
+	ret = request_firmware(&fw, file, dev);
+	if (ret < 0) {
+		cc33xx_error("could not get configuration binary %s: %d",
+			     file, ret);
+		return ret;
+	}
+
+	if (fw->size != CC33X_CONF_SIZE) {
+		cc33xx_error("%s configuration binary size is wrong, expected %zu got %zu",
+			     file, CC33X_CONF_SIZE, fw->size);
+		ret = -EINVAL;
+		goto out_release;
+	}
+
+	conf_file = (struct cc33xx_conf_file *) fw->data;
+
+	if (conf_file->header.magic != cpu_to_le32(CC33XX_CONF_MAGIC)) {
+		cc33xx_error("configuration binary file magic number mismatch, "
+			     "expected 0x%0x got 0x%0x", CC33XX_CONF_MAGIC,
+			     conf_file->header.magic);
+		ret = -EINVAL;
+		goto out_release;
+	}
+
+	memcpy(conf, conf_file, sizeof(*conf));
+
+out_release:
+	release_firmware(fw);
+	return ret;
+}
+
+static int cc33xx_ini_bin_init(struct cc33xx *wl, struct device *dev)
+{
+	struct platform_device *pdev = wl->pdev;
+	struct wlcore_platdev_data *pdata = dev_get_platdata(&pdev->dev);
+
+	if (cc33xx_load_ini_bin_file(dev, &wl->conf, pdata->family->cfg_name) < 0){
+		cc33xx_warning("falling back to default config");
+
+	
+	}
+	
+	return 0;
+}
+
+
+static int cc33xx_setup(struct cc33xx *wl)
+{
+	int ret;
+
+	BUILD_BUG_ON(CC33XX_MAX_AP_STATIONS > CC33XX_MAX_LINKS);
+
+	wl->num_tx_desc = CC33XX_NUM_TX_DESCRIPTORS;
+	wl->num_rx_desc = CC33XX_NUM_RX_DESCRIPTORS;
+	wl->num_links = CC33XX_MAX_LINKS;
+	wl->max_ap_stations = CC33XX_MAX_AP_STATIONS;
+	wl->iface_combinations = cc33xx_iface_combinations;
+	wl->n_iface_combinations = ARRAY_SIZE(cc33xx_iface_combinations);
+	wl->band_rate_to_idx = cc33xx_band_rate_to_idx;
+	wl->hw_tx_rate_tbl_size = CONF_HW_RATE_INDEX_MAX;
+	wl->stats.fw_stats_len = sizeof(struct cc33xx_acx_statistics);
+
+	if (num_rx_desc_param != -1)
+		wl->num_rx_desc = num_rx_desc_param;
+
+	ret = cc33xx_ini_bin_init(wl, wl->dev);
+	if (ret < 0)
+		return ret;
+
+
+	if (ht_mode_param) {
+		if (!strcmp(ht_mode_param, "default"))
+			wl->conf.host_conf.ht.mode = HT_MODE_DEFAULT;
+		else if (!strcmp(ht_mode_param, "wide"))
+			wl->conf.host_conf.ht.mode = HT_MODE_WIDE;
+		else if (!strcmp(ht_mode_param, "siso20"))
+			wl->conf.host_conf.ht.mode = HT_MODE_SISO20;
+		else {
+			cc33xx_error("invalid ht_mode '%s'", ht_mode_param);
+			return -EINVAL;
+		}
+	}
+
+	if (wl->conf.host_conf.ht.mode == HT_MODE_DEFAULT) {
+		/*
+		 * Only support mimo with multiple antennas. Fall back to
+		 * siso40.
+		 */
+		if (cc33xx_is_mimo_supported(wl))
+			wlcore_set_ht_cap(wl, NL80211_BAND_2GHZ,
+					  &cc33xx_mimo_ht_cap_2ghz);
+		else
+			wlcore_set_ht_cap(wl, NL80211_BAND_2GHZ,
+					  &cc33xx_siso40_ht_cap_2ghz);
+
+		/* 5Ghz is always wide */
+		wlcore_set_ht_cap(wl, NL80211_BAND_5GHZ,
+				  &cc33xx_siso40_ht_cap_5ghz);
+	} else if (wl->conf.host_conf.ht.mode == HT_MODE_WIDE) {
+		wlcore_set_ht_cap(wl, NL80211_BAND_2GHZ,
+				  &cc33xx_siso40_ht_cap_2ghz);
+		wlcore_set_ht_cap(wl, NL80211_BAND_5GHZ,
+				  &cc33xx_siso40_ht_cap_5ghz);
+	} else if (wl->conf.host_conf.ht.mode == HT_MODE_SISO20) {
+		wlcore_set_ht_cap(wl, NL80211_BAND_2GHZ,
+				  &cc33xx_siso20_ht_cap);
+		wlcore_set_ht_cap(wl, NL80211_BAND_5GHZ,
+				  &cc33xx_siso20_ht_cap);
+	}
+
+	wl->event_mask = BSS_LOSS_EVENT_ID |
+		SCAN_COMPLETE_EVENT_ID |
+		RADAR_DETECTED_EVENT_ID |
+		RSSI_SNR_TRIGGER_0_EVENT_ID |
+		PERIODIC_SCAN_COMPLETE_EVENT_ID |
+		PERIODIC_SCAN_REPORT_EVENT_ID |
+		DUMMY_PACKET_EVENT_ID |
+		PEER_REMOVE_COMPLETE_EVENT_ID |
+		BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+		REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+		INACTIVE_STA_EVENT_ID |
+		CHANNEL_SWITCH_COMPLETE_EVENT_ID |
+		DFS_CHANNELS_CONFIG_COMPLETE_EVENT |
+		SMART_CONFIG_SYNC_EVENT_ID |
+		SMART_CONFIG_DECODE_EVENT_ID |
+		TIME_SYNC_EVENT_ID |
+		FW_LOGGER_INDICATION |
+		RX_BA_WIN_SIZE_CHANGE_EVENT_ID;
+
+	wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
+
+	return 0;
+}
+
+static int cc33xx_probe(struct platform_device *pdev)
+{
+	struct cc33xx *wl;
+	struct ieee80211_hw *hw;
+	int ret;
+
+	cc33xx_debug(DEBUG_CC33xx, "cc33xx_probe :: Start");
+	
+	hw = wlcore_alloc_hw(CC33XX_AGGR_BUFFER_SIZE);
+	if (IS_ERR(hw)) {
+		cc33xx_error("can't allocate hw");
+		ret = PTR_ERR(hw);
+		goto out;
+	}
+
+	wl = hw->priv;
+	ret = wlcore_probe(wl, pdev);
+	if (ret)
+		goto out_free;
+		
+	cc33xx_debug(DEBUG_CC33xx, "WLAN CC33xx platform device probe done");
+	return ret;
+
+out_free:
+	wlcore_free_hw(wl);
+out:
+	return ret;
+}
+
+static const struct platform_device_id cc33xx_id_table[] = {
+	{ "cc33xx", 0 },
+	{  } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(platform, cc33xx_id_table);
+
+static struct platform_driver cc33xx_driver = {
+	.probe		= cc33xx_probe,
+	.remove		= wlcore_remove,
+	.id_table	= cc33xx_id_table,
+	.driver = {
+		.name	= "cc33xx_driver",
+	}
+};
+
+u32 cc33xx_debug_level = DEBUG_NO_DATAPATH;
+
+module_platform_driver(cc33xx_driver);
+
+module_param_named(debug_level, cc33xx_debug_level, uint, 0600);
+MODULE_PARM_DESC(debug_level, "cc33xx debugging level");
+
+MODULE_PARM_DESC(secure_boot_enable, "Enables secure boot and FW downlaod");
+
+module_param_named(fwlog, fwlog_param, charp, 0);
+MODULE_PARM_DESC(fwlog, "FW logger options: continuous, dbgpins or disable");
+
+module_param(fwlog_mem_blocks, int, 0600);
+MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
+
+module_param(no_recovery, int, 0600);
+MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
+
+module_param_named(ht_mode, ht_mode_param, charp, 0400);
+MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or siso20");
+
+module_param_named(pwr_limit_reference_11_abg,
+		   pwr_limit_reference_11_abg_param, int, 0400);
+MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 "
+		 "(default is 0xc8)");
+
+module_param_named(num_rx_desc, num_rx_desc_param, int, 0400);
+MODULE_PARM_DESC(num_rx_desc_param,
+		 "Number of Rx descriptors: u8 (default is 32)");
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
+MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
+MODULE_FIRMWARE(SECOND_LOADER_NAME);
+MODULE_FIRMWARE(FW_NAME);
diff --git drivers/net/wireless/ti/cc33xx/ps.c drivers/net/wireless/ti/cc33xx/ps.c
new file mode 100644
index 000000000000..f61ee6817117
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/ps.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include "ps.h"
+#include "io.h"
+#include "tx.h"
+#include "debug.h"
+
+int cc33xx_ps_set_mode(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       enum cc33xx_cmd_ps_mode mode)
+{
+	int ret;
+	u16 timeout = wl->conf.host_conf.conn.dynamic_ps_timeout;
+
+	switch (mode) {
+	case STATION_AUTO_PS_MODE:
+	case STATION_POWER_SAVE_MODE:
+		cc33xx_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)",
+			     mode, timeout);
+
+		ret = cc33xx_cmd_ps_mode(wl, wlvif, mode, timeout);
+		if (ret < 0)
+			return ret;
+
+		set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
+
+		/*
+		 * enable beacon early termination.
+		 * Not relevant for 5GHz and for high rates.
+		 */
+		if ((wlvif->band == NL80211_BAND_2GHZ) &&
+		    (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
+			ret = cc33xx_acx_bet_enable(wl, wlvif, true);
+			if (ret < 0)
+				return ret;
+		}
+		break;
+	case STATION_ACTIVE_MODE:
+		cc33xx_debug(DEBUG_PSM, "leaving psm");
+
+		/* disable beacon early termination */
+		if ((wlvif->band == NL80211_BAND_2GHZ) &&
+		    (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
+			ret = cc33xx_acx_bet_enable(wl, wlvif, false);
+			if (ret < 0)
+				return ret;
+		}
+
+		ret = cc33xx_cmd_ps_mode(wl, wlvif, mode, 0);
+		if (ret < 0)
+			return ret;
+
+		clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
+		break;
+	default:
+		cc33xx_warning("trying to set ps to unsupported mode %d", mode);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void cc33xx_ps_filter_frames(struct cc33xx *wl, u8 hlid)
+{
+	int i;
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	unsigned long flags;
+	int filtered[NUM_TX_QUEUES];
+	struct cc33xx_link *lnk = &wl->links[hlid];
+
+	/* filter all frames currently in the low level queues for this hlid */
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		filtered[i] = 0;
+		while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
+			filtered[i]++;
+
+			if (WARN_ON(cc33xx_is_dummy_packet(wl, skb)))
+				continue;
+
+			info = IEEE80211_SKB_CB(skb);
+			info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+			info->status.rates[0].idx = -1;
+			ieee80211_tx_status_ni(wl->hw, skb);
+		}
+	}
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		wl->tx_queue_count[i] -= filtered[i];
+		if (lnk->wlvif)
+			lnk->wlvif->tx_queue_count[i] -= filtered[i];
+	}
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	cc33xx_handle_tx_low_watermark(wl);
+}
+
+void cc33xx_ps_link_start(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  u8 hlid, bool clean_queues)
+{
+	struct ieee80211_sta *sta;
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+	if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+		return;
+
+	if (!test_bit(hlid, wlvif->ap.sta_hlid_map) ||
+	    test_bit(hlid, &wl->ap_ps_map))
+		return;
+
+	cc33xx_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d "
+		     "clean_queues %d", hlid, wl->links[hlid].allocated_pkts,
+		     clean_queues);
+
+	rcu_read_lock();
+	sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
+	if (!sta) {
+		cc33xx_error("could not find sta %pM for starting ps",
+			     wl->links[hlid].addr);
+		rcu_read_unlock();
+		return;
+	}
+
+	ieee80211_sta_ps_transition_ni(sta, true);
+	rcu_read_unlock();
+
+	/* do we want to filter all frames from this link's queues? */
+	if (clean_queues)
+		cc33xx_ps_filter_frames(wl, hlid);
+
+	__set_bit(hlid, &wl->ap_ps_map);
+}
+
+void cc33xx_ps_link_end(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 hlid)
+{
+	struct ieee80211_sta *sta;
+	struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+
+	if (!test_bit(hlid, &wl->ap_ps_map))
+		return;
+
+	cc33xx_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid);
+
+	__clear_bit(hlid, &wl->ap_ps_map);
+
+	rcu_read_lock();
+	sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
+	if (!sta) {
+		cc33xx_error("could not find sta %pM for ending ps",
+			     wl->links[hlid].addr);
+		goto end;
+	}
+
+	ieee80211_sta_ps_transition_ni(sta, false);
+end:
+	rcu_read_unlock();
+}
diff --git drivers/net/wireless/ti/cc33xx/ps.h drivers/net/wireless/ti/cc33xx/ps.h
new file mode 100644
index 000000000000..85651e562bd5
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/ps.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __PS_H__
+#define __PS_H__
+
+#include "wlcore.h"
+#include "acx.h"
+
+int cc33xx_ps_set_mode(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		       enum cc33xx_cmd_ps_mode mode);
+void cc33xx_ps_link_start(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			  u8 hlid, bool clean_queues);
+void cc33xx_ps_link_end(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 hlid);
+
+#endif /* __WL1271_PS_H__ */
diff --git drivers/net/wireless/ti/cc33xx/rx.c drivers/net/wireless/ti/cc33xx/rx.c
new file mode 100644
index 000000000000..67a08ee736e3
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/rx.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/gfp.h>
+#include <linux/sched.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "acx.h"
+#include "rx.h"
+#include "tx.h"
+#include "io.h"
+
+
+
+
+
+/* Construct the rx status structure for upper layers */
+static void cc33xx_rx_status(struct cc33xx *wl,
+			     struct cc33xx_rx_descriptor *desc,
+			     struct ieee80211_rx_status *status,
+			     u8 beacon, u8 probe_rsp)
+{
+	memset(status, 0, sizeof(struct ieee80211_rx_status));
+
+	if ((desc->flags & CC33XX_RX_DESC_BAND_MASK) == CC33XX_RX_DESC_BAND_BG)
+		status->band = NL80211_BAND_2GHZ;
+	else if ((desc->flags & CC33XX_RX_DESC_BAND_MASK) == CC33XX_RX_DESC_BAND_J)		
+		status->band = NL80211_BAND_2GHZ;
+	else if ((desc->flags & CC33XX_RX_DESC_BAND_MASK) == CC33XX_RX_DESC_BAND_A)
+		status->band = NL80211_BAND_5GHZ;	
+	else
+		status->band = NL80211_BAND_5GHZ; /* todo -Should be 6GHZ when added */
+
+	
+	status->rate_idx = wlcore_rate_to_idx(wl, desc->rate, status->band);
+
+
+	if (desc->frame_format == CC33xx_VHT)
+		status->encoding = RX_ENC_VHT;
+	else if ((desc->frame_format == CC33xx_HT_MF) ||
+		(desc->frame_format == CC33xx_HT_GF))
+		status->encoding = RX_ENC_HT;
+	else if ((desc->frame_format == CC33xx_B_SHORT) ||
+		(desc->frame_format == CC33xx_B_LONG) ||
+		(desc->frame_format == CC33xx_LEGACY_OFDM))
+		status->encoding = RX_ENC_LEGACY;
+	else
+		status->encoding = RX_ENC_HE;
+
+	/*
+	* Read the signal level and antenna diversity indication.
+	* The msb in the signal level is always set as it is a
+	* negative number.
+	* The antenna indication is the msb of the rssi.
+	*/
+	status->signal = ((desc->rssi & RSSI_LEVEL_BITMASK) | BIT(7));
+	status->antenna = ((desc->rssi & ANT_DIVERSITY_BITMASK) >> 7);
+
+	/*
+	 * FIXME: In wl1251, the SNR should be divided by two.  In cc33xx we
+	 * need to divide by two for now, but TI has been discussing about
+	 * changing it.  This needs to be rechecked.
+	 */
+	 /*  is snr available? used?*/
+	/*wl->noise = desc->rssi - (desc->snr >> 1);*/
+
+	status->freq = ieee80211_channel_to_frequency(desc->channel,
+						      status->band);
+
+	if (desc->flags & CC33XX_RX_DESC_ENCRYPT_MASK) {
+		u8 desc_err_code = desc->status & CC33XX_RX_DESC_STATUS_MASK;
+
+		/* Frame is sent to driver with the IV (for PN replay check)
+		 * but without the MIC */
+		status->flag |=  RX_FLAG_MMIC_STRIPPED |
+				 RX_FLAG_DECRYPTED |
+				 RX_FLAG_MIC_STRIPPED;
+
+
+		if (unlikely(desc_err_code & CC33XX_RX_DESC_MIC_FAIL)) {
+			status->flag |= RX_FLAG_MMIC_ERROR;
+			cc33xx_warning("Michael MIC error. Desc: 0x%x",
+				       desc_err_code);
+		}
+	}
+
+	if (beacon || probe_rsp)
+		status->boottime_ns = ktime_get_boottime_ns();
+	if (beacon)
+		wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel,
+						status->band);
+	status->nss = 1;
+}
+
+
+/* Copy part\ all of the descriptor. Allocate skb, or drop corrupted packet */
+int wlcore_rx_getPacketDescriptor(struct cc33xx *wl, u8 *raw_buffer_ptr,
+				  u16 *raw_buffer_len)
+{
+	u16 missing_desc_bytes;
+	u16 available_desc_bytes;
+	u16 pkt_data_len;
+	struct sk_buff *skb;
+	u16 prev_buffer_len = *raw_buffer_len;
+
+	missing_desc_bytes = sizeof(struct cc33xx_rx_descriptor) -
+				wl->partial_rx.handled_bytes;
+	available_desc_bytes = min(*raw_buffer_len, missing_desc_bytes);
+	memcpy(((u8 *)(&wl->partial_rx.desc))+wl->partial_rx.handled_bytes,
+		raw_buffer_ptr,available_desc_bytes);
+
+	/* If descriptor was not completed */
+	if (available_desc_bytes != missing_desc_bytes) {
+		wl->partial_rx.handled_bytes += *raw_buffer_len;
+		wl->partial_rx.status = CURR_RX_DESC;
+		*raw_buffer_len = 0;
+		goto out;
+	} else {
+		wl->partial_rx.handled_bytes += available_desc_bytes;
+		*raw_buffer_len -= available_desc_bytes;
+	}
+
+	/* Descriptor was fully copied */
+	pkt_data_len = wl->partial_rx.original_bytes -
+			sizeof(struct cc33xx_rx_descriptor);
+
+
+	if (unlikely(wl->partial_rx.desc.status & CC33XX_RX_DESC_DECRYPT_FAIL)) {
+		cc33xx_warning("corrupted packet in RX: status: 0x%x len: %d",
+			wl->partial_rx.desc.status & CC33XX_RX_DESC_STATUS_MASK,
+			pkt_data_len);
+
+		/* If frame can be fully dropped */
+		if (pkt_data_len <= *raw_buffer_len) {
+			*raw_buffer_len -=  pkt_data_len;
+			wl->partial_rx.status = CURR_RX_START;
+		}
+		else {
+			wl->partial_rx.handled_bytes += *raw_buffer_len;
+			wl->partial_rx.status = CURR_RX_DROP;
+			*raw_buffer_len = 0;
+		}
+		goto out;
+	}
+
+
+	skb = __dev_alloc_skb(pkt_data_len , GFP_KERNEL);
+	if (!skb) {
+		cc33xx_error("Couldn't allocate RX frame");
+		/* If frame can be fully dropped */
+		if (pkt_data_len <= *raw_buffer_len) {
+			*raw_buffer_len -=  pkt_data_len;
+			wl->partial_rx.status = CURR_RX_START;
+		} else {
+		/* Dropped partial frame */
+			wl->partial_rx.handled_bytes += *raw_buffer_len;
+			wl->partial_rx.status = CURR_RX_DROP;
+			*raw_buffer_len = 0;
+		}
+		goto out;
+	}
+
+	wl->partial_rx.skb = skb;
+	wl->partial_rx.status = CURR_RX_DATA;
+
+	out:
+	/* Function return the amount of consumed bytes */
+	return (prev_buffer_len - *raw_buffer_len);
+}
+
+/* Copy part or all of the packet's data. push skb to queue if possible */
+int wlcore_rx_getPacketData(struct cc33xx *wl, u8 *raw_buffer_ptr,
+			    u16 *raw_buffer_len)
+{
+	u16 missing_data_bytes;
+	u16 available_data_bytes;
+	u32 defer_count;
+	enum wl_rx_buf_align rx_align;
+	u16 extra_bytes;
+	struct ieee80211_hdr *hdr;
+	u8 beacon = 0;
+	u8 is_probe_resp = 0;
+	u8 is_data = 0;
+	u16 seq_num;
+	u16 prev_buffer_len = *raw_buffer_len;
+
+	cc33xx_debug(DEBUG_RX, "current rx data: original bytes: %d, "
+		"handled bytes %d, desc pad len %d, missing_data_bytes %d",
+		wl->partial_rx.original_bytes, wl->partial_rx.handled_bytes,
+		wl->partial_rx.desc.pad_len,missing_data_bytes);
+
+	missing_data_bytes = wl->partial_rx.original_bytes -
+				wl->partial_rx.handled_bytes;
+	available_data_bytes = min(missing_data_bytes,*raw_buffer_len);
+
+	skb_put_data(wl->partial_rx.skb, raw_buffer_ptr, available_data_bytes);
+
+	/* Check if we didn't manage to copy the entire packet - got out,
+	* continue next time */
+	if (available_data_bytes != missing_data_bytes) {
+	wl->partial_rx.handled_bytes += *raw_buffer_len;
+	wl->partial_rx.status = CURR_RX_DATA;
+	*raw_buffer_len = 0;
+	goto out;
+	}
+	else {
+	*raw_buffer_len -=  available_data_bytes;
+	}
+
+	/* Data fully copied */
+
+	rx_align = wl->partial_rx.desc.header_alignment;
+	if (rx_align == WLCORE_RX_BUF_PADDED)
+		skb_pull(wl->partial_rx.skb, RX_BUF_ALIGN);
+
+
+	extra_bytes = wl->partial_rx.desc.pad_len;
+	if (extra_bytes != 0)
+		skb_trim(wl->partial_rx.skb, wl->partial_rx.skb->len - extra_bytes);
+
+	hdr = (struct ieee80211_hdr *)wl->partial_rx.skb->data;
+
+	if (ieee80211_is_beacon(hdr->frame_control))
+		beacon = 1;
+	if (ieee80211_is_data_present(hdr->frame_control))
+		is_data = 1;
+	if (ieee80211_is_probe_resp(hdr->frame_control))
+		is_probe_resp = 1;
+
+
+    	cc33xx_rx_status(wl, &wl->partial_rx.desc,
+		     IEEE80211_SKB_RXCB(wl->partial_rx.skb),
+		     beacon, is_probe_resp);
+
+
+	seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+	cc33xx_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d link id %d",
+			wl->partial_rx.skb,
+			wl->partial_rx.skb->len - wl->partial_rx.desc.pad_len,
+			beacon ? "beacon" : "", seq_num, wl->partial_rx.desc.hlid);
+
+	cc33xx_debug(DEBUG_RX, "rx frame. frame type 0x%x, frame length 0x%x, "
+			"frame address 0x%lx",hdr->frame_control,
+			wl->partial_rx.skb->len,
+			(unsigned long)wl->partial_rx.skb->data);
+
+	/* Adding frame to queue */
+	skb_queue_tail(&wl->deferred_rx_queue, wl->partial_rx.skb);
+	wl->rx_counter++;
+	wl->partial_rx.status = CURR_RX_START;
+
+	/* Make sure the deferred queues don't get too long */
+	defer_count = skb_queue_len(&wl->deferred_tx_queue) +
+			skb_queue_len(&wl->deferred_rx_queue);
+	if (defer_count >= CC33XX_RX_QUEUE_MAX_LEN)
+		cc33xx_flush_deferred_work(wl);
+	else
+		queue_work(wl->freezable_netstack_wq, &wl->netstack_work);
+
+out:
+    	return (prev_buffer_len - *raw_buffer_len);
+}
+
+int wlcore_rx_dropPacketData(struct cc33xx *wl, u8 *raw_buffer_ptr,
+			     u16 *raw_buffer_len)
+{
+
+	u16 prev_buffer_len = *raw_buffer_len;
+
+	/* Can we drop the entire frame ? */
+	if (*raw_buffer_len >=
+		(wl->partial_rx.original_bytes - wl->partial_rx.handled_bytes)){
+		*raw_buffer_len -= wl->partial_rx.original_bytes -
+				wl->partial_rx.handled_bytes;
+		wl->partial_rx.handled_bytes = 0;
+		wl->partial_rx.status = CURR_RX_START;
+	} else {
+		wl->partial_rx.handled_bytes += *raw_buffer_len;
+		*raw_buffer_len = 0;
+	}
+
+	return (prev_buffer_len - *raw_buffer_len);
+}
+
+/* Handle single packet from the RX buffer. We don't have to be aligned to
+ * packet boundary (buffer may start \ end in the middle of packet) */
+static void cc33xx_rx_handle_packet(struct cc33xx *wl, u8 *raw_buffer_ptr,
+				    u16 *raw_buffer_len)
+{
+	struct cc33xx_rx_descriptor *desc;
+	u16 consumedBytes;
+
+
+	if (CURR_RX_START == wl->partial_rx.status) {
+		BUG_ON(*raw_buffer_len < 2);
+		desc = (struct cc33xx_rx_descriptor *)raw_buffer_ptr;
+		wl->partial_rx.original_bytes = desc->length;
+		wl->partial_rx.handled_bytes = 0;
+		wl->partial_rx.status = CURR_RX_DESC;
+
+		cc33xx_debug(DEBUG_RX, 
+			"rx frame. desc length 0x%x, alignment 0x%x, padding 0x%x",  
+			desc->length, desc->header_alignment, desc->pad_len);
+	}
+
+
+	/* start \ continue copy descriptor */
+	if (CURR_RX_DESC == wl->partial_rx.status) {
+		consumedBytes = wlcore_rx_getPacketDescriptor(wl, raw_buffer_ptr,
+							raw_buffer_len);
+		raw_buffer_ptr += consumedBytes;
+	}
+
+	/* Check if we are in the middle of dropped packet */
+	if (unlikely(CURR_RX_DROP == wl->partial_rx.status)){
+		consumedBytes = wlcore_rx_dropPacketData(wl, raw_buffer_ptr,
+							raw_buffer_len);
+		raw_buffer_ptr += consumedBytes;
+   	}
+
+	/* start \ continue copy descriptor */
+	if (CURR_RX_DATA == wl->partial_rx.status) {
+		consumedBytes = wlcore_rx_getPacketData(wl, raw_buffer_ptr,
+							raw_buffer_len);
+		raw_buffer_ptr += consumedBytes;
+	}
+}
+
+
+/*
+ * It is assumed that SDIO buffer was read prior to this function (data buffer
+ * is read along with the status). The RX function gets pointer to the RX data
+ * and its length. This buffer may contain unknown number of packets, separated
+ * by hif descriptor and 0-3 bytes padding if required.
+ * The last packet may be truncated in the middle, and should be saved for next
+ * iteration.
+ */
+int wlcore_rx(struct cc33xx *wl, u8 *rx_buf_ptr, u16 rx_buf_len)
+{
+	u16 local_rx_buffer_len = rx_buf_len;
+	u16 pkt_offset = 0;
+	u16 consumed_bytes;
+	u16 prev_rx_buf_len;
+
+
+	/* Split data into separate packets */
+	while (local_rx_buffer_len > 0) {
+		cc33xx_debug(DEBUG_RX,"start loop. buffer length %d" ,
+			local_rx_buffer_len);
+
+
+		/*
+		* the handle data call can only fail in memory-outage
+		* conditions, in that case the received frame will just
+		* be dropped.
+		*/
+		prev_rx_buf_len = local_rx_buffer_len;
+		cc33xx_rx_handle_packet (wl,
+					rx_buf_ptr + pkt_offset,
+					&local_rx_buffer_len);
+		consumed_bytes = prev_rx_buf_len - local_rx_buffer_len;
+
+		pkt_offset +=  consumed_bytes;
+
+		cc33xx_debug(DEBUG_RX,"end rx loop. buffer length %d, "
+			"packet counter %d, current packet status %d" ,
+			local_rx_buffer_len, wl->rx_counter, wl->partial_rx.status);
+	}
+
+	return(0);
+}
+
+#ifdef CONFIG_PM
+int cc33xx_rx_filter_enable(struct cc33xx *wl,
+		int index, bool enable,
+		struct cc33xx_rx_filter *filter)
+{
+	int ret;
+
+	if (!!test_bit(index, wl->rx_filter_enabled) == enable) {
+		cc33xx_warning("Request to enable an already "
+			"enabled rx filter %d", index);
+		return 0;
+	}
+
+	ret = cc33xx_acx_set_rx_filter(wl, index, enable, filter);
+
+	if (ret) {
+		cc33xx_error("Failed to %s rx data filter %d (err=%d)",
+			enable ? "enable" : "disable", index, ret);
+		return ret;
+	}
+
+	if (enable)
+		__set_bit(index, wl->rx_filter_enabled);
+	else
+		__clear_bit(index, wl->rx_filter_enabled);
+
+	return 0;
+}
+
+int cc33xx_rx_filter_clear_all(struct cc33xx *wl)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < CC33XX_MAX_RX_FILTERS; i++) {
+		if (!test_bit(i, wl->rx_filter_enabled))
+		continue;
+		ret = cc33xx_rx_filter_enable(wl, i, 0, NULL);
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+#endif /* CONFIG_PM */
diff --git drivers/net/wireless/ti/cc33xx/rx.h drivers/net/wireless/ti/cc33xx/rx.h
new file mode 100644
index 000000000000..3162aaacf7b3
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/rx.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of wl1271
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __RX_H__
+#define __RX_H__
+
+#include <linux/bitops.h>
+
+#define RSSI_LEVEL_BITMASK	0x7F
+#define ANT_DIVERSITY_BITMASK	BIT(7)
+
+#define SHORT_PREAMBLE_BIT   BIT(0)
+#define OFDM_RATE_BIT        BIT(6)
+#define PBCC_RATE_BIT        BIT(7)
+
+#define PLCP_HEADER_LENGTH 8
+#define RX_DESC_PACKETID_SHIFT 11
+#define RX_MAX_PACKET_ID 3
+
+#define RX_DESC_VALID_FCS         0x0001
+#define RX_DESC_MATCH_RXADDR1     0x0002
+#define RX_DESC_MCAST             0x0004
+#define RX_DESC_STAINTIM          0x0008
+#define RX_DESC_VIRTUAL_BM        0x0010
+#define RX_DESC_BCAST             0x0020
+#define RX_DESC_MATCH_SSID        0x0040
+#define RX_DESC_MATCH_BSSID       0x0080
+#define RX_DESC_ENCRYPTION_MASK   0x0300
+#define RX_DESC_MEASURMENT        0x0400
+#define RX_DESC_SEQNUM_MASK       0x1800
+#define	RX_DESC_MIC_FAIL	  0x2000
+#define	RX_DESC_DECRYPT_FAIL	  0x4000
+
+/*
+ * RX Descriptor flags:
+ *
+ * Bits 0-1 - band
+ * Bit  2   - STBC
+ * Bit  3   - A-MPDU
+ * Bit  4   - HT
+ * Bits 5-7 - encryption
+ */
+#define CC33XX_RX_DESC_BAND_MASK    0x03
+#define CC33XX_RX_DESC_ENCRYPT_MASK 0xE0
+
+#define CC33XX_RX_DESC_BAND_BG      0x00
+#define CC33XX_RX_DESC_BAND_J       0x01
+#define CC33XX_RX_DESC_BAND_A       0x02
+
+
+/*
+ * RX Descriptor status
+ *
+ * Bits 0-2 - error code
+ * Bits 3-5 - process_id tag (AP mode FW)
+ * Bits 6-7 - reserved
+ */
+#define CC33XX_RX_DESC_STATUS_MASK      0x07
+
+#define CC33XX_RX_DESC_SUCCESS          0x00
+#define CC33XX_RX_DESC_DECRYPT_FAIL     0x01
+#define CC33XX_RX_DESC_MIC_FAIL         0x02
+
+#define RX_MEM_BLOCK_MASK            0xFF
+#define RX_BUF_SIZE_MASK             0xFFF00
+#define RX_BUF_SIZE_SHIFT_DIV        6
+#define ALIGNED_RX_BUF_SIZE_MASK     0xFFFF00
+#define ALIGNED_RX_BUF_SIZE_SHIFT    8
+
+/* If set, the start of IP payload is not 4 bytes aligned */
+#define RX_BUF_UNALIGNED_PAYLOAD     BIT(20)
+
+/* If set, the buffer was padded by the FW to be 4 bytes aligned */
+#define RX_BUF_PADDED_PAYLOAD        BIT(30)
+
+/*
+ * Account for the padding inserted by the FW in case of RX_ALIGNMENT
+ * or for fixing alignment in case the packet wasn't aligned.
+ */
+#define RX_BUF_ALIGN                 2
+
+/* Describes the alignment state of a Rx buffer */
+enum wl_rx_buf_align {
+	WLCORE_RX_BUF_ALIGNED,
+	WLCORE_RX_BUF_UNALIGNED,
+	WLCORE_RX_BUF_PADDED,
+};
+
+enum wl_rx_curr_status
+{
+    CURR_RX_START,
+    CURR_RX_DROP,
+    CURR_RX_DESC,
+    CURR_RX_DATA
+};
+
+struct cc33xx_rx_descriptor {
+	__le16 length;
+    	u8  header_alignment;
+	u8  status;
+    __le32 timestamp;
+
+	u8  flags;
+	u8  rate;
+	u8  channel;
+	s8  rssi;
+	u8  snr;
+
+	u8  hlid;
+	u8  pad_len;
+	u8  frame_format;
+} __packed;
+
+
+struct partial_rx_frame{
+    struct sk_buff *skb;
+    struct cc33xx_rx_descriptor desc;
+    u16 handled_bytes;
+    u16 original_bytes; /* including descriptor */
+    enum wl_rx_curr_status status; 
+};
+
+int wlcore_rx(struct cc33xx *wl, u8 *rx_buf_ptr, u16 rx_buf_len);
+int cc33xx_rx_filter_enable(struct cc33xx *wl,
+			    int index, bool enable,
+			    struct cc33xx_rx_filter *filter);
+int cc33xx_rx_filter_clear_all(struct cc33xx *wl);
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/scan.c drivers/net/wireless/ti/cc33xx/scan.c
new file mode 100644
index 000000000000..3d6ed9b71b2c
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/scan.c
@@ -0,0 +1,853 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/ieee80211.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "cmd.h"
+#include "scan.h"
+#include "acx.h"
+#include "tx.h"
+
+
+
+static void cc33xx_adjust_channels(struct scan_param *scanParam,
+				   struct wlcore_scan_channels *cmd_channels,
+				   EScanRequestType scan_type)
+{
+
+    struct conn_scan_ch_info      *ch_list;
+    struct conn_scan_dwell_info   *dwell_info;
+
+    u8 *passive;
+    u8 *dfs;
+    u8 *active;
+    int i,j;
+
+    if(scan_type == SCAN_REQUEST_CONNECT_PERIODIC_SCAN)
+    {
+        //struct scan_periodic_info *pPeriodicScanParams = &scanParam->u.periodic;
+
+        ch_list        = scanParam->u.periodic.channel_list;
+        dwell_info   = scanParam->u.periodic.dwell_info;
+        active        = (u8*)&scanParam->u.periodic.active;
+        passive       = (u8*)&scanParam->u.periodic.passive;
+        dfs           = (u8*)&scanParam->u.periodic.dfs;
+
+    }
+    else
+    {
+        ch_list        = scanParam->u.one_shot.channel_list;
+        dwell_info   = scanParam->u.one_shot.dwell_info;
+        active        = (u8*)&scanParam->u.one_shot.active;
+        passive       = (u8*)&scanParam->u.one_shot.passive;
+        dfs           = (u8*)&scanParam->u.one_shot.dfs;
+    }
+
+    memcpy(passive, cmd_channels->passive, sizeof(cmd_channels->passive));
+    memcpy(active, cmd_channels->active, sizeof(cmd_channels->active));
+    *dfs = cmd_channels->dfs;
+
+    for (i = 0; i < MAX_CHANNELS_2GHZ; ++i)
+    {
+ 	ch_list[i].channel = cmd_channels->channels_2[i].channel;
+	ch_list[i].flags = cmd_channels->channels_2[i].flags;
+	ch_list[i].tx_power_att = cmd_channels->channels_2[i].tx_power_att;
+    }
+
+    dwell_info[NL80211_BAND_2GHZ].min_duration     = cmd_channels->channels_2[0].min_duration;
+    dwell_info[NL80211_BAND_2GHZ].max_duration     = cmd_channels->channels_2[0].max_duration;
+    dwell_info[NL80211_BAND_2GHZ].passive_duration = cmd_channels->channels_2[0].passive_duration;
+
+    for (j = 0; j < MAX_CHANNELS_5GHZ; ++i, ++j)
+    {
+        ch_list[i].channel = cmd_channels->channels_5[j].channel;
+        ch_list[i].flags = cmd_channels->channels_5[j].flags;
+        ch_list[i].tx_power_att = cmd_channels->channels_5[j].tx_power_att;
+    }
+    dwell_info[NL80211_BAND_5GHZ].min_duration     = cmd_channels->channels_5[0].min_duration;
+    dwell_info[NL80211_BAND_5GHZ].max_duration     = cmd_channels->channels_5[0].max_duration;
+    dwell_info[NL80211_BAND_5GHZ].passive_duration = cmd_channels->channels_5[0].passive_duration;
+
+}
+
+
+int cc33xx_cmd_build_probe_req(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+                   u8 role_id, u8 scan_type,
+                   const u8 *ssid, size_t ssid_len,
+                   const u8 *ie0, size_t ie0_len, const u8 *ie1,
+                   size_t ie1_len, bool sched_scan)
+{
+    struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+    struct sk_buff *skb=NULL;
+
+    struct cc33xx_cmd_set_ies *cmd;
+    int ret;
+
+    cc33xx_debug(DEBUG_SCAN, "build probe request scan_type %d", scan_type);
+
+    cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+    if (!cmd) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    skb = ieee80211_probereq_get(wl->hw, vif->addr, ssid, ssid_len,
+                     ie0_len + ie1_len);
+    if (!skb) {
+        ret = -ENOMEM;
+        goto out_free;
+    }
+    if (ie0_len)
+        skb_put_data(skb, ie0, ie0_len);
+    if (ie1_len)
+        skb_put_data(skb, ie1, ie1_len);
+
+    cmd->scan_type = scan_type;
+    cmd->role_id = role_id;
+
+    cmd->len = cpu_to_le16(skb->len) - sizeof(struct ieee80211_hdr_3addr);
+	
+    if (skb->data)
+        memcpy(cmd->data, skb->data + sizeof(struct ieee80211_hdr_3addr), cmd->len);
+
+    //Katya - temporary workaround - untill scan module is changed
+    usleep_range(10000, 11000);
+    ret = cc33xx_cmd_send(wl, CMD_SET_PROBE_IE, cmd, sizeof(*cmd), 0);
+
+    if (ret < 0) {
+        cc33xx_warning("cmd set_template failed: %d", ret);
+        goto out_free;
+    }
+
+out_free:
+    dev_kfree_skb(skb);
+    kfree(cmd);
+out:
+    return ret;
+}
+
+
+static int cc33xx_scan_send(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    struct cfg80211_scan_request *req)
+{
+	struct cc33xx_cmd_scan_params *cmd;
+	struct wlcore_scan_channels *cmd_channels = NULL;
+	struct cc33xx_ssid *cmd_ssid;
+	u16 alloc_size;
+	int ret;
+
+	alloc_size =  sizeof(*cmd) + (sizeof(struct cc33xx_ssid)*req->n_ssids);
+	cmd = kzalloc(alloc_size, GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* scan on the dev role if the regular one is not started */
+	if (wlcore_is_p2p_mgmt(wlvif))
+		cmd->role_id = wlvif->dev_role_id;
+	else
+		cmd->role_id = wlvif->role_id;
+
+	if (WARN_ON(cmd->role_id == CC33XX_INVALID_ROLE_ID)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	cmd->scan_type = SCAN_REQUEST_ONE_SHOT;
+	cmd->rssi_threshold = -127;
+	cmd->snr_threshold = 0;
+
+
+	cmd->ssid_from_list = 0;
+	cmd->filter = 0;
+	WARN_ON(req->n_ssids > 1);
+
+	/* configure channels */
+	cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+	if (!cmd_channels) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+				    req->n_channels, req->n_ssids,
+				    SCAN_TYPE_SEARCH);
+
+	cc33xx_adjust_channels(&cmd->params, cmd_channels, cmd->scan_type);
+    	if (req->n_ssids > 0) 
+	{
+		cmd->ssid_from_list = 1;
+		cmd->num_of_ssids = req->n_ssids;
+		cmd_ssid = (struct cc33xx_ssid *)((u8*)cmd + sizeof(*cmd));
+
+		cmd_ssid->len = req->ssids[0].ssid_len;
+		memcpy(cmd_ssid->ssid, req->ssids[0].ssid, cmd_ssid->len);
+		cmd_ssid->type = (req->ssids[0].ssid_len) ?
+				SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
+	}
+
+    	ret = cc33xx_cmd_build_probe_req(wl, wlvif,
+             cmd->role_id, cmd->scan_type,
+             req->ssids ? req->ssids[0].ssid : NULL,
+             req->ssids ? req->ssids[0].ssid_len : 0,
+             req->ie,
+             req->ie_len,
+             NULL,
+             0,
+             false);
+    	if (ret < 0) {
+        	cc33xx_error("PROBE request template failed");
+        	goto out;
+    	}
+	cc33xx_dump(DEBUG_SCAN, "SCAN: ", cmd, alloc_size);
+
+	ret = cc33xx_cmd_send(wl, CMD_SCAN, cmd, alloc_size, 0);
+	if (ret < 0) {
+		cc33xx_error("SCAN failed");
+		goto out;
+	}
+
+out:
+	kfree(cmd_channels);
+	kfree(cmd);
+	return ret;
+}
+
+int
+cc33xx_scan_sched_scan_ssid_list(struct cc33xx *wl,
+				 struct cc33xx_vif *wlvif,
+				 struct cfg80211_sched_scan_request *req,
+				 struct cc33xx_cmd_ssid_list *cmd)
+{
+	struct cfg80211_match_set *sets = req->match_sets;
+	struct cfg80211_ssid *ssids = req->ssids;
+	int ret = 0, type, i, j, n_match_ssids = 0;
+
+	cc33xx_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list");
+	/* count the match sets that contain SSIDs */
+	for (i = 0; i < req->n_match_sets; i++)
+	{
+		if (sets[i].ssid.ssid_len > 0)
+			n_match_ssids++;
+	}
+	/* No filter, no ssids or only bcast ssid */
+	if (!n_match_ssids &&
+	    (!req->n_ssids ||
+	     (req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) {
+		type = SCAN_SSID_FILTER_ANY;
+		goto out;
+	}
+
+	cmd->role_id = wlvif->role_id;
+	if (!n_match_ssids) {
+		/* No filter, with ssids */
+		type = SCAN_SSID_FILTER_DISABLED;
+
+		for (i = 0; i < req->n_ssids; i++) {
+			cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ?
+				SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
+			cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len;
+			memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid,
+			       ssids[i].ssid_len);
+			cmd->n_ssids++;
+		}
+	} else {
+		type = SCAN_SSID_FILTER_LIST;
+
+		/* Add all SSIDs from the filters */
+		for (i = 0; i < req->n_match_sets; i++) {
+			/* ignore sets without SSIDs */
+			if (!sets[i].ssid.ssid_len)
+				continue;
+
+			cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC;
+			cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len;
+			memcpy(cmd->ssids[cmd->n_ssids].ssid,
+			       sets[i].ssid.ssid, sets[i].ssid.ssid_len);
+			cmd->n_ssids++;
+		}
+		if ((req->n_ssids > 1) ||
+		    (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) {
+			/*
+			 * Mark all the SSIDs passed in the SSID list as HIDDEN,
+			 * so they're used in probe requests.
+			 */
+			for (i = 0; i < req->n_ssids; i++) {
+				if (!req->ssids[i].ssid_len)
+					continue;
+
+				for (j = 0; j < cmd->n_ssids; j++)
+				{
+					if ((req->ssids[i].ssid_len ==
+					     cmd->ssids[j].len) &&
+					    !memcmp(req->ssids[i].ssid,
+						   cmd->ssids[j].ssid,
+						   req->ssids[i].ssid_len)) {
+						cmd->ssids[j].type =
+							SCAN_SSID_TYPE_HIDDEN;
+						break;
+					}
+				}
+				/* Fail if SSID isn't present in the filters */
+				if (j == cmd->n_ssids) {
+					ret = -EINVAL;
+					goto out;
+				}
+			}
+		}
+	}
+
+	cc33xx_debug(DEBUG_CMD, "cmd sched scan with ssid list %d",cmd->n_ssids);
+	return cmd->n_ssids;
+out:
+	if (ret < 0)
+		return ret;
+	return 0;
+
+}
+
+
+static
+int cc33xx_scan_sched_scan_config(struct cc33xx *wl,
+				  struct cc33xx_vif *wlvif,
+				  struct cfg80211_sched_scan_request *req,
+				  struct ieee80211_scan_ies *ies)
+{
+	struct cc33xx_cmd_scan_params *cmd;
+	struct cc33xx_cmd_ssid_list *ssid_list;
+	struct wlcore_scan_channels *cmd_channels = NULL;
+	struct conf_sched_scan_settings *c = &wl->conf.host_conf.sched_scan;
+	int ret;
+	int n_ssids = 0;
+	int alloc_size = sizeof(*cmd);
+
+	cc33xx_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+	ssid_list = kzalloc(sizeof(*ssid_list), GFP_KERNEL);
+	if (!ssid_list) {
+		ret = -ENOMEM;
+		goto out_ssid_free;
+	}
+
+ 	n_ssids = cc33xx_scan_sched_scan_ssid_list(wl, wlvif, req, ssid_list);
+	if(n_ssids < 0) {
+		return n_ssids;
+	}
+	cc33xx_debug(DEBUG_CMD, "ssid list num of ssids %d", 
+		ssid_list->n_ssids);
+	if(n_ssids <= 5)
+		alloc_size += (n_ssids * sizeof(struct cc33xx_ssid));
+	else //n_ssids > 5
+	{	
+		ssid_list->scan_type = SCAN_REQUEST_CONNECT_PERIODIC_SCAN;
+		ret = cc33xx_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, ssid_list,
+			      sizeof(*ssid_list), 0);
+		if (ret < 0) {
+			cc33xx_error("cmd sched scan ssid list failed");
+			goto out_ssid_free;
+		}
+	}
+
+	cmd = kzalloc(alloc_size, GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	cmd->role_id = wlvif->role_id;
+
+	if (WARN_ON(cmd->role_id == CC33XX_INVALID_ROLE_ID)) {
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	cmd->scan_type = SCAN_REQUEST_CONNECT_PERIODIC_SCAN;
+	cmd->rssi_threshold = c->rssi_threshold;
+	cmd->snr_threshold = c->snr_threshold;
+
+	cmd->filter = 1;
+	cmd->num_of_ssids = n_ssids;
+
+	cc33xx_debug(DEBUG_CMD, "ssid list num of n_ssids %d", 
+				n_ssids);
+	if( (n_ssids > 0) && (n_ssids <= 5) )
+	{
+		cmd->ssid_from_list = 1;
+		memcpy((u8*)cmd + sizeof(*cmd),
+			ssid_list->ssids,
+			n_ssids * sizeof(struct cc33xx_ssid));
+	}
+
+	cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+	if (!cmd_channels) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	/* configure channels */
+	wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+				    req->n_channels, req->n_ssids,
+				    SCAN_TYPE_PERIODIC);
+	cc33xx_adjust_channels(&cmd->params, cmd_channels, cmd->scan_type);
+	
+
+	memcpy(cmd->params.u.periodic.sched_scan_plans,
+	       req->scan_plans,
+	       sizeof(struct sched_scan_plan_cmd)*req->n_scan_plans);
+
+	cmd->params.u.periodic.sched_scan_plans_num = req->n_scan_plans;
+
+	cc33xx_debug(DEBUG_SCAN, "interval[0]: %d, iterations[0]: %d, num_plans: %d",
+		     cmd->params.u.periodic.sched_scan_plans[0].interval,
+		     cmd->params.u.periodic.sched_scan_plans[0].iterations,
+		     cmd->params.u.periodic.sched_scan_plans_num);
+
+
+    	ret = cc33xx_cmd_build_probe_req(wl, wlvif,
+             cmd->role_id, cmd->scan_type,
+             req->ssids ? req->ssids[0].ssid : NULL,
+             req->ssids ? req->ssids[0].ssid_len : 0,
+	     ies->ies[NL80211_BAND_2GHZ],
+	     ies->len[NL80211_BAND_2GHZ],
+             ies->common_ies,
+	     ies->common_ie_len,
+	     true);
+    if (ret < 0) {
+        cc33xx_error("PROBE request template failed");
+        goto out_free;
+    }
+
+
+	cc33xx_dump(DEBUG_SCAN, "SCAN: ", cmd, alloc_size);
+
+	ret = cc33xx_cmd_send(wl, CMD_SCAN, cmd, alloc_size, 0);
+	if (ret < 0) {
+		cc33xx_error("SCAN failed");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd_channels);
+	kfree(cmd);
+out_ssid_free:
+	kfree(ssid_list);
+
+	return ret;
+}
+int cc33xx_sched_scan_start(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    struct cfg80211_sched_scan_request *req,
+			    struct ieee80211_scan_ies *ies)
+{
+	return cc33xx_scan_sched_scan_config(wl, wlvif, req, ies);
+}
+
+static int __cc33xx_scan_stop(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			       u8 scan_type)
+{
+
+    	struct cc33xx_cmd_scan_stop *stop;
+
+	int ret;
+
+	cc33xx_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+	stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+	if (!stop) {
+		cc33xx_error("failed to alloc memory to send sched scan stop");
+		return -ENOMEM;
+	}
+
+	stop->role_id = wlvif->role_id;
+	stop->scan_type = scan_type;
+
+	ret = cc33xx_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
+	if (ret < 0) {
+		cc33xx_error("failed to send sched scan stop command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(stop);
+	return ret;
+}
+
+void cc33xx_scan_sched_scan_stop(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	__cc33xx_scan_stop(wl, wlvif, SCAN_REQUEST_CONNECT_PERIODIC_SCAN);
+}
+
+int cc33xx_scan_start(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		      struct cfg80211_scan_request *req)
+{
+	return cc33xx_scan_send(wl, wlvif, req);
+}
+
+int cc33xx_scan_stop(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	return __cc33xx_scan_stop(wl, wlvif, SCAN_REQUEST_ONE_SHOT);
+
+}
+
+void cc33xx_scan_complete_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct cc33xx *wl;
+	struct cc33xx_vif *wlvif;
+	struct cfg80211_scan_info info = {
+		.aborted = false,
+	};
+
+	dwork = to_delayed_work(work);
+	wl = container_of(dwork, struct cc33xx, scan_complete_work);
+
+	cc33xx_debug(DEBUG_SCAN, "Scanning complete");
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (wl->scan.state == CC33XX_SCAN_STATE_IDLE)
+		goto out;
+
+	wlvif = wl->scan_wlvif;
+
+	/*
+	 * Rearm the tx watchdog just before idling scan. This
+	 * prevents just-finished scans from triggering the watchdog
+	 */
+	cc33xx_rearm_tx_watchdog_locked(wl);
+
+	wl->scan.state = CC33XX_SCAN_STATE_IDLE;
+	memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+	wl->scan.req = NULL;
+	wl->scan_wlvif = NULL;
+
+	if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+		/* restore hardware connection monitoring template */
+		cc33xx_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq);
+	}
+
+	if (wl->scan.failed) {
+		cc33xx_info("Scan completed due to error.");
+		cc33xx_queue_recovery_work(wl);
+	}
+
+	wlcore_cmd_regdomain_config_locked(wl);
+
+	ieee80211_scan_completed(wl->hw, &info);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+}
+
+static void wlcore_started_vifs_iter(void *data, u8 *mac,
+				     struct ieee80211_vif *vif)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+	bool active = false;
+	int *count = (int *)data;
+
+	/*
+	 * count active interfaces according to interface type.
+	 * checking only bss_conf.idle is bad for some cases, e.g.
+	 * we don't want to count sta in p2p_find as active interface.
+	 */
+	switch (wlvif->bss_type) {
+	case BSS_TYPE_STA_BSS:
+		if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+			active = true;
+		break;
+
+	case BSS_TYPE_AP_BSS:
+		if (wlvif->wl->active_sta_count > 0)
+			active = true;
+		break;
+
+	default:
+		break;
+	}
+
+	if (active)
+		(*count)++;
+}
+
+static int wlcore_count_started_vifs(struct cc33xx *wl)
+{
+	int count = 0;
+
+	ieee80211_iterate_active_interfaces_atomic(wl->hw,
+					IEEE80211_IFACE_ITER_RESUME_ALL,
+					wlcore_started_vifs_iter, &count);
+	return count;
+}
+
+static int
+wlcore_scan_get_channels(struct cc33xx *wl,
+			 struct ieee80211_channel *req_channels[],
+			 u32 n_channels,
+			 u32 n_ssids,
+			 struct conn_scan_ch_params *channels,
+			 u32 band, bool radar, bool passive,
+			 int start, int max_channels,
+			 u8 *n_pactive_ch,
+			 int scan_type)
+{
+	int i, j;
+	u32 flags;
+	bool force_passive = !n_ssids;
+	u32 min_dwell_time_active, max_dwell_time_active;
+	u32 dwell_time_passive, dwell_time_dfs;
+
+	/* configure dwell times according to scan type */
+	if (scan_type == SCAN_TYPE_SEARCH) {
+		struct conf_scan_settings *c = &wl->conf.host_conf.scan;
+		bool active_vif_exists = !!wlcore_count_started_vifs(wl);
+
+		min_dwell_time_active = active_vif_exists ?
+			c->min_dwell_time_active :
+			c->min_dwell_time_active_long;
+		max_dwell_time_active = active_vif_exists ?
+			c->max_dwell_time_active :
+			c->max_dwell_time_active_long;
+		dwell_time_passive = c->dwell_time_passive;
+		dwell_time_dfs = c->dwell_time_dfs;
+	} else {
+		struct conf_sched_scan_settings *c = &wl->conf.host_conf.sched_scan;
+		u32 delta_per_probe;
+
+		if (band == NL80211_BAND_5GHZ)
+			delta_per_probe = c->dwell_time_delta_per_probe_5;
+		else
+			delta_per_probe = c->dwell_time_delta_per_probe;
+
+		min_dwell_time_active = c->base_dwell_time +
+			 n_ssids * c->num_probe_reqs * delta_per_probe;
+
+		max_dwell_time_active = min_dwell_time_active +
+					c->max_dwell_time_delta;
+		dwell_time_passive = c->dwell_time_passive;
+		dwell_time_dfs = c->dwell_time_dfs;
+	}
+	min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
+	max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
+	dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
+	dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
+
+	for (i = 0, j = start;
+	     i < n_channels && j < max_channels;
+	     i++) {
+		flags = req_channels[i]->flags;
+
+		if (force_passive)
+			flags |= IEEE80211_CHAN_NO_IR;
+
+		if ((req_channels[i]->band == band) &&
+		    !(flags & IEEE80211_CHAN_DISABLED) &&
+		    (!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
+		    /* if radar is set, we ignore the passive flag */
+		    (radar ||
+		     !!(flags & IEEE80211_CHAN_NO_IR) == passive)) {
+			if (flags & IEEE80211_CHAN_RADAR) {
+				channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS;
+
+				channels[j].passive_duration =
+					cpu_to_le16(dwell_time_dfs);
+			} else {
+				channels[j].passive_duration =
+					cpu_to_le16(dwell_time_passive);
+			}
+
+			channels[j].min_duration =
+				cpu_to_le16(min_dwell_time_active);
+			channels[j].max_duration =
+				cpu_to_le16(max_dwell_time_active);
+
+			channels[j].tx_power_att = req_channels[i]->max_power;
+			channels[j].channel = req_channels[i]->hw_value;
+
+			if (n_pactive_ch &&
+			    (band == NL80211_BAND_2GHZ) &&
+			    (channels[j].channel >= 12) &&
+			    (channels[j].channel <= 14) &&
+			    (flags & IEEE80211_CHAN_NO_IR) &&
+			    !force_passive) {
+				/* pactive channels treated as DFS */
+				channels[j].flags = SCAN_CHANNEL_FLAGS_DFS;
+
+				/*
+				 * n_pactive_ch is counted down from the end of
+				 * the passive channel list
+				 */
+				(*n_pactive_ch)++;
+				cc33xx_debug(DEBUG_SCAN, "n_pactive_ch = %d",
+					     *n_pactive_ch);
+			}
+
+			cc33xx_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s",
+				     req_channels[i]->center_freq,
+				     req_channels[i]->hw_value,
+				     req_channels[i]->flags,
+				     req_channels[i]->max_power,
+				     min_dwell_time_active,
+				     max_dwell_time_active,
+				     flags & IEEE80211_CHAN_RADAR ?
+					", DFS" : "",
+				     flags & IEEE80211_CHAN_NO_IR ?
+					", NO-IR" : "");
+			j++;
+		}
+	}
+
+	return j - start;
+}
+
+bool
+wlcore_set_scan_chan_params(struct cc33xx *wl,
+			    struct wlcore_scan_channels *cfg,
+			    struct ieee80211_channel *channels[],
+			    u32 n_channels,
+			    u32 n_ssids,
+			    int scan_type)
+{
+	u8 n_pactive_ch = 0;
+
+	cfg->passive[0] =
+		wlcore_scan_get_channels(wl,
+					 channels,
+					 n_channels,
+					 n_ssids,
+					 cfg->channels_2,
+					 NL80211_BAND_2GHZ,
+					 false, true, 0,
+					 MAX_CHANNELS_2GHZ,
+					 &n_pactive_ch,
+					 scan_type);
+	cfg->active[0] =
+		wlcore_scan_get_channels(wl,
+					 channels,
+					 n_channels,
+					 n_ssids,
+					 cfg->channels_2,
+					 NL80211_BAND_2GHZ,
+					 false, false,
+					 cfg->passive[0],
+					 MAX_CHANNELS_2GHZ,
+					 &n_pactive_ch,
+					 scan_type);
+	cfg->passive[1] =
+		wlcore_scan_get_channels(wl,
+					 channels,
+					 n_channels,
+					 n_ssids,
+					 cfg->channels_5,
+					 NL80211_BAND_5GHZ,
+					 false, true, 0,
+					 wl->max_channels_5,
+					 &n_pactive_ch,
+					 scan_type);
+	cfg->dfs =
+		wlcore_scan_get_channels(wl,
+					 channels,
+					 n_channels,
+					 n_ssids,
+					 cfg->channels_5,
+					 NL80211_BAND_5GHZ,
+					 true, true,
+					 cfg->passive[1],
+					 wl->max_channels_5,
+					 &n_pactive_ch,
+					 scan_type);
+	cfg->active[1] =
+		wlcore_scan_get_channels(wl,
+					 channels,
+					 n_channels,
+					 n_ssids,
+					 cfg->channels_5,
+					 NL80211_BAND_5GHZ,
+					 false, false,
+					 cfg->passive[1] + cfg->dfs,
+					 wl->max_channels_5,
+					 &n_pactive_ch,
+					 scan_type);
+
+	/* 802.11j channels are not supported yet */
+	cfg->passive[2] = 0;
+	cfg->active[2] = 0;
+
+	cfg->passive_active = n_pactive_ch;
+
+	cc33xx_debug(DEBUG_SCAN, "    2.4GHz: active %d passive %d",
+		     cfg->active[0], cfg->passive[0]);
+	cc33xx_debug(DEBUG_SCAN, "    5GHz: active %d passive %d",
+		     cfg->active[1], cfg->passive[1]);
+	cc33xx_debug(DEBUG_SCAN, "    DFS: %d", cfg->dfs);
+
+	return  cfg->passive[0] || cfg->active[0] ||
+		cfg->passive[1] || cfg->active[1] || cfg->dfs ||
+		cfg->passive[2] || cfg->active[2];
+}
+
+int wlcore_scan(struct cc33xx *wl, struct ieee80211_vif *vif,
+		const u8 *ssid, size_t ssid_len,
+		struct cfg80211_scan_request *req)
+{
+	struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+
+	/*
+	 * cfg80211 should guarantee that we don't get more channels
+	 * than what we have registered.
+	 */
+	BUG_ON(req->n_channels > CC33XX_MAX_CHANNELS);
+
+	if (wl->scan.state != CC33XX_SCAN_STATE_IDLE)
+		return -EBUSY;
+
+	wl->scan.state = CC33XX_SCAN_STATE_2GHZ_ACTIVE;
+
+	if (ssid_len && ssid) {
+		wl->scan.ssid_len = ssid_len;
+		memcpy(wl->scan.ssid, ssid, ssid_len);
+	} else {
+		wl->scan.ssid_len = 0;
+	}
+
+	wl->scan_wlvif = wlvif;
+	wl->scan.req = req;
+	memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+
+	/* we assume failure so that timeout scenarios are handled correctly */
+	wl->scan.failed = true;
+	ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+				     msecs_to_jiffies(CC33XX_SCAN_TIMEOUT));
+
+	cc33xx_scan_start(wl, wlvif, req);
+
+	return 0;
+}
+
+void wlcore_scan_sched_scan_results(struct cc33xx *wl)
+{
+	cc33xx_debug(DEBUG_SCAN, "got periodic scan results");
+
+	ieee80211_sched_scan_results(wl->hw);
+}
+
+void cc33xx_scan_completed(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	cc33xx_debug(DEBUG_SCAN, "calling scan complete!");
+	wl->scan.failed = false;
+	cancel_delayed_work(&wl->scan_complete_work);
+	ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+				     msecs_to_jiffies(0));
+}
diff --git drivers/net/wireless/ti/cc33xx/scan.h drivers/net/wireless/ti/cc33xx/scan.h
new file mode 100644
index 000000000000..c8d161725431
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/scan.h
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __SCAN_H__
+#define __SCAN_H__
+
+#include "wlcore.h"
+
+#define CC33XX_SCAN_TIMEOUT    30000 /* msec */
+
+enum {
+	CC33XX_SCAN_STATE_IDLE,
+	CC33XX_SCAN_STATE_2GHZ_ACTIVE,
+	CC33XX_SCAN_STATE_2GHZ_PASSIVE,
+	CC33XX_SCAN_STATE_5GHZ_ACTIVE,
+	CC33XX_SCAN_STATE_5GHZ_PASSIVE,
+	CC33XX_SCAN_STATE_DONE
+};
+
+struct conn_scan_ch_params {
+	__le16 min_duration;
+	__le16 max_duration;
+	__le16 passive_duration;
+
+	u8  channel;
+	u8  tx_power_att;
+
+	/* bit 0: DFS channel; bit 1: DFS enabled */
+	u8  flags;
+
+	u8  padding[3];
+} __packed;
+
+enum {
+	SCAN_SSID_TYPE_PUBLIC = 0,
+	SCAN_SSID_TYPE_HIDDEN = 1,
+};
+
+
+struct tracking_ch_params {
+	struct conn_scan_ch_params channel;
+
+	__le32 bssid_lsb;
+	__le16 bssid_msb;
+
+	u8 padding[2];
+} __packed;
+
+
+#define MAX_CHANNELS_2GHZ	14
+#define MAX_CHANNELS_4GHZ	4
+#define MAX_CHANNELS_5GHZ	32
+
+#define SCAN_MAX_CYCLE_INTERVALS 16
+
+/* The FW intervals can take up to 16 entries.
+ * The 1st entry isn't used (scan is immediate). The last
+ * entry should be used for the long_interval
+ */
+#define SCAN_MAX_SHORT_INTERVALS (SCAN_MAX_CYCLE_INTERVALS - 2)
+#define SCAN_MAX_BANDS 3
+
+
+#define SCHED_SCAN_MAX_SSIDS 16
+
+/******************************************************************************
+* ** ***                                                               *** ** *
+* ** ***                          SCAN API                             *** ** *
+* ** ***                                                               *** ** *
+*******************************************************************************/
+
+#define CONN_SCAN_MAX_NUMBER_OF_SSID_ENTRIES        (16)
+#define CONN_SCAN_MAX_BAND                          (2)
+#define CONN_SCAN_MAX_CHANNELS_ALL_BANDS            (46)
+
+// Maximum number of supported scan plans for scheduled scan, supported by the driver
+#define SCAN_MAX_SCHED_SCAN_PLANS           (12)
+
+typedef enum
+{
+    SCAN_REQUEST_NONE,
+    SCAN_REQUEST_CONNECT_PERIODIC_SCAN,
+    SCAN_REQUEST_ONE_SHOT,
+    SCAN_REQUEST_SURVEY_SCAN,
+    SCAN_NUM_OF_REQUEST_TYPE
+} EScanRequestType;
+
+/******************************************************************************
+        ID:     CMD_SCAN
+        Desc:   This command will start scan process depending scan request
+                type
+        Return: CMD_COMPLETE
+******************************************************************************/
+/**
+* struct cc33xx_ssid - SSIDs connection scan description
+*
+* @type: SSID type - SCAN_SSID_TYPE_HIDDEN/SCAN_SSID_TYPE_PUBLIC
+*
+* @len:  Length of the ssid
+*
+* @ssid: SSID
+*/
+ struct cc33xx_ssid
+{
+    u8 type;
+	u8 len;
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+    u8 padding[2];
+
+} __packed;
+
+/**
+ * struct cc33xx_cmd_ssid_list - scan SSID list description
+ *
+ * @role_id:            roleID
+ *
+ * @num_of_ssids:       Number of SSID in the list. MAX 16 entries
+ * 
+ * @ssid_list:          SSIDs to scan for (active scan only)
+*/
+struct cc33xx_cmd_ssid_list
+{
+    struct cc33xx_cmd_header header;
+
+    u8 role_id;
+    u8 scan_type;
+	u8 n_ssids;
+    struct cc33xx_ssid ssids[SCHED_SCAN_MAX_SSIDS];
+    u8 padding;
+}__packed;
+
+/**
+ * struct conn_scan_dwell_info - Channels duration info per band
+ *
+ * @min_duration:        Min duration (in ms)
+ *
+ * @max_duration:        Max duration (in ms)
+ *
+ * @passive_duration:    Duration to use for passive scans (in ms)
+*/
+struct conn_scan_dwell_info
+{
+    __le16  min_duration;
+    __le16  max_duration;
+    __le16  passive_duration;
+} __packed ;
+
+/**
+ * struct conn_scan_ch_info - Channels info
+ *
+ * @channel:            channel number (channel_e)
+ *
+ * @tx_power_att:    TX power level in dbm
+ *
+ * @flags:       0 - DFS channel, 1 - DFS enabled (to be included in active scan)
+*/
+struct conn_scan_ch_info
+{
+    u8   channel;
+    u8   tx_power_att;
+    u8   flags;
+} __packed;
+
+/**
+ * struct scan_one_shot_info - ONE_SHOT scan param
+ *
+ * @passive:           		Number of passive scan channels in bands BG,A
+ *
+ * @active:            		Number of active scan channels in bands BG,A
+ *
+ * @dfs:               		Number of DFS channels in A band
+ *
+ * @channel_list:           Channel list info
+ *                          channels that are belonged to BG band are set from place 0 and forward.
+ *                          channels that are belonged to A band are set from place CONN_SCAN_MAX_CHANNELS_BG (14) and forward.
+ *                          channels that are belonged to 6Ghz band are set from place
+ *                          CONN_SCAN_MAX_CHANNELS_A_BG(14+32) and forward.
+ * @dwell_info:             Scan duration time info per band
+ *
+ * @reserved:
+ *           
+*/
+struct scan_one_shot_info
+{
+    u8  passive[CONN_SCAN_MAX_BAND];
+    u8  active[CONN_SCAN_MAX_BAND];
+    u8  dfs;
+
+    struct conn_scan_ch_info    channel_list[ CONN_SCAN_MAX_CHANNELS_ALL_BANDS ];
+    struct conn_scan_dwell_info dwell_info[CONN_SCAN_MAX_BAND];
+    u8  reserved;
+
+};
+
+/**
+ * sched_scan_plans - Scan plans for scheduled scan
+ *
+ * Each scan plan consists of the number of iterations to scan and the
+ * interval between scans. When a scan plan finishes (i.e., it was run
+ * for the specified number of iterations), the next scan plan is
+ * executed. The scan plans are executed in the order they appear in
+ * the array (lower index first). The last scan plan will run infinitely
+ * (until requested to stop), thus must not specify the number of
+ * iterations. All other scan plans must specify the number of
+ * iterations.
+ */
+struct sched_scan_plan_cmd {
+     u32 interval; /* In seconds */
+     u32 iterations; /* Zero to run infinitely */
+ } ;
+ /**
+ * struct periodicScanParams_t - Periodic scan param
+ *
+ * @sched_scan_plans:       Scan plans for a scheduled scan (defined in supplicant's driver.h)
+ *                          interval and iterations
+ *
+ * @sched_scan_plans_num:    Number of scan plans in sched_scan_plans array
+ *
+ * @passive:           Number of passive scan channels in bands BG,A
+ *
+ * @active:            Number of active scan channels in bands BG,A
+ *
+ * @dfs:               number of DFS channels in A band
+ *
+ * @channel_list:            Channel list info
+ *                          channels that are belonged to BG band are set from place 0 and forward.
+ *                          channels that are belonged to A band are set from place CONN_SCAN_MAX_CHANNELS_BG (14) and forward.
+ *                          channels that are belonged to 6Ghz band are set from place
+ *                          CONN_SCAN_MAX_CHANNELS_A_BG(14+32) and forward.
+ * @dwell_info:             Scan duration time info per band
+ *
+*/
+struct scan_periodic_info
+{
+    struct sched_scan_plan_cmd  sched_scan_plans[SCAN_MAX_SCHED_SCAN_PLANS];
+    u16 sched_scan_plans_num;
+
+    u8 passive[CONN_SCAN_MAX_BAND];
+    u8 active[CONN_SCAN_MAX_BAND];
+    u8 dfs;
+
+    struct conn_scan_ch_info      channel_list[ CONN_SCAN_MAX_CHANNELS_ALL_BANDS ];
+    struct conn_scan_dwell_info   dwell_info[CONN_SCAN_MAX_BAND];
+
+}__packed;
+
+/**
+ * struct scan_param - union for ONE_SHOT/PERIODIC scan param
+ *
+ * @one_shot:       ONE_SHOT scan param
+ *
+ * @periodic:       Periodic scan param
+*/
+struct scan_param
+{
+    union
+    {
+        struct scan_one_shot_info    one_shot;
+        struct scan_periodic_info    periodic;
+    } u;
+}__packed;
+
+/**
+ * struct cc33xx_cmd_scan_params - scan configured param
+ *
+ * @scan_type:    		ONE_SHOT/PERIODIC scan
+ *
+ * @role_id:            role ID
+ *
+ * @params:         	Scan parameter for ONE_SHOT/PERIODIC Scan
+ *
+ * @rssi_threshold:     RSSI threshold for basic filter
+ *
+ * @snr_threshold:      SNR threshold for basic filter
+ *
+ * @ssid_from_list:     0 - if there are more than 5 SSIDs entries, (list was sent SSID CONFIGURE COMMAND),
+ * 						1 - 5 or less SSIDs entries, the list is at the end of the scan command 
+ *
+ * @filter:       		0 - not using filter and all the beacons/probe response frame
+ *                      forward to upper mac,  1 - using filter
+ * 
+ * @num_of_ssids: 		Number of SSIDs 
+*/
+struct cc33xx_cmd_scan_params{
+    struct cc33xx_cmd_header header;
+    u8 scan_type;
+    u8 role_id;
+
+    struct scan_param   params;
+    s8 rssi_threshold; /* for filtering (in dBm) */
+    s8 snr_threshold;  /* for filtering (in dB) */
+
+    u8 ssid_from_list; /* use ssid from configured ssid list */
+    u8 filter;         /* forward only results with matching ssids */
+
+    u8 num_of_ssids;
+
+} __packed;
+/******************************************************************************
+        ID:     CMD_SET_PROBE_IE
+        Desc:   This command will  set the Info elements data for
+                probe request
+        Return: CMD_COMPLETE
+******************************************************************************/
+#define MAX_EXTRA_IES_LEN 512
+/**
+ * struct cc33xx_cmd_set_ies - Probe request info elements
+ *
+ * @scan_type:    ONE_SHOT/PERIODIC scan
+ *
+ * @role_id:      roleID
+ *
+ * @data:         info element buffer
+ *
+ * @len:          info element length
+*/
+struct cc33xx_cmd_set_ies{
+    struct cc33xx_cmd_header header;
+    u8 scan_type;
+    u8 role_id;
+    __le16 len;
+    u8                   data[MAX_EXTRA_IES_LEN];
+} __packed;
+/******************************************************************************
+        ID:     CMD_STOP_SCAN
+        Desc:   This command will stop scan process depending scan request
+                type, and if early termination is on
+        Return: CMD_COMPLETE
+******************************************************************************/
+/**
+ * struct cc33xx_cmd_scan_stop - scan stop param
+ *
+ * @scan_type:           Scan request type
+ *
+ * @role_id:             role ID
+ *
+ * @is_ET:               TRUE - Early termination is on, FALSE - no ET
+*/
+struct cc33xx_cmd_scan_stop {
+    struct cc33xx_cmd_header header;
+
+    u8 scan_type;
+    u8 role_id;
+    u8 is_ET;
+    u8 padding;
+} __packed;
+
+
+int cc33xx_scan_stop(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+void cc33xx_scan_completed(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+int cc33xx_sched_scan_start(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			    struct cfg80211_sched_scan_request *req,
+			    struct ieee80211_scan_ies *ies);
+void cc33xx_scan_sched_scan_stop(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+
+int wlcore_scan(struct cc33xx *wl, struct ieee80211_vif *vif,
+		const u8 *ssid, size_t ssid_len,
+		struct cfg80211_scan_request *req);
+void cc33xx_scan_complete_work(struct work_struct *work);
+void wlcore_scan_sched_scan_results(struct cc33xx *wl);
+
+enum {
+	SCAN_SSID_FILTER_ANY      = 0,
+	SCAN_SSID_FILTER_SPECIFIC = 1,
+	SCAN_SSID_FILTER_LIST     = 2,
+	SCAN_SSID_FILTER_DISABLED = 3
+};
+
+enum {
+	SCAN_BSS_TYPE_INDEPENDENT,
+	SCAN_BSS_TYPE_INFRASTRUCTURE,
+	SCAN_BSS_TYPE_ANY,
+};
+
+#define SCAN_CHANNEL_FLAGS_DFS		BIT(0) /* channel is passive until an
+						  activity is detected on it */
+#define SCAN_CHANNEL_FLAGS_DFS_ENABLED	BIT(1)
+
+struct wlcore_scan_channels {
+	u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+	u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+	u8 dfs;		   /* number of dfs channels in 5ghz */
+	u8 passive_active; /* number of passive before active channels 2.4ghz */
+
+	struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+	struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
+	struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+};
+
+enum {
+	SCAN_TYPE_SEARCH	= 0,
+	SCAN_TYPE_PERIODIC	= 1,
+	SCAN_TYPE_TRACKING	= 2,
+};
+
+bool
+wlcore_set_scan_chan_params(struct cc33xx *wl,
+			    struct wlcore_scan_channels *cfg,
+			    struct ieee80211_channel *channels[],
+			    u32 n_channels,
+			    u32 n_ssids,
+			    int scan_type);
+
+#endif /* __CC33XX_SCAN_H__ */
diff --git drivers/net/wireless/ti/cc33xx/sdio.c drivers/net/wireless/ti/cc33xx/sdio.c
new file mode 100644
index 000000000000..2df998c5242b
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/sdio.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/printk.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+
+#include "wlcore.h"
+#include "cc33xx_80211.h"
+#include "io.h"
+
+#ifndef SDIO_VENDOR_ID_TI
+#define SDIO_VENDOR_ID_TI		0x0097
+#endif
+
+#define SDIO_DEVICE_ID_CC33XX_NO_EFUSE 	0x4076
+#define SDIO_DEVICE_ID_TI_CC33XX	0x4077
+
+
+static bool dump = false;
+
+struct cc33xx_sdio_glue {
+	struct device *dev;
+	struct platform_device *core;
+};
+
+static const struct sdio_device_id cc33xx_devices[] = {
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_CC33XX) },
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_CC33XX_NO_EFUSE) },
+	{}
+};
+MODULE_DEVICE_TABLE(sdio, cc33xx_devices);
+
+static void cc33xx_sdio_claim(struct device *child)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+	sdio_claim_host(func);
+}
+
+static void cc33xx_sdio_release(struct device *child)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+	
+	sdio_release_host(func);
+}
+
+static void cc33xx_sdio_set_block_size(struct device *child,
+				       unsigned int blksz)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+	sdio_claim_host(func);
+	sdio_set_block_size(func, blksz);
+	sdio_release_host(func);
+}
+
+static int __must_check cc33xx_sdio_raw_read(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	int ret;
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+
+	sdio_claim_host(func);
+	
+
+	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
+		((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
+		dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
+			addr, ((u8 *)buf)[0]);
+	
+	} else {
+		if (fixed)
+			ret = sdio_readsb(func, buf, addr, len);
+		else
+			ret = sdio_memcpy_fromio(func, buf, addr, len);
+
+		dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
+			addr, len);
+	
+	}
+
+	sdio_release_host(func);
+
+	if (WARN_ON(ret))
+		dev_err(child->parent, "sdio read failed (%d)\n", ret);
+
+	if (unlikely(dump)) {
+		printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
+		print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
+			       DUMP_PREFIX_OFFSET, 16, 1,
+			       buf, len, false);
+	}
+
+	return ret;
+}
+
+static int __must_check cc33xx_sdio_raw_write(struct device *child, int addr,
+					      void *buf, size_t len, bool fixed)
+{
+	int ret;
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+	sdio_claim_host(func);
+
+	if (unlikely(dump)) {
+		printk(KERN_DEBUG "wlcore_sdio: "
+			"WRITE to 0x%04x length 0x%x (first 64 Bytes):\n", addr, len);
+		print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ",
+				DUMP_PREFIX_OFFSET, 16, 1,
+				buf, min(len, (size_t)64), false);
+	}
+
+	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
+		sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
+		dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
+			addr, ((u8 *)buf)[0]);
+	} else {
+		dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
+			addr, len);
+
+		if (fixed)
+			ret = sdio_writesb(func, addr, buf, len);
+		else
+			ret = sdio_memcpy_toio(func, addr, buf, len);
+	}
+
+	sdio_release_host(func);
+
+	if (WARN_ON(ret))
+		dev_err(child->parent, "sdio write failed (%d)\n", ret);
+
+	return ret;
+}
+
+static int cc33xx_sdio_power_on(struct cc33xx_sdio_glue *glue)
+{
+	int ret;
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+	struct mmc_card *card = func->card;
+
+	ret = pm_runtime_get_sync(&card->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(&card->dev);
+		dev_err(glue->dev, "%s: failed to get_sync(%d)\n",
+			__func__, ret);
+
+		return ret;
+	}
+
+	sdio_claim_host(func);
+	sdio_enable_func(func);
+	sdio_release_host(func);
+
+	return 0;
+}
+
+static int cc33xx_sdio_power_off(struct cc33xx_sdio_glue *glue)
+{
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+	struct mmc_card *card = func->card;
+
+	sdio_claim_host(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+
+	/* Let runtime PM know the card is powered off */
+	pm_runtime_put(&card->dev);
+	return 0;
+}
+
+static int cc33xx_sdio_set_power(struct device *child, bool enable)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+
+	if (enable)
+		return cc33xx_sdio_power_on(glue);
+	else
+		return cc33xx_sdio_power_off(glue);
+}
+
+
+/**
+ *	inband_irq_handler - Called from the MMC subsystem when the 
+ *	function's IRQ is signaled.
+ *	@func: an SDIO function of the card
+
+ *	Note that the host is already claimed when handler is invoked.
+ */
+static void inband_irq_handler(struct sdio_func *func)
+{
+	struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	dev_dbg(glue->dev, "Inband SDIO IRQ");
+
+	BUG_ON(!pdev_data->irq_handler);
+	pdev_data->irq_handler(pdev);
+}
+
+static void cc33xx_enable_async_interrupt(struct sdio_func *func)
+{
+	uint8_t regVal;
+	const int CCCR_REG_16_ADDR = 0x16;
+	const int ENABLE_ASYNC_IRQ_BIT = BIT(1);
+
+	regVal = sdio_f0_readb(func, CCCR_REG_16_ADDR, NULL);
+	regVal |= ENABLE_ASYNC_IRQ_BIT;
+	sdio_f0_writeb(func, regVal, CCCR_REG_16_ADDR, NULL);
+}
+
+static void cc33xx_sdio_enable_irq(struct device *child)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+	sdio_claim_host(func);
+	cc33xx_enable_async_interrupt(func);
+	sdio_claim_irq(func, inband_irq_handler);
+	sdio_release_host(func);
+}
+
+static void cc33xx_sdio_disable_irq(struct device *child)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_release_host(func);
+}
+
+static void cc33xx_enable_line_irq(struct device *child)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	enable_irq(pdev_data->gpio_irq_num);
+}
+
+static void cc33xx_disable_line_irq(struct device *child)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	disable_irq(pdev_data->gpio_irq_num);
+}
+
+static void cc33xx_set_irq_handler(struct device *child, void* handler)
+{
+	struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	pdev_data->irq_handler = handler;
+}
+
+
+static struct cc33xx_if_operations sdio_ops_gpio_irq = {
+	.interface_claim	= cc33xx_sdio_claim,
+	.interface_release 	= cc33xx_sdio_release,
+	.read			= cc33xx_sdio_raw_read,
+	.write			= cc33xx_sdio_raw_write,
+	.power			= cc33xx_sdio_set_power,
+	.set_block_size 	= cc33xx_sdio_set_block_size,
+	.set_irq_handler	= cc33xx_set_irq_handler,
+	.disable_irq		= cc33xx_disable_line_irq,
+	.enable_irq		= cc33xx_enable_line_irq,
+};
+
+static struct cc33xx_if_operations sdio_ops_inband_irq = {
+	.interface_claim	= cc33xx_sdio_claim,
+	.interface_release 	= cc33xx_sdio_release,
+	.read			= cc33xx_sdio_raw_read,
+	.write			= cc33xx_sdio_raw_write,
+	.power			= cc33xx_sdio_set_power,
+	.set_block_size 	= cc33xx_sdio_set_block_size,
+	.set_irq_handler	= cc33xx_set_irq_handler,
+	.disable_irq		= cc33xx_sdio_disable_irq,
+	.enable_irq		= cc33xx_sdio_enable_irq,
+};
+
+#ifdef CONFIG_OF
+
+static const struct cc33xx_family_data cc33xx_data = {
+	.name = "cc33xx",
+	.cfg_name = "ti-connectivity/cc33xx-conf.bin",
+	.nvs_name = "ti-connectivity/cc33xx-nvs.bin",
+};
+
+static const struct of_device_id wlcore_sdio_of_match_table[] = {
+	{ .compatible = "ti,cc33xx", .data = &cc33xx_data },
+	{ }
+};
+
+static int wlcore_probe_of(struct device *dev, int *irq, int *wakeirq,
+			   struct wlcore_platdev_data *pdev_data)
+{
+	struct device_node *np = dev->of_node;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_sdio_of_match_table, np);
+	if (!of_id)
+		return -ENODEV;
+
+	pdev_data->family = of_id->data;
+
+	*irq = irq_of_parse_and_map(np, 0);
+
+	*wakeirq = irq_of_parse_and_map(np, 1);
+
+	/* optional clock frequency params */
+	of_property_read_u32(np, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(np, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
+
+	return 0;
+}
+#else
+static int wlcore_probe_of(struct device *dev, int *irq, int *wakeirq,
+			   struct wlcore_platdev_data *pdev_data)
+{
+	return -ENODATA;
+}
+#endif
+
+static irqreturn_t gpio_irq_hard_handler(int irq, void *cookie)
+{
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t gpio_irq_thread_handler(int irq, void *cookie)
+{
+	struct sdio_func *func = cookie;
+	struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	BUG_ON(!pdev_data->irq_handler);
+
+	pdev_data->irq_handler(pdev);
+
+	return IRQ_HANDLED;
+}
+
+static int sdio_cc33xx_probe(struct sdio_func *func,
+				  const struct sdio_device_id *id)
+{
+	struct wlcore_platdev_data *pdev_data;
+	struct cc33xx_sdio_glue *glue;
+	struct resource res[1];
+	mmc_pm_flag_t mmcflags;
+	int ret = -ENOMEM;
+	int gpio_irq, wakeirq, irq_flags;
+	const char *chip_family;
+
+	/* We are only able to handle the wlan function */
+	if (func->num != 0x02)
+		return -ENODEV;
+
+	pdev_data = devm_kzalloc(&func->dev, sizeof(*pdev_data), GFP_KERNEL);
+	if (!pdev_data)
+		return -ENOMEM;
+
+	glue = devm_kzalloc(&func->dev, sizeof(*glue), GFP_KERNEL);
+	if (!glue)
+		return -ENOMEM;
+
+	glue->dev = &func->dev;
+
+	/* Grab access to FN0 for ELP reg. */
+	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+	/* Use block mode for transferring over one block size of data */
+	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+
+	ret = wlcore_probe_of(&func->dev, &gpio_irq, &wakeirq, pdev_data);
+	if (ret)
+		goto out;
+
+	/* if sdio can keep power while host is suspended, enable wow */
+	mmcflags = sdio_get_host_pm_caps(func);
+	dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
+
+	sdio_set_drvdata(func, glue);
+
+	/* Tell PM core that we don't need the card to be powered now */
+	pm_runtime_put_noidle(&func->dev);
+
+	chip_family = "cc33xx";
+
+	glue->core = platform_device_alloc(chip_family, PLATFORM_DEVID_AUTO);
+	if (!glue->core) {
+		dev_err(glue->dev, "can't allocate platform_device");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	glue->core->dev.parent = &func->dev;
+
+	if (gpio_irq)
+	{
+		dev_info(glue->dev, "Using GPIO as IRQ\n");
+		
+		irq_flags = irqd_get_trigger_type(irq_get_irq_data(gpio_irq));
+
+		irq_set_status_flags(gpio_irq, IRQ_NOAUTOEN);
+
+		if (irq_flags & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW))
+			irq_flags |= IRQF_ONESHOT;
+
+		ret = request_threaded_irq(
+			gpio_irq, gpio_irq_hard_handler, gpio_irq_thread_handler,
+			irq_flags, glue->core->name, func);
+		if (ret) {
+			dev_err(glue->dev, "can't register GPIO IRQ handler\n");
+			goto out_dev_put;
+		}
+
+		pdev_data->gpio_irq_num = gpio_irq;
+
+		if ((mmcflags & MMC_PM_KEEP_POWER) && (enable_irq_wake(gpio_irq)==0))
+			pdev_data->pwr_in_suspend = true;
+
+		pdev_data->if_ops = &sdio_ops_gpio_irq;
+	}
+	else
+	{
+		dev_info(glue->dev, "Using SDIO in-band IRQ\n");
+
+		pdev_data->if_ops = &sdio_ops_inband_irq;
+	}
+
+	if (wakeirq > 0) {
+		res[0].start = wakeirq;
+		res[0].flags = IORESOURCE_IRQ |
+			irqd_get_trigger_type(irq_get_irq_data(wakeirq));
+		res[0].name = "wakeirq";
+
+		ret = platform_device_add_resources(glue->core, res, 1);
+		if (ret) {
+			dev_err(glue->dev, "can't add resources\n");
+			goto out_dev_put;
+		}
+	}	
+
+	ret = platform_device_add_data(glue->core, pdev_data,
+				       sizeof(*pdev_data));
+	if (ret) {
+		dev_err(glue->dev, "can't add platform data\n");
+		goto out_dev_put;
+	}
+
+	ret = platform_device_add(glue->core);
+	if (ret) {
+		dev_err(glue->dev, "can't add platform device\n");
+		goto out_dev_put;
+	}
+	return 0;
+
+out_dev_put:
+	platform_device_put(glue->core);
+
+out:
+	return ret;
+}
+
+static void sdio_cc33xx_remove(struct sdio_func *func)
+{
+	struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	/* Undo decrement done above in sdio_cc33xx_probe */
+	pm_runtime_get_noresume(&func->dev);
+
+	platform_device_unregister(glue->core);
+
+	if (pdev_data->gpio_irq_num){
+		free_irq(pdev_data->gpio_irq_num, func);
+		disable_irq_wake(pdev_data->gpio_irq_num);
+	}
+	else{
+		sdio_claim_host(func);
+		sdio_release_irq(func);
+		sdio_release_host(func);
+	}
+}
+
+#ifdef CONFIG_PM
+static int cc33xx_suspend(struct device *dev)
+{
+	/* Tell MMC/SDIO core it's OK to power down the card
+	 * (if it isn't already), but not to remove it completely */
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+	struct cc33xx *wl = platform_get_drvdata(glue->core);
+	mmc_pm_flag_t sdio_flags;
+	int ret = 0;
+
+	if (!wl) {
+		dev_err(dev, "no wilink module was probed\n");
+		goto out;
+	}
+
+	dev_dbg(dev, "cc33xx suspend. keep_device_power: %d\n",
+		wl->keep_device_power);
+
+	if (wl->keep_device_power) {
+		sdio_flags = sdio_get_host_pm_caps(func);
+
+		if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+			dev_err(dev, "can't keep power while host "
+				     "is suspended\n");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/* keep power while host suspended */
+		ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+		if (ret) {
+			dev_err(dev, "error while trying to keep power\n");
+			goto out;
+		}
+	}
+out:
+	return ret;
+}
+
+static int cc33xx_resume(struct device *dev)
+{
+	dev_dbg(dev, "cc33xx resume\n");
+
+	return 0;
+}
+
+static const struct dev_pm_ops cc33xx_sdio_pm_ops = {
+	.suspend	= cc33xx_suspend,
+	.resume		= cc33xx_resume,
+};
+#endif
+
+static struct sdio_driver cc33xx_sdio_driver = {
+	.name		= "cc33xx_sdio",
+	.id_table	= cc33xx_devices,
+	.probe		= sdio_cc33xx_probe,
+	.remove		= sdio_cc33xx_remove,
+#ifdef CONFIG_PM
+	.drv = {
+		.pm = &cc33xx_sdio_pm_ops,
+	},
+#endif
+};
+
+static int __init sdio_cc33xx_init(void)
+{
+	return sdio_register_driver(&cc33xx_sdio_driver);
+}
+
+static void __exit sdio_cc33xx_exit(void)
+{
+	sdio_unregister_driver(&cc33xx_sdio_driver);
+}
+
+module_init(sdio_cc33xx_init);
+module_exit(sdio_cc33xx_exit);
+
+module_param(dump, bool, 0600);
+MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
+MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
diff --git drivers/net/wireless/ti/cc33xx/spi.c drivers/net/wireless/ti/cc33xx/spi.c
new file mode 100644
index 000000000000..705dc7eb63d7
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/spi.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
+#include <linux/crc7.h>
+#include <linux/spi/spi.h>
+#include <linux/wl12xx.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+
+#include "wlcore.h"
+#include "cc33xx_80211.h"
+#include "io.h"
+
+#define WSPI_CMD_READ			0x40000000
+#define WSPI_CMD_WRITE			0x00000000
+#define WSPI_CMD_FIXED			0x20000000
+#define WSPI_CMD_BYTE_LENGTH		0x1FFE0000
+#define WSPI_CMD_BYTE_LENGTH_OFFSET	17
+#define WSPI_CMD_BYTE_ADDR		0x0001FFFF
+
+#define WSPI_INIT_CMD_CRC_LEN		5
+
+#define WSPI_INIT_CMD_START		0x00
+#define WSPI_INIT_CMD_TX		0x40
+/* the extra bypass bit is sampled by the TNET as '1' */
+#define WSPI_INIT_CMD_BYPASS_BIT	0x80
+#define WSPI_INIT_CMD_FIXEDBUSY_LEN	0x07
+#define WSPI_INIT_CMD_EN_FIXEDBUSY	0x80
+#define WSPI_INIT_CMD_DIS_FIXEDBUSY	0x00
+#define WSPI_INIT_CMD_OPS		0x08
+#define WSPI_INIT_CMD_IOD		0x40
+#define WSPI_INIT_CMD_IP		0x20
+#define WSPI_INIT_CMD_CS		0x10
+#define WSPI_INIT_CMD_WS		0x08
+#define WSPI_INIT_CMD_WSPI		0x01
+#define WSPI_INIT_CMD_END		0x01
+
+#define WSPI_INIT_CMD_LEN		8
+
+#define HW_ACCESS_WSPI_FIXED_BUSY_LEN \
+		((CC33XX_BUSY_WORD_LEN - 4) / sizeof(u32))
+#define HW_ACCESS_WSPI_INIT_CMD_MASK  	0
+
+/* HW limitation: maximum possible chunk size is 4095 bytes */
+/* Actual size will have to be 32 bit aligned */
+#define WSPI_MAX_CHUNK_SIZE		4092
+
+/*
+ * wl18xx driver aggregation buffer size is (13 * 4K) compared to
+ * (4 * 4K) for wl12xx, so use the larger buffer needed for wl18xx
+ */
+#define SPI_AGGR_BUFFER_SIZE (13 * SZ_4K)
+
+/* Maximum number of SPI write chunks */
+#define WSPI_MAX_NUM_OF_CHUNKS \
+	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
+
+
+static const struct cc33xx_family_data cc33xx_data = {
+	.name = "cc33xx",
+	.cfg_name = "ti-connectivity/cc33xx-conf.bin",
+	.nvs_name = "ti-connectivity/cc33xx-nvs.bin",
+};
+
+struct cc33xx_spi_glue {
+	struct device *dev;
+	struct platform_device *core;
+	struct regulator *reg; /* Power regulator */
+	pid_t locking_pid;
+	int lock_count;
+};
+
+/* Must be allocated from DMA-safe memory */
+struct spi_transaction_buffers {
+	u32 spi_cmd;
+	u32 busyword;
+};
+
+static void __cc33xx_spi_lock(struct cc33xx_spi_glue *glue)
+{
+	if (glue->locking_pid != current->pid)
+	{
+		spi_bus_lock(to_spi_device(glue->dev)->master);
+		glue->locking_pid = current->pid;
+		glue->lock_count = 1;
+	} else {
+		glue->lock_count++;
+	}
+}
+
+static void __cc33xx_spi_unlock(struct cc33xx_spi_glue *glue)
+{
+	BUG_ON(glue->locking_pid != current->pid);
+	BUG_ON(!glue->lock_count);
+
+	glue->lock_count--;
+	if (!glue->lock_count){
+		glue->locking_pid = 0;
+		spi_bus_unlock(to_spi_device(glue->dev)->master);
+	}
+}
+
+static void cc33xx_spi_reset(struct device *child)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	u8 *cmd;
+	struct spi_transfer t;
+	struct spi_message m;
+
+	cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+	if (!cmd) {
+		dev_err(child->parent,
+			"could not allocate cmd for spi reset\n");
+		return;
+	}
+
+	memset(&t, 0, sizeof(t));
+	spi_message_init(&m);
+
+	memset(cmd, 0xff, WSPI_INIT_CMD_LEN);
+
+	t.tx_buf = cmd;
+	t.len = WSPI_INIT_CMD_LEN;
+	spi_message_add_tail(&t, &m);
+
+	spi_sync(to_spi_device(glue->dev), &m);
+
+	kfree(cmd);
+}
+
+static void cc33xx_spi_init(struct device *child)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	struct spi_transfer t;
+	struct spi_message m;
+	struct spi_device *spi = to_spi_device(glue->dev);
+	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+
+	if (!cmd) {
+		dev_err(child->parent,
+			"could not allocate cmd for spi init\n");
+		return;
+	}
+
+	memset(&t, 0, sizeof(t));
+	spi_message_init(&m);
+
+	/*
+	 * Set WSPI_INIT_COMMAND
+	 * the data is being send from the MSB to LSB
+	 */
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
+	cmd[3] = 0;
+	cmd[4] = 0;
+	cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
+	cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+	cmd[5] |= WSPI_INIT_CMD_OPS;
+
+	cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
+		| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
+
+	if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
+		cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
+	else
+		cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
+
+	cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
+
+	/*
+	 * The above is the logical order; it must actually be stored
+	 * in the buffer byte-swapped.
+	 */
+	__swab32s((u32 *)cmd);
+	__swab32s((u32 *)cmd+1);
+
+	t.tx_buf = cmd;
+	t.len = WSPI_INIT_CMD_LEN;
+	spi_message_add_tail(&t, &m);
+
+	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Send extra clocks with inverted CS (high). this is required
+	 * by the wilink family in order to successfully enter WSPI mode.
+	 */
+	spi->mode ^= SPI_CS_HIGH;
+	memset(&m, 0, sizeof(m));
+	spi_message_init(&m);
+
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = 0xff;
+	cmd[3] = 0xff;
+	__swab32s((u32 *)cmd);
+
+	t.tx_buf = cmd;
+	t.len = 4;
+	spi_message_add_tail(&t, &m);
+
+	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Restore chip select configration to normal */
+	spi->mode ^= SPI_CS_HIGH;
+	kfree(cmd);
+}
+
+#define CC33XX_BUSY_READ_TIMEOUT_MSEC 50
+
+static int cc33xx_spi_read_busy(struct device *child, u32 *busy_buf)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	unsigned long end_time = jiffies + msecs_to_jiffies(CC33XX_BUSY_READ_TIMEOUT_MSEC);
+	bool read_timeout = false;
+	struct spi_transfer t[1];
+	struct spi_message m;
+
+	/*
+	 * Read further busy words from SPI until a non-busy word is
+	 * encountered, then read the data itself into the buffer.
+	 */
+
+	while (!read_timeout) {
+		read_timeout = time_is_before_eq_jiffies(end_time);
+		spi_message_init(&m);
+		memset(t, 0, sizeof(t));
+		t[0].rx_buf = busy_buf;
+		t[0].len = sizeof(u32);
+		t[0].cs_change = true;
+		spi_message_add_tail(&t[0], &m);
+		spi_sync_locked(to_spi_device(glue->dev), &m);
+
+		if (*busy_buf & 0x1)
+			return 0;
+	}
+
+	/* The SPI bus is unresponsive, the read failed. */
+	dev_err(child->parent, "SPI read busy-word timeout!\n");
+	return -ETIMEDOUT;
+}
+
+static int __must_check cc33xx_spi_raw_read(struct device *child, int addr,
+					    void *buf, size_t len, bool fixed)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	struct spi_transaction_buffers* txn_buffers; 
+	struct spi_transfer t[2];
+	struct spi_message m;
+	int ret;
+	u32 *busy_buf;
+	u32 *cmd;
+
+	if (unlikely(len > WSPI_MAX_CHUNK_SIZE)){
+		WARN_ON(1);
+		return -EFAULT;
+	}
+
+	txn_buffers = kzalloc(sizeof (*txn_buffers), GFP_KERNEL);
+	if (!txn_buffers)
+		return -ENOMEM;
+
+	__cc33xx_spi_lock(glue);
+
+	cmd = &txn_buffers->spi_cmd;
+	busy_buf = &txn_buffers->busyword;
+
+	*cmd = 0;
+	*cmd |= WSPI_CMD_READ;
+	*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) &
+		WSPI_CMD_BYTE_LENGTH;
+	*cmd |= addr & WSPI_CMD_BYTE_ADDR;
+
+	if (fixed)
+		*cmd |= WSPI_CMD_FIXED;
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof(t));
+
+	t[0].tx_buf = cmd;
+	t[0].len = 4;
+	t[0].cs_change = false;
+	spi_message_add_tail(&t[0], &m);
+
+	/* Busy and non busy words read */
+	t[1].rx_buf = busy_buf;
+	t[1].len = CC33XX_BUSY_WORD_LEN;
+	t[1].cs_change = false;
+	spi_message_add_tail(&t[1], &m);
+
+	spi_sync_locked(to_spi_device(glue->dev), &m);
+
+	if (unlikely((*busy_buf & 0x1) == 0)){			
+		if( cc33xx_spi_read_busy(child, busy_buf) != 0){
+			memset(buf, 0, len);
+			ret = -EIO;
+			goto out;
+		}
+	}
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof(t));
+
+	t[0].rx_buf = buf;
+	t[0].len = len;
+	t[0].cs_change = false;
+	spi_message_add_tail(&t[0], &m);
+
+	spi_sync_locked(to_spi_device(glue->dev), &m);
+
+	ret=0;
+
+out:
+	__cc33xx_spi_unlock(glue);
+	kfree(txn_buffers);
+	return ret;
+}
+
+static int __cc33xx_spi_raw_write(struct device *child, int addr,
+				  void *buf, size_t len, bool fixed)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	struct spi_transfer t[2];
+	struct spi_transaction_buffers* txn_buffers; 
+	struct spi_message m;
+	u32 *cmd;
+	u32 *busy_buf;
+	u32 chunk_len;
+	int ret;
+
+	txn_buffers = kzalloc(sizeof (*txn_buffers), GFP_KERNEL);
+	if (!txn_buffers)
+		return -ENOMEM;
+
+	cmd = &txn_buffers->spi_cmd;
+	busy_buf = &txn_buffers->busyword;
+
+	__cc33xx_spi_lock(glue);
+
+	while (len > 0) {
+		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
+
+		*cmd = 0;
+		*cmd |= WSPI_CMD_WRITE;
+		*cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) &
+			WSPI_CMD_BYTE_LENGTH;
+		*cmd |= addr & WSPI_CMD_BYTE_ADDR;
+
+		if (fixed)
+			*cmd |= WSPI_CMD_FIXED;
+
+		spi_message_init(&m);
+		memset(t, 0, sizeof(t));
+
+		t[0].tx_buf = cmd;
+		t[0].len = 4;
+		t[0].cs_change = false;
+		spi_message_add_tail(&t[0], &m);
+
+		/* Busy and non busy words read */
+		t[1].rx_buf = busy_buf;
+		t[1].len = CC33XX_BUSY_WORD_LEN;
+		t[1].cs_change = false;
+		spi_message_add_tail(&t[1], &m);	
+
+		spi_sync_locked(to_spi_device(glue->dev), &m);	
+
+		if (unlikely((*busy_buf & 0x1) == 0)){			
+			if( cc33xx_spi_read_busy(child, busy_buf) != 0){
+				memset(buf, 0, chunk_len);
+				ret = -EIO;
+				goto out;
+			}
+		}
+
+		spi_message_init(&m);
+		memset(t, 0, sizeof(t));
+
+		t[0].tx_buf = buf;
+		t[0].len = chunk_len;
+		t[0].cs_change = false;
+		spi_message_add_tail(&t[0], &m);
+
+		spi_sync_locked(to_spi_device(glue->dev), &m);
+
+		if (!fixed)
+			addr += chunk_len;
+		buf += chunk_len;
+		len -= chunk_len;
+	}
+
+	ret=0;
+
+out:
+	__cc33xx_spi_unlock(glue);
+	kfree(txn_buffers);
+	return ret;
+}
+
+static int __must_check cc33xx_spi_raw_write(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	/* The ELP wakeup write may fail the first time due to internal
+	 * hardware latency. It is safer to send the wakeup command twice to
+	 * avoid unexpected failures.
+	 */
+	if (addr == HW_ACCESS_ELP_CTRL_REG)
+		__cc33xx_spi_raw_write(child, addr, buf, len, fixed);
+
+	return __cc33xx_spi_raw_write(child, addr, buf, len, fixed);
+}
+
+/**
+ * cc33xx_spi_set_power - power on/off the cc33xx unit
+ * @child: cc33xx device handle.
+ * @enable: true/false to power on/off the unit.
+ *
+ * use the WiFi enable regulator to enable/disable the WiFi unit.
+ */
+static int cc33xx_spi_set_power(struct device *child, bool enable)
+{
+	int ret = 0;
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+
+	WARN_ON(!glue->reg);
+
+	/* Update regulator state */
+	if (enable) {
+		ret = regulator_enable(glue->reg);
+		if (ret)
+			dev_err(child, "Power enable failure\n");
+	} else {
+		ret =  regulator_disable(glue->reg);
+		if (ret)
+			dev_err(child, "Power disable failure\n");
+	}
+
+	return ret;
+}
+
+/**
+ * cc33xx_spi_set_block_size
+ *
+ * This function is not needed for spi mode, but need to be present.
+ * Without it defined the wlcore fallback to use the wrong packet
+ * allignment on tx.
+ */
+static void cc33xx_spi_set_block_size(struct device *child,
+				      unsigned int blksz)
+{
+}
+
+static size_t cc33xx_spi_get_max_transfer_len(struct device *child)
+{
+	return WSPI_MAX_CHUNK_SIZE;
+}
+
+static void cc33xx_spi_set_irq_handler(struct device *child, void* irq_handler)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	pdev_data->irq_handler = irq_handler;
+}
+
+static void cc33xx_spi_enable_irq (struct device *child)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	enable_irq(pdev_data->gpio_irq_num);
+}
+
+static void cc33xx_spi_disable_irq (struct device *child)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	disable_irq(pdev_data->gpio_irq_num);
+}
+
+static void cc33xx_spi_interface_claim(struct device *child)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+
+	__cc33xx_spi_lock(glue);
+}
+
+static void cc33xx_spi_interface_release(struct device *child)
+{
+	struct cc33xx_spi_glue *glue = dev_get_drvdata(child->parent);
+
+	__cc33xx_spi_unlock(glue);
+}
+
+static irqreturn_t cc33xx_spi_irq_hard_handler(int irq, void *cookie)
+{
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cc33xx_spi_irq_handler(int irq, void *cookie)
+{
+	struct spi_device *spi = cookie;
+	struct cc33xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	BUG_ON(!pdev_data->irq_handler);
+	pdev_data->irq_handler(pdev);
+
+	return IRQ_HANDLED;
+}
+
+static struct cc33xx_if_operations spi_ops = {
+	.read				= cc33xx_spi_raw_read,
+	.write				= cc33xx_spi_raw_write,
+	.reset				= cc33xx_spi_reset,
+	.init				= cc33xx_spi_init,
+	.power				= cc33xx_spi_set_power,
+	.set_block_size 		= cc33xx_spi_set_block_size,
+	.get_max_transaction_len	= cc33xx_spi_get_max_transfer_len,
+	.set_irq_handler		= cc33xx_spi_set_irq_handler,
+	.enable_irq			= cc33xx_spi_enable_irq,
+	.disable_irq			= cc33xx_spi_disable_irq,
+	.interface_claim		= cc33xx_spi_interface_claim,
+	.interface_release		= cc33xx_spi_interface_release,
+};
+
+static const struct of_device_id wlcore_spi_of_match_table[] = {
+	{ .compatible = "ti,cc33xx", .data = &cc33xx_data},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
+
+/**
+ * wlcore_probe_of - DT node parsing.
+ * @spi: SPI slave device parameters.
+ * @res: resource parameters.
+ * @glue: cc33xx SPI bus to slave device glue parameters.
+ * @pdev_data: wlcore device parameters
+ */
+static int wlcore_probe_of(struct spi_device *spi, struct cc33xx_spi_glue *glue,
+			   int *irq, struct wlcore_platdev_data *pdev_data)
+{
+	struct device_node *dt_node = spi->dev.of_node;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
+	if (!of_id)
+		return -ENODEV;
+
+	pdev_data->family = of_id->data;
+	dev_info(&spi->dev, "selected chip family is %s\n",
+		 pdev_data->family->name);
+
+	*irq = irq_of_parse_and_map(dt_node, 0);
+	if (0 == *irq){
+		dev_err(&spi->dev, "Could not parse IRQ property");
+		return -ENODEV;
+	}
+
+	if (of_find_property(dt_node, "clock-xtal", NULL))
+		pdev_data->ref_clock_xtal = true;
+	/* optional clock frequency params */
+	of_property_read_u32(dt_node, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(dt_node, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
+
+	return 0;
+}
+
+static int spi_cc33xx_probe(struct spi_device *spi)
+{
+	struct cc33xx_spi_glue *glue;
+	struct wlcore_platdev_data *pdev_data;
+	int irq;
+	int ret;
+
+	pdev_data = devm_kzalloc(&spi->dev, sizeof(*pdev_data), GFP_KERNEL);
+	if (!pdev_data)
+		return -ENOMEM;
+
+	pdev_data->if_ops = &spi_ops;
+
+	glue = devm_kzalloc(&spi->dev, sizeof(*glue), GFP_KERNEL);
+	if (!glue) {
+		dev_err(&spi->dev, "can't allocate glue\n");
+		return -ENOMEM;
+	}
+
+	glue->dev = &spi->dev;
+
+	spi_set_drvdata(spi, glue);
+
+	/* This is the only SPI value that we need to set here, the rest
+	 * comes from the board-peripherals file */
+	spi->bits_per_word = 32;
+
+	glue->reg = devm_regulator_get(&spi->dev, "vwlan");
+	if (PTR_ERR(glue->reg) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (IS_ERR(glue->reg)) {
+		dev_err(glue->dev, "can't get regulator\n");
+		return PTR_ERR(glue->reg);
+	}
+
+	ret = wlcore_probe_of(spi, glue, &irq, pdev_data);
+	if (ret) {
+		dev_err(glue->dev,
+			"can't get device tree parameters (%d)\n", ret);
+		return ret;
+	}
+
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+	ret = request_threaded_irq(
+		irq, cc33xx_spi_irq_hard_handler, cc33xx_spi_irq_handler,
+		IRQF_TRIGGER_HIGH|IRQF_ONESHOT, pdev_data->family->name, spi);
+	if (ret) {
+		dev_err(glue->dev, "can't register GPIO IRQ handler\n");
+		goto out_dev_put;
+	}
+
+	pdev_data->gpio_irq_num = irq;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(glue->dev, "spi_setup failed\n");
+		return ret;
+	}
+
+	glue->core = platform_device_alloc(pdev_data->family->name,
+					   PLATFORM_DEVID_AUTO);
+	if (!glue->core) {
+		dev_err(glue->dev, "can't allocate platform_device\n");
+		return -ENOMEM;
+	}
+
+	glue->core->dev.parent = &spi->dev;
+
+	ret = platform_device_add_data(glue->core, pdev_data,
+				       sizeof(*pdev_data));
+	if (ret) {
+		dev_err(glue->dev, "can't add platform data\n");
+		goto out_dev_put;
+	}
+
+	ret = platform_device_add(glue->core);
+	if (ret) {
+		dev_err(glue->dev, "can't register platform device\n");
+		goto out_dev_put;
+	}
+
+	return 0;
+
+out_dev_put:
+	platform_device_put(glue->core);
+	return ret;
+}
+
+static void cc33xx_remove(struct spi_device *spi)
+{
+	struct cc33xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct platform_device *pdev = glue->core;
+	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+	platform_device_unregister(glue->core);
+
+	free_irq(pdev_data->gpio_irq_num, spi);
+
+	return;
+}
+
+static struct spi_driver cc33xx_spi_driver = {
+	.driver = {
+		.name		= "cc33xx_spi",
+		.of_match_table = of_match_ptr(wlcore_spi_of_match_table),
+	},
+
+	.probe		= spi_cc33xx_probe,
+	.remove		= cc33xx_remove,
+};
+
+module_spi_driver(cc33xx_spi_driver);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
+MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
+MODULE_ALIAS("spi:cc33xx");
diff --git drivers/net/wireless/ti/cc33xx/sysfs.c drivers/net/wireless/ti/cc33xx/sysfs.c
new file mode 100644
index 000000000000..6d99ba839aaa
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/sysfs.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ */
+
+#include "acx.h"
+#include "wlcore.h"
+#include "debug.h"
+#include "sysfs.h"
+
+
+static ssize_t cc33xx_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
+				       struct bin_attribute *bin_attr,
+				       char *buffer, loff_t pos, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct cc33xx *wl = dev_get_drvdata(dev);
+	ssize_t len;
+	int ret;
+
+	ret = mutex_lock_interruptible(&wl->mutex);
+	if (ret < 0)
+		return -ERESTARTSYS;
+
+	/* Check if the fwlog is still valid */
+	if (wl->fwlog_size < 0) {
+		mutex_unlock(&wl->mutex);
+		return 0;
+	}
+
+	/* Seeking is not supported - old logs are not kept. Disregard pos. */
+	len = min_t(size_t, count, wl->fwlog_size);
+	wl->fwlog_size -= len;
+	memcpy(buffer, wl->fwlog, len);
+
+	/* Make room for new messages */
+	memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
+
+	mutex_unlock(&wl->mutex);
+
+	return len;
+}
+
+static const struct bin_attribute fwlog_attr = {
+	.attr = { .name = "fwlog", .mode = 0400 },
+	.read = cc33xx_sysfs_read_fwlog,
+};
+
+int wlcore_sysfs_init(struct cc33xx *wl)
+{
+	int ret;
+
+
+	/* Create sysfs file for the FW log */
+	ret = device_create_bin_file(wl->dev, &fwlog_attr);
+	if (ret < 0) {
+		cc33xx_error("failed to create sysfs file fwlog");
+	}
+
+	return ret;
+}
+
+void wlcore_sysfs_free(struct cc33xx *wl)
+{
+	device_remove_bin_file(wl->dev, &fwlog_attr);
+}
diff --git drivers/net/wireless/ti/cc33xx/sysfs.h drivers/net/wireless/ti/cc33xx/sysfs.h
new file mode 100644
index 000000000000..cd346177959c
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/sysfs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ */
+
+#ifndef __SYSFS_H__
+#define __SYSFS_H__
+
+int wlcore_sysfs_init(struct cc33xx *wl);
+void wlcore_sysfs_free(struct cc33xx *wl);
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/testmode.c drivers/net/wireless/ti/cc33xx/testmode.c
new file mode 100644
index 000000000000..809c812b8903
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/testmode.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+#include "testmode.h"
+
+#include <linux/slab.h>
+#include <net/genetlink.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "acx.h"
+#include "io.h"
+
+#define CC33XX_TM_MAX_DATA_LENGTH 1024
+
+enum cc33xx_tm_commands {
+	CC33XX_TM_CMD_UNSPEC,
+	CC33XX_TM_CMD_TEST,
+	CC33XX_TM_CMD_INTERROGATE,
+	CC33XX_TM_CMD_CONFIGURE,
+	CC33XX_TM_CMD_NVS_PUSH,		/* Not in use. Keep to not break ABI */
+	CC33XX_TM_CMD_SET_PLT_MODE,
+	CC33XX_TM_CMD_RECOVER,		/* Not in use. Keep to not break ABI */
+	CC33XX_TM_CMD_GET_MAC,
+
+	__CC33XX_TM_CMD_AFTER_LAST
+};
+#define CC33XX_TM_CMD_MAX (__CC33XX_TM_CMD_AFTER_LAST - 1)
+
+enum cc33xx_tm_attrs {
+	CC33XX_TM_ATTR_UNSPEC,
+	CC33XX_TM_ATTR_CMD_ID,
+	CC33XX_TM_ATTR_ANSWER,
+	CC33XX_TM_ATTR_DATA,
+	CC33XX_TM_ATTR_IE_ID,
+	CC33XX_TM_ATTR_PLT_MODE,
+
+	__CC33XX_TM_ATTR_AFTER_LAST
+};
+#define CC33XX_TM_ATTR_MAX (__CC33XX_TM_ATTR_AFTER_LAST - 1)
+
+static struct nla_policy cc33xx_tm_policy[CC33XX_TM_ATTR_MAX + 1] = {
+	[CC33XX_TM_ATTR_CMD_ID] =	{ .type = NLA_U32 },
+	[CC33XX_TM_ATTR_ANSWER] =	{ .type = NLA_U8 },
+	[CC33XX_TM_ATTR_DATA] =		{ .type = NLA_BINARY,
+					  .len = CC33XX_TM_MAX_DATA_LENGTH },
+	[CC33XX_TM_ATTR_IE_ID] =	{ .type = NLA_U32 },
+	[CC33XX_TM_ATTR_PLT_MODE] =	{ .type = NLA_U32 },
+};
+
+
+static int cc33xx_tm_cmd_test(struct cc33xx *wl, struct nlattr *tb[])
+{
+	int buf_len, ret, len;
+	struct sk_buff *skb;
+	void *buf;
+	u8 answer = 0;
+
+	cc33xx_debug(DEBUG_TESTMODE, "testmode cmd test");
+
+	if (!tb[CC33XX_TM_ATTR_DATA])
+		return -EINVAL;
+
+	buf = nla_data(tb[CC33XX_TM_ATTR_DATA]);
+	buf_len = nla_len(tb[CC33XX_TM_ATTR_DATA]);
+
+	if (tb[CC33XX_TM_ATTR_ANSWER])
+		answer = nla_get_u8(tb[CC33XX_TM_ATTR_ANSWER]);
+
+	if (buf_len > sizeof(struct cc33xx_command))
+		return -EMSGSIZE;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_test(wl, buf, buf_len, answer);
+	if (ret < 0) {
+		cc33xx_warning("testmode cmd test failed: %d", ret);
+		goto out;
+	}
+
+	if (answer) {
+		/* If we got bip calibration answer print radio status */
+		struct cc33xx_cmd_cal_p2g *params =
+			(struct cc33xx_cmd_cal_p2g *) buf;
+		s16 radio_status = (s16) le16_to_cpu(params->radio_status);
+
+		if (params->test.id == TEST_CMD_P2G_CAL && radio_status < 0)
+			cc33xx_warning("testmode cmd: radio status=%d", radio_status);
+		else
+			cc33xx_info("testmode cmd: radio status=%d",
+					radio_status);
+
+		len = nla_total_size(buf_len);
+		skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
+		if (!skb) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		if (nla_put(skb, CC33XX_TM_ATTR_DATA, buf_len, buf)) {
+			kfree_skb(skb);
+			ret = -EMSGSIZE;
+			goto out;
+		}
+
+		ret = cfg80211_testmode_reply(skb);
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static int cc33xx_tm_cmd_interrogate(struct cc33xx *wl, struct nlattr *tb[])
+{
+	int ret;
+	struct cc33xx_command *cmd;
+	struct sk_buff *skb;
+	u8 ie_id;
+
+	cc33xx_debug(DEBUG_TESTMODE, "testmode cmd interrogate");
+
+	if (!tb[CC33XX_TM_ATTR_IE_ID])
+		return -EINVAL;
+
+	ie_id = nla_get_u8(tb[CC33XX_TM_ATTR_IE_ID]);
+
+	cc33xx_debug(DEBUG_TESTMODE, "testmode cmd interrogate id %d", ie_id);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = cc33xx_cmd_debug_inter(wl, ie_id, cmd,
+				     sizeof(struct acx_header), sizeof(*cmd));
+	if (ret < 0) {
+		cc33xx_warning("testmode cmd interrogate failed: %d", ret);
+		goto out_free;
+	}
+
+	skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	if (nla_put(skb, CC33XX_TM_ATTR_DATA, sizeof(*cmd), cmd)) {
+		kfree_skb(skb);
+		ret = -EMSGSIZE;
+		goto out_free;
+	}
+
+	ret = cfg80211_testmode_reply(skb);
+	if (ret < 0)
+		goto out_free;
+
+out_free:
+	kfree(cmd);
+
+out:
+	mutex_unlock(&wl->mutex);
+
+	return ret;
+}
+
+static int cc33xx_tm_cmd_configure(struct cc33xx *wl, struct nlattr *tb[])
+{
+	int buf_len, ret;
+	void *buf;
+	u8 ie_id;
+
+	cc33xx_debug(DEBUG_TESTMODE, "testmode cmd configure");
+
+	if (!tb[CC33XX_TM_ATTR_DATA])
+		return -EINVAL;
+	if (!tb[CC33XX_TM_ATTR_IE_ID])
+		return -EINVAL;
+
+	ie_id = nla_get_u8(tb[CC33XX_TM_ATTR_IE_ID]);
+	buf = nla_data(tb[CC33XX_TM_ATTR_DATA]);
+	buf_len = nla_len(tb[CC33XX_TM_ATTR_DATA]);
+
+	if (buf_len > sizeof(struct cc33xx_command))
+		return -EMSGSIZE;
+
+	mutex_lock(&wl->mutex);
+	ret = cc33xx_cmd_debug(wl, ie_id, buf, buf_len);
+	mutex_unlock(&wl->mutex);
+
+	if (ret < 0) {
+		cc33xx_warning("testmode cmd configure failed: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cc33xx_tm_detect_fem(struct cc33xx *wl, struct nlattr *tb[])
+{
+	/* return FEM type */
+	int ret, len;
+	struct sk_buff *skb;
+
+	ret = cc33xx_plt_start(wl, PLT_FEM_DETECT);
+	if (ret < 0)
+		goto out;
+
+	mutex_lock(&wl->mutex);
+
+	len = nla_total_size(sizeof(wl->fem_manuf));
+	skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out_mutex;
+	}
+
+	if (nla_put(skb, CC33XX_TM_ATTR_DATA, sizeof(wl->fem_manuf),
+					      &wl->fem_manuf)) {
+		kfree_skb(skb);
+		ret = -EMSGSIZE;
+		goto out_mutex;
+	}
+
+	ret = cfg80211_testmode_reply(skb);
+
+out_mutex:
+	mutex_unlock(&wl->mutex);
+
+	/* We always stop plt after DETECT mode */
+	cc33xx_plt_stop(wl);
+out:
+	return ret;
+}
+
+static int cc33xx_tm_cmd_set_plt_mode(struct cc33xx *wl, struct nlattr *tb[])
+{
+	u32 val;
+	int ret;
+
+	cc33xx_debug(DEBUG_TESTMODE, "testmode cmd set plt mode");
+
+	if (!tb[CC33XX_TM_ATTR_PLT_MODE])
+		return -EINVAL;
+
+	val = nla_get_u32(tb[CC33XX_TM_ATTR_PLT_MODE]);
+
+	switch (val) {
+	case PLT_OFF:
+		ret = cc33xx_plt_stop(wl);
+		break;
+	case PLT_ON:
+	case PLT_CHIP_AWAKE:
+		ret = cc33xx_plt_start(wl, val);
+		break;
+	case PLT_FEM_DETECT:
+		ret = cc33xx_tm_detect_fem(wl, tb);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int cc33xx_tm_cmd_get_mac(struct cc33xx *wl, struct nlattr *tb[])
+{
+	struct sk_buff *skb;
+	u8 zero_mac[ETH_ALEN] = {0};
+	int ret = 0;
+
+	mutex_lock(&wl->mutex);
+
+	if (!wl->plt) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (0 == memcmp(zero_mac, wl->efuse_mac_address, ETH_ALEN)) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (nla_put(skb, CC33XX_TM_ATTR_DATA, ETH_ALEN, wl->efuse_mac_address)) {
+		kfree_skb(skb);
+		ret = -EMSGSIZE;
+		goto out;
+	}
+
+	ret = cfg80211_testmode_reply(skb);
+	if (ret < 0)
+		goto out;
+
+out:
+	mutex_unlock(&wl->mutex);
+	return ret;
+}
+
+int cc33xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		  void *data, int len)
+{
+	struct cc33xx *wl = hw->priv;
+	struct nlattr *tb[CC33XX_TM_ATTR_MAX + 1];
+	u32 nla_cmd;
+	int err;
+
+	err = nla_parse_deprecated(tb, CC33XX_TM_ATTR_MAX, data, len,
+				   cc33xx_tm_policy, NULL);
+	if (err)
+		return err;
+
+	if (!tb[CC33XX_TM_ATTR_CMD_ID])
+		return -EINVAL;
+
+	nla_cmd = nla_get_u32(tb[CC33XX_TM_ATTR_CMD_ID]);
+
+	/* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */
+	if (wl->plt_mode == PLT_CHIP_AWAKE &&
+	    nla_cmd != CC33XX_TM_CMD_SET_PLT_MODE)
+		return -EOPNOTSUPP;
+
+	switch (nla_cmd) {
+	case CC33XX_TM_CMD_TEST:
+		return cc33xx_tm_cmd_test(wl, tb);
+	case CC33XX_TM_CMD_INTERROGATE:
+		return cc33xx_tm_cmd_interrogate(wl, tb);
+	case CC33XX_TM_CMD_CONFIGURE:
+		return cc33xx_tm_cmd_configure(wl, tb);
+	case CC33XX_TM_CMD_SET_PLT_MODE:
+		return cc33xx_tm_cmd_set_plt_mode(wl, tb);
+	case CC33XX_TM_CMD_GET_MAC:
+		return cc33xx_tm_cmd_get_mac(wl, tb);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
diff --git drivers/net/wireless/ti/cc33xx/testmode.h drivers/net/wireless/ti/cc33xx/testmode.h
new file mode 100644
index 000000000000..f650db7ee83e
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/testmode.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __TESTMODE_H__
+#define __TESTMODE_H__
+
+#include <net/mac80211.h>
+
+int cc33xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		  void *data, int len);
+
+#endif /* __WL1271_TESTMODE_H__ */
diff --git drivers/net/wireless/ti/cc33xx/tx.c drivers/net/wireless/ti/cc33xx/tx.c
new file mode 100644
index 000000000000..67031aad71df
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/tx.c
@@ -0,0 +1,1458 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/spinlock.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "io.h"
+#include "ps.h"
+#include "tx.h"
+#include "event.h"
+
+/*
+ * TODO: this is here just for now, it must be removed when the data
+ * operations are in place.
+ */
+#include "../wl12xx/reg.h"
+
+static int cc33xx_set_default_wep_key(struct cc33xx *wl,
+				      struct cc33xx_vif *wlvif, u8 id)
+{
+	int ret;
+	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+
+	if (is_ap)
+		ret = cc33xx_cmd_set_default_wep_key(wl, id,
+						     wlvif->ap.bcast_hlid);
+	else
+		ret = cc33xx_cmd_set_default_wep_key(wl, id, wlvif->sta.hlid);
+
+	if (ret < 0)
+		return ret;
+
+	cc33xx_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id);
+	return 0;
+}
+
+static int cc33xx_alloc_tx_id(struct cc33xx *wl, struct sk_buff *skb)
+{
+	int id;
+
+	id = find_first_zero_bit(wl->tx_frames_map, wl->num_tx_desc);
+	if (id >= wl->num_tx_desc)
+		return -EBUSY;
+
+	__set_bit(id, wl->tx_frames_map);
+	wl->tx_frames[id] = skb;
+	wl->tx_frames_cnt++;
+	cc33xx_debug(DEBUG_TX, "alloc desc ID. id - %d, frames count %d",id,wl->tx_frames_cnt);
+	return id;
+}
+
+void cc33xx_free_tx_id(struct cc33xx *wl, int id)
+{
+	if (__test_and_clear_bit(id, wl->tx_frames_map)) {
+		if (unlikely(wl->tx_frames_cnt == wl->num_tx_desc))
+			clear_bit(CC33XX_FLAG_FW_TX_BUSY, &wl->flags);
+
+		wl->tx_frames[id] = NULL;
+		wl->tx_frames_cnt--;
+	}
+	cc33xx_debug(DEBUG_TX, "free desc ID. id - %d, frames count %d",id,wl->tx_frames_cnt);
+
+}
+EXPORT_SYMBOL(cc33xx_free_tx_id);
+
+static void cc33xx_tx_ap_update_inconnection_sta(struct cc33xx *wl,
+						 struct cc33xx_vif *wlvif,
+						 struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+
+	hdr = (struct ieee80211_hdr *)(skb->data +
+				       sizeof(struct cc33xx_tx_hw_descr));
+	if (!ieee80211_is_auth(hdr->frame_control))
+		return;
+
+	/*
+	 * add the station to the known list before transmitting the
+	 * authentication response. this way it won't get de-authed by FW
+	 * when transmitting too soon.
+	 */
+	cc33xx_acx_set_inconnection_sta(wl, wlvif, hdr->addr1);
+
+	/*
+	 * ROC for 1 second on the AP channel for completing the connection.
+	 * Note the ROC will be continued by the update_sta_state callbacks
+	 * once the station reaches the associated state.
+	 */
+	wlcore_update_inconn_sta(wl, wlvif, NULL, true);
+	wlvif->pending_auth_reply_time = jiffies;
+	cancel_delayed_work(&wlvif->pending_auth_complete_work);
+	ieee80211_queue_delayed_work(wl->hw,
+				&wlvif->pending_auth_complete_work,
+				msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT));
+}
+
+static void cc33xx_tx_regulate_link(struct cc33xx *wl,
+				    struct cc33xx_vif *wlvif,
+				    u8 hlid)
+{
+	bool fw_ps;
+	u8 tx_pkts;
+
+	if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
+		return;
+
+	fw_ps = test_bit(hlid, &wl->ap_fw_ps_map);
+	tx_pkts = wl->links[hlid].allocated_pkts;
+
+	/*
+	 * if in FW PS and there is enough data in FW we can put the link
+	 * into high-level PS and clean out its TX queues.
+	 * Make an exception if this is the only connected link. In this
+	 * case FW-memory congestion is less of a problem.
+	 * Note that a single connected STA means 2*ap_count + 1 active links,
+	 * since we must account for the global and broadcast AP links
+	 * for each AP. The "fw_ps" check assures us the other link is a STA
+	 * connected to the AP. Otherwise the FW would not set the PSM bit.
+	 */
+	if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
+	    tx_pkts >= CC33XX_PS_STA_MAX_PACKETS)
+		cc33xx_ps_link_start(wl, wlvif, hlid, true);
+}
+
+bool cc33xx_is_dummy_packet(struct cc33xx *wl, struct sk_buff *skb)
+{
+	return wl->dummy_packet == skb;
+}
+EXPORT_SYMBOL(cc33xx_is_dummy_packet);
+
+static u8 cc33xx_tx_get_hlid_ap(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+	if (sta) {
+		struct cc33xx_station *wl_sta;
+
+		wl_sta = (struct cc33xx_station *)sta->drv_priv;
+		return wl_sta->hlid;
+	} else {
+		struct ieee80211_hdr *hdr;
+
+		if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
+			return CC33XX_SYSTEM_HLID;
+
+		hdr = (struct ieee80211_hdr *)skb->data;
+		if (is_multicast_ether_addr(ieee80211_get_DA(hdr)))
+			return wlvif->ap.bcast_hlid;
+		else
+			return wlvif->ap.global_hlid;
+	}
+}
+
+u8 cc33xx_tx_get_hlid(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		      struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+	struct ieee80211_tx_info *control;
+
+	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+		return cc33xx_tx_get_hlid_ap(wl, wlvif, skb, sta);
+
+	control = IEEE80211_SKB_CB(skb);
+	if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+		cc33xx_debug(DEBUG_TX, "tx offchannel");
+		return wlvif->dev_hlid;
+	}
+
+	return wlvif->sta.hlid;
+}
+
+unsigned int wlcore_calc_packet_alignment(struct cc33xx *wl,
+					  unsigned int packet_length)
+{
+	if ((wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) ||
+	    !(wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN))
+		return ALIGN(packet_length, CC33XX_TX_ALIGN_TO);
+	else
+		return ALIGN(packet_length, CC33XX_BUS_BLOCK_SIZE);
+}
+EXPORT_SYMBOL(wlcore_calc_packet_alignment);
+
+static u32 cc33xx_calc_tx_blocks(struct cc33xx *wl, u32 len, u32 spare_blks)
+{
+    u32 blk_size = CC33XX_TX_HW_BLOCK_SIZE;
+    /* In CC33xx the packet will be stored along with its internal descriptor.
+     * the descriptor is not part of the host transaction, but should be considered as part of
+     * the allocate memory blocks in the device
+     */
+    len = len + CC33xx_INTERNAL_DESC_SIZE;
+    return (len + blk_size - 1) / blk_size + spare_blks;
+}
+
+static void
+cc33xx_set_tx_desc_blocks(struct cc33xx *wl, struct cc33xx_tx_hw_descr *desc,
+			  u32 blks, u32 spare_blks)
+{
+	desc->cc33xx_mem.total_mem_blocks = blks;
+}
+
+static void
+cc33xx_set_tx_desc_data_len(struct cc33xx *wl, struct cc33xx_tx_hw_descr *desc,
+			    struct sk_buff *skb)
+{
+	desc->length = cpu_to_le16(skb->len);
+
+	/* if only the last frame is to be padded, we unset this bit on Tx */
+	if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME)
+		desc->cc33xx_mem.ctrl = CC33XX_TX_CTRL_NOT_PADDED;
+	else
+		desc->cc33xx_mem.ctrl = 0;
+
+	cc33xx_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d "
+		     "len: %d life: %d mem: %d", desc->hlid,
+		     le16_to_cpu(desc->length),
+		     le16_to_cpu(desc->life_time),
+		     desc->cc33xx_mem.total_mem_blocks);
+}
+
+
+static int cc33xx_get_spare_blocks(struct cc33xx *wl, bool is_gem)
+{
+	/* If we have keys requiring extra spare, indulge them */
+	if (wl->extra_spare_key_count)
+		return CC33XX_TX_HW_EXTRA_BLOCK_SPARE;
+
+	return CC33XX_TX_HW_BLOCK_SPARE;
+}
+
+
+static int cc33xx_tx_allocate(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      struct sk_buff *skb, u32 extra, u32 buf_offset,
+			      u8 hlid, bool is_gem, struct NAB_tx_header *nab_cmd)
+{
+	struct cc33xx_tx_hw_descr *desc;
+
+	u32 total_blocks;
+	int id, ret = -EBUSY, ac;
+	u32 spare_blocks;
+    	u32 total_skb_len = skb->len + sizeof(struct cc33xx_tx_hw_descr) + extra;
+    	// Add  NAB command required for CC33xx architecture
+    	u32 total_len = total_skb_len + sizeof(struct NAB_tx_header);
+
+    cc33xx_debug(DEBUG_TX,"michal1 wl->tx_blocks_available %d", wl->tx_blocks_available);
+
+	if (buf_offset + total_len > wl->aggr_buf_size)
+	{
+	    cc33xx_debug(DEBUG_TX,"michal2");
+
+		return -EAGAIN;
+	}
+	spare_blocks = cc33xx_get_spare_blocks(wl, is_gem);
+
+	/* allocate free identifier for the packet */
+	id = cc33xx_alloc_tx_id(wl, skb);
+	if (id < 0)
+	{
+	    cc33xx_debug(DEBUG_TX,"michal3");
+		return id;
+	}
+
+	/* memblocks should not include nab descriptor */
+	total_blocks = cc33xx_calc_tx_blocks(wl, total_skb_len, spare_blocks);
+	cc33xx_debug(DEBUG_TX,"michal1 total blocks %d", total_blocks);
+
+	if (total_blocks <= wl->tx_blocks_available) {
+	   
+	    // In CC33XX the packet starts with NAB command, only then the descriptor.
+
+	    nab_cmd->sync = cpu_to_le32(HOST_SYNC_PATTERN);
+	    nab_cmd->opcode = cpu_to_le16(NAB_SEND_CMD);
+	    nab_cmd->len = cpu_to_le16(total_len - sizeof(struct NAB_header)); // length should include the following 4 bytes of the NAB comand.
+	    nab_cmd->desc_length = cpu_to_le16(total_len - sizeof(struct NAB_tx_header));
+	    nab_cmd->sd = 0;
+	    nab_cmd->flags = NAB_SEND_FLAGS;
+
+	    desc = skb_push(skb, total_skb_len - skb->len);	  
+
+		cc33xx_set_tx_desc_blocks(wl, desc, total_blocks, spare_blocks);
+
+		desc->id = id;
+
+		cc33xx_debug(DEBUG_TX, "tx alocate id %u skb 0x%p tx_memblocks %d",
+		             id, skb,desc->cc33xx_mem.total_mem_blocks);
+
+		wl->tx_blocks_available -= total_blocks;
+		wl->tx_allocated_blocks += total_blocks;
+
+		/*
+		 * If the FW was empty before, arm the Tx watchdog. Also do
+		 * this on the first Tx after resume, as we always cancel the
+		 * watchdog on suspend.
+		 */
+		if (wl->tx_allocated_blocks == total_blocks ||
+		    test_and_clear_bit(CC33XX_FLAG_REINIT_TX_WDOG, &wl->flags))
+			cc33xx_rearm_tx_watchdog_locked(wl);
+
+		ac = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+		desc->ac = ac;
+		wl->tx_allocated_pkts[ac]++;
+
+		if (test_bit(hlid, wl->links_map))
+			wl->links[hlid].allocated_pkts++;
+
+		ret = 0;
+
+		cc33xx_debug(DEBUG_TX,
+			     "tx_allocate: size: %d, blocks: %d, id: %d",
+			     total_len, total_blocks, id);
+	} else {
+	    cc33xx_debug(DEBUG_TX,"michal4");
+		cc33xx_free_tx_id(wl, id);
+	}
+
+	return ret;
+}
+
+static void cc33xx_tx_fill_hdr(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			       struct sk_buff *skb, u32 extra,
+			       struct ieee80211_tx_info *control, u8 hlid)
+{
+	struct cc33xx_tx_hw_descr *desc;
+	int ac, rate_idx;
+	s64 hosttime;
+	u16 tx_attr = 0;
+	__le16 frame_control;
+	struct ieee80211_hdr *hdr;
+	u8 *frame_start;
+	bool is_dummy;
+
+
+	desc = (struct cc33xx_tx_hw_descr *) skb->data;
+
+	frame_start = (u8 *)(desc + 1);
+	hdr = (struct ieee80211_hdr *)(frame_start + extra);
+	frame_control = hdr->frame_control;
+
+	/* relocate space for security header */
+	if (extra) {
+		int hdrlen = ieee80211_hdrlen(frame_control);
+		memmove(frame_start, hdr, hdrlen);
+		skb_set_network_header(skb, skb_network_offset(skb) + extra);
+	}
+
+	/* configure packet life time */
+	hosttime = (ktime_get_boottime_ns() >> 10);
+	// michal temp removal
+	//desc->start_time = cpu_to_le32(hosttime - wl->time_offset);
+
+	is_dummy = cc33xx_is_dummy_packet(wl, skb);
+	if (is_dummy || !wlvif || wlvif->bss_type != BSS_TYPE_AP_BSS)
+		desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
+	else
+		desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);
+
+	/* queue */
+	ac = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+	desc->tid = skb->priority;
+
+	if (is_dummy) {
+		/*
+		 * FW expects the dummy packet to have an invalid session id -
+		 * any session id that is different than the one set in the join
+		 */
+		tx_attr = (SESSION_COUNTER_INVALID <<
+			   TX_HW_ATTR_OFST_SESSION_COUNTER) &
+			   TX_HW_ATTR_SESSION_COUNTER;
+
+		tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
+	} else if (wlvif) {
+		u8 session_id = wl->session_ids[hlid];
+
+		if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) &&
+		    (wlvif->bss_type == BSS_TYPE_AP_BSS))
+			session_id = 0;
+
+		/* configure the tx attributes */
+		tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER;
+	}
+
+	desc->hlid = hlid;
+	if (is_dummy || !wlvif)
+		rate_idx = 0;
+	else if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
+		/*
+		 * if the packets are data packets
+		 * send them with AP rate policies (EAPOLs are an exception),
+		 * otherwise use default basic rates
+		 */
+		if (skb->protocol == cpu_to_be16(ETH_P_PAE))
+			rate_idx = wlvif->sta.basic_rate_idx;
+		else if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+			rate_idx = wlvif->sta.p2p_rate_idx;
+		else if (ieee80211_is_data(frame_control))
+			rate_idx = wlvif->sta.ap_rate_idx;
+		else
+			rate_idx = wlvif->sta.basic_rate_idx;
+	} else {
+		if (hlid == wlvif->ap.global_hlid)
+			rate_idx = wlvif->ap.mgmt_rate_idx;
+		else if (hlid == wlvif->ap.bcast_hlid ||
+			 skb->protocol == cpu_to_be16(ETH_P_PAE) ||
+			 !ieee80211_is_data(frame_control))
+			/*
+			 * send non-data, bcast and EAPOLs using the
+			 * min basic rate
+			 */
+			rate_idx = wlvif->ap.bcast_rate_idx;
+		else
+			rate_idx = wlvif->ap.ucast_rate_idx[ac];
+	}
+
+	tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
+
+	/* for WEP shared auth - no fw encryption is needed */
+	if (ieee80211_is_auth(frame_control) &&
+	    ieee80211_has_protected(frame_control))
+		tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
+
+	/* send EAPOL frames as voice */
+	if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)
+		tx_attr |= TX_HW_ATTR_EAPOL_FRAME;
+
+	desc->tx_attr = cpu_to_le16(tx_attr);
+
+
+	cc33xx_set_tx_desc_data_len(wl, desc, skb);
+}
+
+/* caller must hold wl->mutex */
+static int cc33xx_prepare_tx_frame(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				   struct sk_buff *skb, u32 buf_offset, u8 hlid)
+{
+	struct ieee80211_tx_info *info;
+	u32 extra = 0;
+	int ret = 0;
+	u32 total_len;
+	bool is_dummy;
+	bool is_gem = false;
+	struct NAB_tx_header nab_cmd;
+
+	if (!skb) {
+		cc33xx_error("discarding null skb");
+		return -EINVAL;
+	}
+
+	if (hlid == CC33XX_INVALID_LINK_ID) {
+		cc33xx_error("invalid hlid. dropping skb 0x%p", skb);
+		return -EINVAL;
+	}
+
+	info = IEEE80211_SKB_CB(skb);
+
+	is_dummy = cc33xx_is_dummy_packet(wl, skb);
+
+	if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+	    info->control.hw_key &&
+	    info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+		extra = CC33XX_EXTRA_SPACE_TKIP;
+
+	if (info->control.hw_key) {
+		bool is_wep;
+		u8 idx = info->control.hw_key->hw_key_idx;
+		u32 cipher = info->control.hw_key->cipher;
+
+		is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
+			 (cipher == WLAN_CIPHER_SUITE_WEP104);
+
+		if (WARN_ON(is_wep && wlvif && wlvif->default_key != idx)) {
+			ret = cc33xx_set_default_wep_key(wl, wlvif, idx);
+			if (ret < 0)
+				return ret;
+			wlvif->default_key = idx;
+		}
+
+		is_gem = (cipher == CC33XX_CIPHER_SUITE_GEM);
+	}
+	extra += IEEE80211_HT_CTL_LEN; // Add 4 bytes gap, may be filled later on by the PMAC.
+	ret = cc33xx_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
+				 is_gem, &nab_cmd);
+	cc33xx_debug(DEBUG_TX, "cc33xx_tx_allocate %d", ret);
+
+	if (ret < 0)
+		return ret;
+
+	cc33xx_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid);
+
+	cc33xx_debug(DEBUG_TX, "cc33xx_tx_fill_hdr ");
+
+	if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
+		cc33xx_tx_ap_update_inconnection_sta(wl, wlvif, skb);
+		cc33xx_tx_regulate_link(wl, wlvif, hlid);
+	}
+
+	/*
+	 * The length of each packet is stored in terms of
+	 * words. Thus, we must pad the skb data to make sure its
+	 * length is aligned.  The number of padding bytes is computed
+	 * and set in cc33xx_tx_fill_hdr.
+	 * In special cases, we want to align to a specific block size
+	 * (eg. for wl128x with SDIO we align to 256).
+	 */
+	total_len = wlcore_calc_packet_alignment(wl, skb->len);
+	cc33xx_debug(DEBUG_TX, "wlcore_calc_packet_alignment ");
+
+	memcpy(wl->aggr_buf + buf_offset, &nab_cmd, sizeof(struct NAB_tx_header));
+	memcpy(wl->aggr_buf + buf_offset + sizeof(struct NAB_tx_header), skb->data, skb->len);
+	memset(wl->aggr_buf + buf_offset + sizeof(struct NAB_tx_header) + skb->len, 0, total_len - skb->len);
+
+	/* Revert side effects in the dummy packet skb, so it can be reused */
+	if (is_dummy)
+		skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr));
+
+	return (total_len + sizeof(struct NAB_tx_header));
+}
+
+u32 cc33xx_tx_enabled_rates_get(struct cc33xx *wl, u32 rate_set,
+				enum nl80211_band rate_band)
+{
+	struct ieee80211_supported_band *band;
+	u32 enabled_rates = 0;
+	int bit;
+
+	band = wl->hw->wiphy->bands[rate_band];
+	for (bit = 0; bit < band->n_bitrates; bit++) {
+		if (rate_set & 0x1)
+			enabled_rates |= band->bitrates[bit].hw_value;
+		rate_set >>= 1;
+	}
+
+	/* MCS rates indication are on bits 16 - 31 */
+	rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
+
+	for (bit = 0; bit < 16; bit++) {
+		if (rate_set & 0x1)
+			enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
+		rate_set >>= 1;
+	}
+
+	return enabled_rates;
+}
+
+void cc33xx_handle_tx_low_watermark(struct cc33xx *wl)
+{
+	int i;
+	struct cc33xx_vif *wlvif;
+
+	cc33xx_for_each_wlvif(wl, wlvif) {
+		for (i = 0; i < NUM_TX_QUEUES; i++) {
+			if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i,
+					WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
+			    wlvif->tx_queue_count[i] <=
+					CC33XX_TX_QUEUE_LOW_WATERMARK)
+				/* firmware buffer has space, restart queues */
+				wlcore_wake_queue(wl, wlvif, i,
+					WLCORE_QUEUE_STOP_REASON_WATERMARK);
+		}
+	}
+}
+
+static int wlcore_select_ac(struct cc33xx *wl)
+{
+	int i, q = -1, ac;
+	u32 min_pkts = 0xffffffff;
+
+	/*
+	 * Find a non-empty ac where:
+	 * 1. There are packets to transmit
+	 * 2. The FW has the least allocated blocks
+	 *
+	 * We prioritize the ACs according to VO>VI>BE>BK
+	 */
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		ac = cc33xx_tx_get_queue(i);
+		if (wl->tx_queue_count[ac] &&
+		    wl->tx_allocated_pkts[ac] < min_pkts) {
+			q = ac;
+			min_pkts = wl->tx_allocated_pkts[q];
+		}
+	}
+
+	return q;
+}
+
+static struct sk_buff *wlcore_lnk_dequeue(struct cc33xx *wl,
+					  struct cc33xx_link *lnk, u8 q)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	skb = skb_dequeue(&lnk->tx_queue[q]);
+	if (skb) {
+		spin_lock_irqsave(&wl->wl_lock, flags);
+		WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
+		wl->tx_queue_count[q]--;
+		if (lnk->wlvif) {
+			WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
+			lnk->wlvif->tx_queue_count[q]--;
+		}
+		spin_unlock_irqrestore(&wl->wl_lock, flags);
+	}
+
+	return skb;
+}
+
+static bool cc33xx_lnk_high_prio(struct cc33xx *wl, u8 hlid,
+				 struct cc33xx_link *lnk)
+{
+
+	u8 thold;
+	struct core_fw_status * core_fw_status = &wl->core_status->fwInfo;
+	unsigned long suspend_bitmap, fast_bitmap, ps_bitmap;
+
+	suspend_bitmap = le32_to_cpu(core_fw_status->link_suspend_bitmap);
+	fast_bitmap = le32_to_cpu(core_fw_status->link_fast_bitmap);
+	ps_bitmap = le32_to_cpu(core_fw_status->link_ps_bitmap);
+
+	/* suspended links are never high priority */
+	if (test_bit(hlid, &suspend_bitmap))
+		return false;
+
+	/* the priority thresholds are taken from FW */
+	if (test_bit(hlid, &fast_bitmap) &&
+		!test_bit(hlid, &ps_bitmap))
+		thold = core_fw_status->tx_fast_link_prio_threshold;
+	else
+		thold = core_fw_status->tx_slow_link_prio_threshold;
+
+	return lnk->allocated_pkts < thold;
+}
+
+static bool cc33xx_lnk_low_prio(struct cc33xx *wl, u8 hlid,
+				struct cc33xx_link *lnk)
+{
+	u8 thold;
+	struct core_fw_status *core_fw_status = &wl->core_status->fwInfo;
+	unsigned long suspend_bitmap, fast_bitmap, ps_bitmap;
+
+	suspend_bitmap = le32_to_cpu(core_fw_status->link_suspend_bitmap);
+	fast_bitmap = le32_to_cpu(core_fw_status->link_fast_bitmap);
+	ps_bitmap = le32_to_cpu(core_fw_status->link_ps_bitmap);
+
+	if (test_bit(hlid, &suspend_bitmap))
+		thold = core_fw_status->tx_suspend_threshold;
+	else if (test_bit(hlid, &fast_bitmap) &&
+		 !test_bit(hlid, &ps_bitmap))
+		thold = core_fw_status->tx_fast_stop_threshold;
+	else
+		thold = core_fw_status->tx_slow_stop_threshold;
+
+	return lnk->allocated_pkts < thold;
+}
+
+static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct cc33xx *wl,
+						    u8 hlid, u8 ac,
+						    u8 *low_prio_hlid)
+{
+	struct cc33xx_link *lnk = &wl->links[hlid];
+
+	if (!cc33xx_lnk_high_prio(wl, hlid, lnk)) {
+		if (*low_prio_hlid == CC33XX_INVALID_LINK_ID &&
+		    !skb_queue_empty(&lnk->tx_queue[ac]) &&
+		    cc33xx_lnk_low_prio(wl, hlid, lnk))
+			/* we found the first non-empty low priority queue */
+			*low_prio_hlid = hlid;
+
+		return NULL;
+	}
+
+	return wlcore_lnk_dequeue(wl, lnk, ac);
+}
+
+static struct sk_buff *wlcore_vif_dequeue_high_prio(struct cc33xx *wl,
+						    struct cc33xx_vif *wlvif,
+						    u8 ac, u8 *hlid,
+						    u8 *low_prio_hlid)
+{
+	struct sk_buff *skb = NULL;
+	int i, h, start_hlid;
+
+	/* start from the link after the last one */
+	start_hlid = (wlvif->last_tx_hlid + 1) % wl->num_links;
+
+	/* dequeue according to AC, round robin on each link */
+	for (i = 0; i < wl->num_links; i++) {
+		h = (start_hlid + i) % wl->num_links;
+
+		/* only consider connected stations */
+		if (!test_bit(h, wlvif->links_map))
+			continue;
+
+		skb = wlcore_lnk_dequeue_high_prio(wl, h, ac,
+						   low_prio_hlid);
+		if (!skb)
+			continue;
+
+		wlvif->last_tx_hlid = h;
+		break;
+	}
+
+	if (!skb)
+		wlvif->last_tx_hlid = 0;
+
+	*hlid = wlvif->last_tx_hlid;
+	return skb;
+}
+
+static struct sk_buff *cc33xx_skb_dequeue(struct cc33xx *wl, u8 *hlid)
+{
+	unsigned long flags;
+	struct cc33xx_vif *wlvif = wl->last_wlvif;
+	struct sk_buff *skb = NULL;
+	int ac;
+	u8 low_prio_hlid = CC33XX_INVALID_LINK_ID;
+
+	ac = wlcore_select_ac(wl);
+	if (ac < 0)
+		goto out;
+
+	/* continue from last wlvif (round robin) */
+	if (wlvif) {
+		cc33xx_for_each_wlvif_continue(wl, wlvif) {
+			if (!wlvif->tx_queue_count[ac])
+				continue;
+
+			skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+							   &low_prio_hlid);
+			if (!skb)
+				continue;
+
+			wl->last_wlvif = wlvif;
+			break;
+		}
+	}
+
+	/* dequeue from the system HLID before the restarting wlvif list */
+	if (!skb) {
+		skb = wlcore_lnk_dequeue_high_prio(wl, CC33XX_SYSTEM_HLID,
+						   ac, &low_prio_hlid);
+		if (skb) {
+			*hlid = CC33XX_SYSTEM_HLID;
+			wl->last_wlvif = NULL;
+		}
+	}
+
+	/* Do a new pass over the wlvif list. But no need to continue
+	 * after last_wlvif. The previous pass should have found it. */
+	if (!skb) {
+		cc33xx_for_each_wlvif(wl, wlvif) {
+			if (!wlvif->tx_queue_count[ac])
+				goto next;
+
+			skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+							   &low_prio_hlid);
+			if (skb) {
+				wl->last_wlvif = wlvif;
+				break;
+			}
+
+next:
+			if (wlvif == wl->last_wlvif)
+				break;
+		}
+	}
+
+	/* no high priority skbs found - but maybe a low priority one? */
+	if (!skb && low_prio_hlid != CC33XX_INVALID_LINK_ID) {
+		struct cc33xx_link *lnk = &wl->links[low_prio_hlid];
+		skb = wlcore_lnk_dequeue(wl, lnk, ac);
+
+		WARN_ON(!skb); /* we checked this before */
+		*hlid = low_prio_hlid;
+
+		/* ensure proper round robin in the vif/link levels */
+		wl->last_wlvif = lnk->wlvif;
+		if (lnk->wlvif)
+			lnk->wlvif->last_tx_hlid = low_prio_hlid;
+
+	}
+
+out:
+	if (!skb &&
+	    test_and_clear_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
+		int q;
+
+		skb = wl->dummy_packet;
+		*hlid = CC33XX_SYSTEM_HLID;
+		q = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+		spin_lock_irqsave(&wl->wl_lock, flags);
+		WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
+		wl->tx_queue_count[q]--;
+		spin_unlock_irqrestore(&wl->wl_lock, flags);
+	}
+
+	return skb;
+}
+
+static void cc33xx_skb_queue_head(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				  struct sk_buff *skb, u8 hlid)
+{
+	unsigned long flags;
+	int q = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+
+	if (cc33xx_is_dummy_packet(wl, skb)) {
+		set_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
+	} else {
+		skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
+
+		/* make sure we dequeue the same packet next time */
+		wlvif->last_tx_hlid = (hlid + wl->num_links - 1) %
+				      wl->num_links;
+	}
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	wl->tx_queue_count[q]++;
+	if (wlvif)
+		wlvif->tx_queue_count[q]++;
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+static bool cc33xx_tx_is_data_present(struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
+
+	return ieee80211_is_data_present(hdr->frame_control);
+}
+
+void cc33xx_rearm_rx_streaming(struct cc33xx *wl, unsigned long *active_hlids)
+{
+	struct cc33xx_vif *wlvif;
+	u32 timeout;
+	u8 hlid;
+
+	if (!wl->conf.host_conf.rx_streaming.interval)
+		return;
+
+	if (!wl->conf.host_conf.rx_streaming.always &&
+	    !test_bit(CC33XX_FLAG_SOFT_GEMINI, &wl->flags))
+		return;
+
+	timeout = wl->conf.host_conf.rx_streaming.duration;
+	cc33xx_for_each_wlvif_sta(wl, wlvif) {
+		bool found = false;
+		for_each_set_bit(hlid, active_hlids, wl->num_links) {
+			if (test_bit(hlid, wlvif->links_map)) {
+				found  = true;
+				break;
+			}
+		}
+
+		if (!found)
+			continue;
+
+		/* enable rx streaming */
+		if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
+			ieee80211_queue_work(wl->hw,
+					     &wlvif->rx_streaming_enable_work);
+
+		mod_timer(&wlvif->rx_streaming_timer,
+			  jiffies + msecs_to_jiffies(timeout));
+	}
+}
+
+/*
+ * Returns failure values only in case of failed bus ops within this function.
+ * cc33xx_prepare_tx_frame retvals won't be returned in order to avoid
+ * triggering recovery by higher layers when not necessary.
+ * In case a FW command fails within cc33xx_prepare_tx_frame fails a recovery
+ * will be queued in cc33xx_cmd_send. -EAGAIN/-EBUSY from prepare_tx_frame
+ * can occur and are legitimate so don't propagate. -EINVAL will emit a WARNING
+ * within prepare_tx_frame code but there's nothing we should do about those
+ * as well.
+ */
+int wlcore_tx_work_locked(struct cc33xx *wl)
+{
+	struct cc33xx_vif *wlvif;
+	struct sk_buff *skb;
+	struct cc33xx_tx_hw_descr *desc;
+	u32 buf_offset = 0, last_len = 0;
+	u32 transfer_len = 0;
+	u32 padding_size = 0;
+	bool sent_packets = false;
+	unsigned long active_hlids[BITS_TO_LONGS(CC33XX_MAX_LINKS)] = {0};
+	int ret = 0;
+	int bus_ret = 0;
+	u8 hlid;
+
+
+
+	cc33xx_debug(DEBUG_TX, " Tx work locked");
+
+	memset(wl->aggr_buf,0,0x300);
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		return 0;
+
+	while ((skb = cc33xx_skb_dequeue(wl, &hlid))) {
+		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+		bool has_data = false;
+
+		cc33xx_debug(DEBUG_TX, " skb dequeue skb: 0x%p data %#lx head %#lx tail %#lx end %#lx", skb, (unsigned long)skb->data, (unsigned long)skb->head, (unsigned long)skb->tail, (unsigned long)skb->end);
+		wlvif = NULL;
+		if (!cc33xx_is_dummy_packet(wl, skb))
+		{
+			wlvif = cc33xx_vif_to_data(info->control.vif);
+		}
+		else
+		{
+			hlid = CC33XX_SYSTEM_HLID;
+		}
+
+		has_data = wlvif && cc33xx_tx_is_data_present(skb);
+		ret = cc33xx_prepare_tx_frame(wl, wlvif, skb, buf_offset,
+					      hlid);
+
+		if (ret == -EAGAIN) {
+			/*
+			 * Aggregation buffer is full.
+			 * Flush buffer and try again.
+			 */
+			cc33xx_skb_queue_head(wl, wlvif, skb, hlid);
+
+			transfer_len = __ALIGN_MASK(buf_offset, 
+						CC33XX_BUS_BLOCK_SIZE*2 - 1);
+
+			padding_size = transfer_len - buf_offset;
+			memset(wl->aggr_buf + buf_offset, 0x33, padding_size);
+
+			cc33xx_debug(DEBUG_TX, "sdio transaction length: %d ",
+					transfer_len);
+
+			bus_ret = wlcore_write(wl, NAB_DATA_ADDR, wl->aggr_buf,
+			                       transfer_len, true);
+			if (bus_ret < 0)
+				goto out;
+
+			sent_packets = true;
+			buf_offset = 0;
+			continue;
+		} else if (ret == -EBUSY) {
+			/*
+			 * Firmware buffer is full.
+			 * Queue back last skb, and stop aggregating.
+			 */
+			cc33xx_skb_queue_head(wl, wlvif, skb, hlid);
+			/* No work left, avoid scheduling redundant tx work */
+			set_bit(CC33XX_FLAG_FW_TX_BUSY, &wl->flags);
+			goto out_ack;
+		} else if (ret < 0) {
+			if (cc33xx_is_dummy_packet(wl, skb))
+				/*
+				 * fw still expects dummy packet,
+				 * so re-enqueue it
+				 */
+				cc33xx_skb_queue_head(wl, wlvif, skb, hlid);
+			else
+				ieee80211_free_txskb(wl->hw, skb);
+			goto out_ack;
+		}
+		last_len = ret;
+		buf_offset += last_len;
+		
+		if (has_data) {
+			desc = (struct cc33xx_tx_hw_descr *) skb->data;
+			__set_bit(desc->hlid, active_hlids);
+		}
+	}
+
+out_ack:
+	if (buf_offset) {
+	
+		transfer_len = __ALIGN_MASK(buf_offset, 
+						CC33XX_BUS_BLOCK_SIZE*2 - 1);
+
+		padding_size = transfer_len - buf_offset;
+		memset(wl->aggr_buf + buf_offset, 0x33, padding_size);
+
+		cc33xx_debug(DEBUG_TX, "sdio transaction (926) length: %d ",
+			transfer_len);
+
+		bus_ret = wlcore_write(wl, NAB_DATA_ADDR, wl->aggr_buf,
+		                       transfer_len, true); 
+		if (bus_ret < 0)
+			goto out;
+
+		sent_packets = true;
+	}
+	if (sent_packets) 
+		cc33xx_handle_tx_low_watermark(wl);
+	
+	cc33xx_rearm_rx_streaming(wl, active_hlids);
+
+out:  
+	return bus_ret;
+}
+
+void cc33xx_tx_work(struct work_struct *work)
+{
+	struct cc33xx *wl = container_of(work, struct cc33xx, tx_work);
+	int ret;
+
+	mutex_lock(&wl->mutex);
+
+	ret = wlcore_tx_work_locked(wl);
+	if (ret < 0) {
+		cc33xx_queue_recovery_work(wl);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+void cc33xx_tx_reset_link_queues(struct cc33xx *wl, u8 hlid)
+{
+	struct sk_buff *skb;
+	int i;
+	unsigned long flags;
+	struct ieee80211_tx_info *info;
+	int total[NUM_TX_QUEUES];
+	struct cc33xx_link *lnk = &wl->links[hlid];
+
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		total[i] = 0;
+		while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
+			cc33xx_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
+
+			if (!cc33xx_is_dummy_packet(wl, skb)) {
+				info = IEEE80211_SKB_CB(skb);
+				info->status.rates[0].idx = -1;
+				info->status.rates[0].count = 0;
+				ieee80211_tx_status_ni(wl->hw, skb);
+			}
+
+			total[i]++;
+		}
+	}
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		wl->tx_queue_count[i] -= total[i];
+		if (lnk->wlvif)
+			lnk->wlvif->tx_queue_count[i] -= total[i];
+	}
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	cc33xx_handle_tx_low_watermark(wl);
+}
+
+/* caller must hold wl->mutex and TX must be stopped */
+void cc33xx_tx_reset_wlvif(struct cc33xx *wl, struct cc33xx_vif *wlvif)
+{
+	int i;
+
+	/* TX failure */
+	for_each_set_bit(i, wlvif->links_map, wl->num_links) {
+		if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+		    i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) {
+			/* this calls cc33xx_clear_link */
+			cc33xx_free_sta(wl, wlvif, i);
+		} else {
+			u8 hlid = i;
+			cc33xx_clear_link(wl, wlvif, &hlid);
+		}
+	}
+	wlvif->last_tx_hlid = 0;
+
+	for (i = 0; i < NUM_TX_QUEUES; i++)
+		wlvif->tx_queue_count[i] = 0;
+}
+/* caller must hold wl->mutex and TX must be stopped */
+void cc33xx_tx_reset(struct cc33xx *wl)
+{
+	int i;
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+
+	/* only reset the queues if something bad happened */
+	if (cc33xx_tx_total_queue_count(wl) != 0) {
+		for (i = 0; i < wl->num_links; i++)
+			cc33xx_tx_reset_link_queues(wl, i);
+
+		for (i = 0; i < NUM_TX_QUEUES; i++)
+			wl->tx_queue_count[i] = 0;
+	}
+
+	/*
+	 * Make sure the driver is at a consistent state, in case this
+	 * function is called from a context other than interface removal.
+	 * This call will always wake the TX queues.
+	 */
+	cc33xx_handle_tx_low_watermark(wl);
+
+	for (i = 0; i < wl->num_tx_desc; i++) {
+		if (wl->tx_frames[i] == NULL)
+			continue;
+
+		skb = wl->tx_frames[i];
+		cc33xx_free_tx_id(wl, i);
+		cc33xx_debug(DEBUG_TX, "freeing skb 0x%p", skb);
+
+		if (!cc33xx_is_dummy_packet(wl, skb)) {
+			/*
+			 * Remove private headers before passing the skb to
+			 * mac80211
+			 */
+			info = IEEE80211_SKB_CB(skb);
+			skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr));
+			if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+			    info->control.hw_key &&
+			    info->control.hw_key->cipher ==
+			    WLAN_CIPHER_SUITE_TKIP) {
+				int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+				memmove(skb->data + CC33XX_EXTRA_SPACE_TKIP,
+					skb->data, hdrlen);
+				skb_pull(skb, CC33XX_EXTRA_SPACE_TKIP);
+			}
+
+			info->status.rates[0].idx = -1;
+			info->status.rates[0].count = 0;
+
+			ieee80211_tx_status_ni(wl->hw, skb);
+		}
+	}
+}
+
+#define CC33XX_TX_FLUSH_TIMEOUT 500000
+
+/* caller must *NOT* hold wl->mutex */
+void cc33xx_tx_flush(struct cc33xx *wl)
+{
+	unsigned long timeout, start_time;
+	int i;
+	start_time = jiffies;
+	timeout = start_time + usecs_to_jiffies(CC33XX_TX_FLUSH_TIMEOUT);
+
+	/* only one flush should be in progress, for consistent queue state */
+	mutex_lock(&wl->flush_mutex);
+
+	mutex_lock(&wl->mutex);
+	if (wl->tx_frames_cnt == 0 && cc33xx_tx_total_queue_count(wl) == 0) {
+		mutex_unlock(&wl->mutex);
+		goto out;
+	}
+
+	wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+
+	while (!time_after(jiffies, timeout)) {
+		cc33xx_debug(DEBUG_MAC80211, "flushing tx buffer: %d %d",
+			     wl->tx_frames_cnt,
+			     cc33xx_tx_total_queue_count(wl));
+
+		/* force Tx and give the driver some time to flush data */
+		mutex_unlock(&wl->mutex);
+		if (cc33xx_tx_total_queue_count(wl))
+			cc33xx_tx_work(&wl->tx_work);
+		msleep(20);
+		mutex_lock(&wl->mutex);
+
+		if ((wl->tx_frames_cnt == 0) &&
+		    (cc33xx_tx_total_queue_count(wl) == 0)) {
+			cc33xx_debug(DEBUG_MAC80211, "tx flush took %d ms",
+				     jiffies_to_msecs(jiffies - start_time));
+			goto out_wake;
+		}
+	}
+
+	cc33xx_warning("Unable to flush all TX buffers, "
+		       "timed out (timeout %d ms",
+		       CC33XX_TX_FLUSH_TIMEOUT / 1000);
+
+	/* forcibly flush all Tx buffers on our queues */
+	for (i = 0; i < wl->num_links; i++)
+		cc33xx_tx_reset_link_queues(wl, i);
+
+out_wake:
+	wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+	mutex_unlock(&wl->mutex);
+out:
+	mutex_unlock(&wl->flush_mutex);
+}
+
+u32 cc33xx_tx_min_rate_get(struct cc33xx *wl, u32 rate_set)
+{
+	if (WARN_ON(!rate_set))
+		return 0;
+
+	return BIT(__ffs(rate_set));
+}
+
+void wlcore_stop_queue_locked(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      u8 queue, enum wlcore_queue_stop_reason reason)
+{
+	int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+	bool stopped = !!wl->queue_stop_reasons[hwq];
+
+	/* queue should not be stopped for this reason */
+	WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq]));
+
+	if (stopped)
+		return;
+
+	ieee80211_stop_queue(wl->hw, hwq);
+}
+
+void wlcore_stop_queue(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 queue,
+		       enum wlcore_queue_stop_reason reason)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	wlcore_stop_queue_locked(wl, wlvif, queue, reason);
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_wake_queue(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 queue,
+		       enum wlcore_queue_stop_reason reason)
+{
+	unsigned long flags;
+	int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+
+	/* queue should not be clear for this reason */
+	WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq]));
+
+	if (wl->queue_stop_reasons[hwq])
+		goto out;
+
+	ieee80211_wake_queue(wl->hw, hwq);
+
+out:
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_stop_queues(struct cc33xx *wl,
+			enum wlcore_queue_stop_reason reason)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+
+	/* mark all possible queues as stopped */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+                WARN_ON_ONCE(test_and_set_bit(reason,
+					      &wl->queue_stop_reasons[i]));
+
+	/* use the global version to make sure all vifs in mac80211 we don't
+	 * know are stopped.
+	 */
+	ieee80211_stop_queues(wl->hw);
+
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_wake_queues(struct cc33xx *wl,
+			enum wlcore_queue_stop_reason reason)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+
+	/* mark all possible queues as awake */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+		WARN_ON_ONCE(!test_and_clear_bit(reason,
+						 &wl->queue_stop_reasons[i]));
+
+	/* use the global version to make sure all vifs in mac80211 we don't
+	 * know are woken up.
+	 */
+	ieee80211_wake_queues(wl->hw);
+
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+bool wlcore_is_queue_stopped_by_reason(struct cc33xx *wl,
+				       struct cc33xx_vif *wlvif, u8 queue,
+				       enum wlcore_queue_stop_reason reason)
+{
+	unsigned long flags;
+	bool stopped;
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue,
+							   reason);
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	return stopped;
+}
+
+bool wlcore_is_queue_stopped_by_reason_locked(struct cc33xx *wl,
+				       struct cc33xx_vif *wlvif, u8 queue,
+				       enum wlcore_queue_stop_reason reason)
+{
+	int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+	assert_spin_locked(&wl->wl_lock);
+	return test_bit(reason, &wl->queue_stop_reasons[hwq]);
+}
+
+bool wlcore_is_queue_stopped_locked(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				    u8 queue)
+{
+	int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+	assert_spin_locked(&wl->wl_lock);
+	return !!wl->queue_stop_reasons[hwq];
+}
+
+static void cc33xx_tx_complete_packet(struct cc33xx *wl, u8 tx_stat_byte,struct core_fw_status *pCoreFwStatus)
+{
+	struct ieee80211_tx_info *info;
+	struct sk_buff *skb;
+	int id = tx_stat_byte & CC33XX_TX_STATUS_DESC_ID_MASK;
+	bool tx_success;
+	struct cc33xx_tx_hw_descr *tx_desc;
+	u16 desc_session_idx;
+					       
+
+	/* check for id legality */
+	if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) {
+		cc33xx_warning("illegal id in tx completion: %d", id);
+		
+		print_hex_dump(KERN_DEBUG, "fwInfo local:",
+		            DUMP_PREFIX_OFFSET, 16, 4,
+		            (u8*)(pCoreFwStatus),
+		            sizeof (struct core_fw_status), false);
+			    
+		cc33xx_queue_recovery_work(wl);
+		return;
+	}
+
+	/* a zero bit indicates Tx success */
+	tx_success = !(tx_stat_byte & BIT(CC33XX_TX_STATUS_STAT_BIT_IDX));
+
+	skb = wl->tx_frames[id];
+	info = IEEE80211_SKB_CB(skb);
+	tx_desc = (struct cc33xx_tx_hw_descr *)skb->data;
+
+	if (cc33xx_is_dummy_packet(wl, skb)) {
+		cc33xx_free_tx_id(wl, id);
+		return;
+	}
+
+	/* update the TX status info */
+	if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
+		info->flags |= IEEE80211_TX_STAT_ACK;
+
+	/* todo - michal - do we need to habdle rates feedback? why? how? */
+
+	info->status.rates[0].count = 1; /* no data about retries */
+	info->status.ack_signal = -1;
+
+	if (!tx_success)
+		wl->stats.retry_count++;
+
+	/*
+	 * TODO: update sequence number for encryption? seems to be
+	 * unsupported for now. needed for recovery with encryption.
+	 */
+	/* todo michal - should we fix header ? should we remove the space we added for the ht header? */
+	/* remove private header from packet */
+	skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr));
+
+	/* remove TKIP header space if present */
+	if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+	    info->control.hw_key &&
+	    info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+		int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+		memmove(skb->data + CC33XX_EXTRA_SPACE_TKIP, skb->data, hdrlen);
+		skb_pull(skb, CC33XX_EXTRA_SPACE_TKIP);
+	}
+
+	cc33xx_debug(DEBUG_TX, "tx status id %u skb 0x%p success %d, tx_memblocks %d",
+		     id, skb, tx_success,tx_desc->cc33xx_mem.total_mem_blocks);
+
+
+	/* in order to update the memory managemetn we should have total_blocks, ac, and hlid */
+	/* update memory managemetn variables - michal michal michal*/
+	wl->tx_blocks_available += tx_desc->cc33xx_mem.total_mem_blocks;
+	wl->tx_allocated_blocks -= tx_desc->cc33xx_mem.total_mem_blocks;
+	// per queue
+
+     	/* prevent wrap-around in freed-packets counter */
+	wl->tx_allocated_pkts[tx_desc->ac]--;
+
+	/* per link */
+	desc_session_idx = (tx_desc->tx_attr & TX_HW_ATTR_SESSION_COUNTER) >> TX_HW_ATTR_OFST_SESSION_COUNTER;
+	if (wl->session_ids[tx_desc->hlid] == desc_session_idx) {
+		wl->links[tx_desc->hlid].allocated_pkts--;
+	}
+
+	cc33xx_free_tx_id(wl, id);
+
+	/* new mem blocks are available now */
+	clear_bit(CC33XX_FLAG_FW_TX_BUSY, &wl->flags);
+
+	/* return the packet to the stack */
+	skb_queue_tail(&wl->deferred_tx_queue, skb);
+	queue_work(wl->freezable_wq, &wl->netstack_work);
+
+}
+
+void cc33xx_tx_immediate_complete(struct cc33xx *wl)
+{
+	u8 txResultQueueIndex;
+	struct core_fw_status coreFwStatus;
+	u8 i;
+
+	claim_core_status_lock(wl);
+	memcpy(&coreFwStatus,&wl->core_status->fwInfo,sizeof(struct core_fw_status));
+
+	txResultQueueIndex = wl->core_status->fwInfo.txResultQueueIndex;
+	/* Lock guarantees we shadow txResultQueueIndex NOT during 
+	an active transaction. Subsequent references to fwInfo can be done
+	without locking as long we do not pass this index. */
+	release_core_status_lock(wl);
+
+	cc33xx_debug(DEBUG_TX, "last released desc = %d, current idx = %d",
+	             wl->last_fw_rls_idx, txResultQueueIndex);
+
+	/* nothing to do here */
+	if (wl->last_fw_rls_idx == txResultQueueIndex)
+		return;
+
+	/* freed Tx descriptors */
+
+	if (txResultQueueIndex >= TX_RESULT_QUEUE_SIZE) {
+		cc33xx_error("invalid desc release index %d",
+		             txResultQueueIndex);
+		WARN_ON(1);
+		return;
+	}
+
+	cc33xx_debug(DEBUG_TX, "TX result queue! priv last fw idx %d, current resut index %d ",wl->last_fw_rls_idx, txResultQueueIndex);
+	for (i = wl->last_fw_rls_idx;
+	     i != txResultQueueIndex;
+	     i = (i + 1) % TX_RESULT_QUEUE_SIZE) {
+		cc33xx_tx_complete_packet(wl,
+		                          coreFwStatus.txResultQueue[i],&coreFwStatus);
+		                          //wl->core_status->fwInfo.txResultQueue[i],&coreFwStatus);
+
+
+	}
+
+	wl->last_fw_rls_idx = txResultQueueIndex;
+}
+
+
diff --git drivers/net/wireless/ti/cc33xx/tx.h drivers/net/wireless/ti/cc33xx/tx.h
new file mode 100644
index 000000000000..20b8bb5fe555
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/tx.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __TX_H__
+#define __TX_H__
+
+
+
+
+
+#define CC33XX_TX_HW_BLOCK_SPARE        1
+/* for special cases - namely, TKIP and GEM */
+#define CC33XX_TX_HW_EXTRA_BLOCK_SPARE  2
+#define CC33XX_TX_HW_BLOCK_SIZE         256
+
+#define CC33XX_TX_STATUS_DESC_ID_MASK    0x7F
+#define CC33XX_TX_STATUS_STAT_BIT_IDX    7
+
+/* Indicates this TX HW frame is not padded to SDIO block size */
+#define CC33XX_TX_CTRL_NOT_PADDED	BIT(7)
+
+/*
+ * The FW uses a special bit to indicate a wide channel should be used in
+ * the rate policy.
+ */
+#define CONF_TX_RATE_USE_WIDE_CHAN BIT(31)
+
+#define TX_HW_MGMT_PKT_LIFETIME_TU       2000
+#define TX_HW_AP_MODE_PKT_LIFETIME_TU    8000
+
+#define TX_HW_ATTR_SAVE_RETRIES          BIT(0)
+#define TX_HW_ATTR_HEADER_PAD            BIT(1)
+#define TX_HW_ATTR_SESSION_COUNTER       (BIT(2) | BIT(3) | BIT(4))
+#define TX_HW_ATTR_RATE_POLICY           (BIT(5) | BIT(6) | BIT(7) | \
+					  BIT(8) | BIT(9))
+#define TX_HW_ATTR_LAST_WORD_PAD         (BIT(10) | BIT(11))
+#define TX_HW_ATTR_TX_CMPLT_REQ          BIT(12)
+#define TX_HW_ATTR_TX_DUMMY_REQ          BIT(13)
+#define TX_HW_ATTR_HOST_ENCRYPT          BIT(14)
+#define TX_HW_ATTR_EAPOL_FRAME           BIT(15)
+
+#define TX_HW_ATTR_OFST_SAVE_RETRIES     0
+#define TX_HW_ATTR_OFST_HEADER_PAD       1
+#define TX_HW_ATTR_OFST_SESSION_COUNTER  2
+#define TX_HW_ATTR_OFST_RATE_POLICY      5
+#define TX_HW_ATTR_OFST_LAST_WORD_PAD    10
+#define TX_HW_ATTR_OFST_TX_CMPLT_REQ     12
+
+#define TX_HW_RESULT_QUEUE_LEN           16
+#define TX_HW_RESULT_QUEUE_LEN_MASK      0xf
+
+#define CC33XX_TX_ALIGN_TO 4
+#define CC33XX_EXTRA_SPACE_TKIP 4
+#define CC33XX_EXTRA_SPACE_AES  8
+#define CC33XX_EXTRA_SPACE_MAX  8
+
+#define CC33XX_TX_EXTRA_HEADROOM (sizeof(struct cc33xx_tx_hw_descr) + IEEE80211_HT_CTL_LEN)
+
+/* Used for management frames and dummy packets */
+#define CC33XX_TID_MGMT 7
+
+/* stop a ROC for pending authentication reply after this time (ms) */
+#define WLCORE_PEND_AUTH_ROC_TIMEOUT     1000
+#define CC33xx_PEND_ROC_COMPLETE_TIMEOUT 2000 
+
+struct cc33xx_tx_mem {
+	/*
+	 * Total number of memory blocks allocated by the host for
+	 * this packet.
+	 */
+	u8 total_mem_blocks;
+
+	/*
+	 * control bits
+	 */
+	u8 ctrl;
+} __packed;
+
+/*
+ * On cc33xx based devices, when TX packets are aggregated, each packet
+ * size must be aligned to the SDIO block size. The maximum block size
+ * is bounded by the type of the padded bytes field that is sent to the
+ * FW. Currently the type is u8, so the maximum block size is 256 bytes.
+ */
+// For CC33xx O3
+#define CC33XX_BUS_BLOCK_SIZE 128
+
+struct cc33xx_tx_hw_descr {
+	/* Length of packet in words, including descriptor+header+data */
+	__le16 length;
+	
+	struct cc33xx_tx_mem cc33xx_mem;
+	// michal temp removal - need four bytes for ax header
+
+	/* Packet identifier used also in the Tx-Result. */
+	u8 id;
+	/* The packet TID value (as User-Priority) */
+	u8 tid;
+	/* host link ID (HLID) */
+	u8 hlid;
+	u8  ac;
+	/*
+	* Max delay in TUs until transmission. The last device time the
+	* packet can be transmitted is: start_time + (1024 * life_time)
+	*/
+	__le16 life_time;
+	/* Bitwise fields - see TX_ATTR... definitions above. */
+	__le16 tx_attr;
+} __packed;
+
+enum cc33xx_tx_hw_res_status {
+	TX_SUCCESS          = 0,
+	TX_HW_ERROR         = 1,
+	TX_DISABLED         = 2,
+	TX_RETRY_EXCEEDED   = 3,
+	TX_TIMEOUT          = 4,
+	TX_KEY_NOT_FOUND    = 5,
+	TX_PEER_NOT_FOUND   = 6,
+	TX_SESSION_MISMATCH = 7,
+	TX_LINK_NOT_VALID   = 8,
+};
+
+struct cc33xx_tx_hw_res_descr {
+	/* Packet Identifier - same value used in the Tx descriptor.*/
+	u8 id;
+	/* The status of the transmission, indicating success or one of
+	   several possible reasons for failure. */
+	u8 status;
+	/* Total air access duration including all retrys and overheads.*/
+	__le16 medium_usage;
+	/* The time passed from host xfer to Tx-complete.*/
+	__le32 fw_handling_time;
+	/* Total media delay
+	   (from 1st EDCA AIFS counter until TX Complete). */
+	__le32 medium_delay;
+	/* LS-byte of last TKIP seq-num (saved per AC for recovery). */
+	u8 tx_security_sequence_number_lsb;
+	/* Retry count - number of transmissions without successful ACK.*/
+	u8 ack_failures;
+	/* The rate that succeeded getting ACK
+	   (Valid only if status=SUCCESS). */
+	u8 rate_class_index;
+	/* for 4-byte alignment. */
+	u8 spare;
+} __packed;
+
+
+
+struct cc33xx_tx_hw_res_if {
+	__le32 tx_result_fw_counter;
+	__le32 tx_result_host_counter;
+	struct cc33xx_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN];
+} __packed;
+
+enum wlcore_queue_stop_reason {
+	WLCORE_QUEUE_STOP_REASON_WATERMARK,
+	WLCORE_QUEUE_STOP_REASON_FW_RESTART,
+	WLCORE_QUEUE_STOP_REASON_FLUSH,
+	WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */
+};
+
+static inline int cc33xx_tx_get_queue(int queue)
+{
+	switch (queue) {
+	case 0:
+		return CONF_TX_AC_VO;
+	case 1:
+		return CONF_TX_AC_VI;
+	case 2:
+		return CONF_TX_AC_BE;
+	case 3:
+		return CONF_TX_AC_BK;
+	default:
+		return CONF_TX_AC_BE;
+	}
+}
+
+static inline
+int wlcore_tx_get_mac80211_queue(struct cc33xx_vif *wlvif, int queue)
+{
+	int mac_queue = wlvif->hw_queue_base;
+
+	switch (queue) {
+	case CONF_TX_AC_VO:
+		return mac_queue + 0;
+	case CONF_TX_AC_VI:
+		return mac_queue + 1;
+	case CONF_TX_AC_BE:
+		return mac_queue + 2;
+	case CONF_TX_AC_BK:
+		return mac_queue + 3;
+	default:
+		return mac_queue + 2;
+	}
+}
+
+static inline int cc33xx_tx_total_queue_count(struct cc33xx *wl)
+{
+	int i, count = 0;
+
+	for (i = 0; i < NUM_TX_QUEUES; i++)
+		count += wl->tx_queue_count[i];
+
+	return count;
+}
+
+
+void cc33xx_tx_immediate_complete(struct cc33xx *wl);
+void cc33xx_tx_work(struct work_struct *work);
+int wlcore_tx_work_locked(struct cc33xx *wl);
+void cc33xx_tx_reset_wlvif(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+void cc33xx_tx_reset(struct cc33xx *wl);
+void cc33xx_tx_flush(struct cc33xx *wl);
+u8 wlcore_rate_to_idx(struct cc33xx *wl, u8 rate, enum nl80211_band band);
+u32 cc33xx_tx_enabled_rates_get(struct cc33xx *wl, u32 rate_set,
+				enum nl80211_band rate_band);
+u32 cc33xx_tx_min_rate_get(struct cc33xx *wl, u32 rate_set);
+u8 cc33xx_tx_get_hlid(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+		      struct sk_buff *skb, struct ieee80211_sta *sta);
+void cc33xx_tx_reset_link_queues(struct cc33xx *wl, u8 hlid);
+void cc33xx_handle_tx_low_watermark(struct cc33xx *wl);
+bool cc33xx_is_dummy_packet(struct cc33xx *wl, struct sk_buff *skb);
+void cc33xx_rearm_rx_streaming(struct cc33xx *wl, unsigned long *active_hlids);
+unsigned int wlcore_calc_packet_alignment(struct cc33xx *wl,
+					  unsigned int packet_length);
+void cc33xx_free_tx_id(struct cc33xx *wl, int id);
+void wlcore_stop_queue_locked(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      u8 queue, enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queue(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 queue,
+		       enum wlcore_queue_stop_reason reason);
+void wlcore_wake_queue(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 queue,
+		       enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queues(struct cc33xx *wl,
+			enum wlcore_queue_stop_reason reason);
+void wlcore_wake_queues(struct cc33xx *wl,
+			enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped_by_reason(struct cc33xx *wl,
+				       struct cc33xx_vif *wlvif, u8 queue,
+				       enum wlcore_queue_stop_reason reason);
+bool
+wlcore_is_queue_stopped_by_reason_locked(struct cc33xx *wl,
+					 struct cc33xx_vif *wlvif,
+					 u8 queue,
+					 enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped_locked(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+				    u8 queue);
+
+/* from main.c */
+void cc33xx_free_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif, u8 hlid);
+void cc33xx_rearm_tx_watchdog_locked(struct cc33xx *wl);
+
+#endif
diff --git drivers/net/wireless/ti/cc33xx/wlcore.h drivers/net/wireless/ti/cc33xx/wlcore.h
new file mode 100644
index 000000000000..cd254ebe11e7
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/wlcore.h
@@ -0,0 +1,604 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ */
+
+#ifndef __WLCORE_H__
+#define __WLCORE_H__
+
+#include <linux/platform_device.h>
+
+#include "wlcore_i.h"
+#include "event.h"
+#include "boot.h"
+#include "rx.h"
+
+
+/* Wireless Driver Version */
+#define MAJOR_VERSION 	1
+#define MINOR_VERSION 	7
+#define API_VERSION 	0
+#define BUILD_VERSION	26
+
+
+/* The maximum number of Tx descriptors in all chip families */
+#define WLCORE_MAX_TX_DESCRIPTORS 32
+
+#define CC33XX_CMD_MAX_SIZE          (896)
+#define CC33XX_INI_PARAM_COMMAND_SIZE (16UL)//size of struct cc33xx_cmd_ini_params_download 
+#define CC33XX_INI_CMD_MAX_SIZE      (CC33X_CONF_SIZE + CC33XX_INI_PARAM_COMMAND_SIZE + sizeof(int))
+
+#define CC33XX_CMD_BUFFER_SIZE ((CC33XX_INI_CMD_MAX_SIZE > CC33XX_CMD_MAX_SIZE) ? CC33XX_INI_CMD_MAX_SIZE : CC33XX_CMD_MAX_SIZE)
+
+#define WLCORE_NUM_MAC_ADDRESSES 3
+
+/* Texas Instruments pre assigned OUI */
+#define WLCORE_TI_OUI_ADDRESS 0x080028
+
+#define CC33XX_AGGR_BUFFER_SIZE		(8 * PAGE_SIZE)
+
+#define CC33XX_NUM_TX_DESCRIPTORS 32
+#define CC33XX_NUM_RX_DESCRIPTORS 32
+
+#define CC33XX_RX_BA_MAX_SESSIONS 13
+
+#define CC33XX_MAX_AP_STATIONS 16
+
+/* forward declaration */
+struct cc33xx_tx_hw_descr;
+struct cc33xx_rx_descriptor;
+struct partial_rx_frame;
+struct core_fw_status;
+struct core_status;
+
+enum wl_rx_buf_align;
+
+struct driver_versions{
+	__u16 major_version;
+	__u16 minor_version;
+	__u16 api_version;
+	__u16 build_version;
+};
+
+struct driver_fw_versions{
+	struct driver_versions driver_ver;
+	struct cc33xx_acx_fw_versions *fw_ver;
+
+};
+
+struct cc33xx_stats {
+	void *fw_stats;
+	unsigned long fw_stats_update;
+	size_t fw_stats_len;
+
+	unsigned int retry_count;
+	unsigned int excessive_retries;
+};
+
+
+struct cc33xx {
+	bool initialized;
+	struct ieee80211_hw *hw;
+	bool mac80211_registered;
+
+	struct device *dev;
+	struct platform_device *pdev; 
+
+
+	struct cc33xx_if_operations *if_ops;
+
+	int wakeirq; 
+
+
+	spinlock_t wl_lock;
+
+	enum wlcore_state state;
+	bool plt;
+	enum plt_mode plt_mode;
+	u8 plt_role_id;
+	u8 fem_manuf;
+	u8 last_vif_count;
+	struct mutex mutex;
+	struct core_status *core_status;
+	u8 last_fw_rls_idx;
+	/* Temp O3 solution for transferring command results from IRQ 
+		to API-caller context. */
+	u8 command_result[CC33XX_CMD_MAX_SIZE];
+	u16 result_length;
+	struct partial_rx_frame partial_rx;
+
+	unsigned long flags;
+
+	void *nvs_mac_addr;
+	size_t nvs_mac_addr_len;
+	struct cc33xx_fw_download *fw_download;
+
+	struct mac_address addresses[WLCORE_NUM_MAC_ADDRESSES];
+
+	unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)];
+	unsigned long roles_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)];
+	unsigned long roc_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)];
+	unsigned long rate_policies_map[
+			BITS_TO_LONGS(CC33XX_MAX_RATE_POLICIES)];
+
+	u8 session_ids[CC33XX_MAX_LINKS];
+
+	struct list_head wlvif_list;
+
+	u8 sta_count;
+	u8 ap_count;
+
+	struct cc33xx_acx_mem_map *target_mem_map;
+
+	/* Accounting for allocated / available TX blocks on HW */
+	
+	u32 tx_blocks_available;
+	u32 tx_allocated_blocks;
+
+	/* Accounting for allocated / available Tx packets in HW */
+
+	
+	u32 tx_allocated_pkts[NUM_TX_QUEUES];
+
+
+	/* Time-offset between host and chipset clocks */
+	
+
+	/* Frames scheduled for transmission, not handled yet */
+	int tx_queue_count[NUM_TX_QUEUES];
+	unsigned long queue_stop_reasons[
+				NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
+
+	/* Frames received, not handled yet by mac80211 */
+	struct sk_buff_head deferred_rx_queue;
+
+	/* Frames sent, not returned yet to mac80211 */
+	struct sk_buff_head deferred_tx_queue;
+
+	struct work_struct tx_work;
+	struct workqueue_struct *freezable_wq;
+
+	/*freezable wq for netstack_work*/
+	struct workqueue_struct *freezable_netstack_wq;
+
+	/* Pending TX frames */
+	unsigned long tx_frames_map[BITS_TO_LONGS(WLCORE_MAX_TX_DESCRIPTORS)];
+	struct sk_buff *tx_frames[WLCORE_MAX_TX_DESCRIPTORS];
+	int tx_frames_cnt;
+
+	/* FW Rx counter */
+	u32 rx_counter;
+
+	/* Intermediate buffer, used for packet aggregation */
+	u8 *aggr_buf;
+	u32 aggr_buf_size;
+	size_t max_transaction_len;
+
+	/* Reusable dummy packet template */
+	struct sk_buff *dummy_packet;
+
+	/* Network stack work  */
+	struct work_struct netstack_work;
+	/* FW log buffer */
+	u8 *fwlog; 
+
+	/* Number of valid bytes in the FW log buffer */
+	ssize_t fwlog_size;
+
+	/* Hardware recovery work */
+	struct work_struct recovery_work;
+
+	struct work_struct irq_deferred_work;
+
+	/* Reg domain last configuration */
+	DECLARE_BITMAP(reg_ch_conf_last, 64);
+	/* Reg domain pending configuration */
+	DECLARE_BITMAP(reg_ch_conf_pending, 64);
+
+	/* Lock-less list for deferred event handling */
+	struct llist_head event_list;
+	/* The mbox event mask */
+	u32 event_mask;
+	/* events to unmask only when ap interface is up */
+	u32 ap_event_mask;
+
+	/* Are we currently scanning */
+	struct cc33xx_vif *scan_wlvif;
+	struct cc33xx_scan scan;
+	struct delayed_work scan_complete_work;
+
+	struct ieee80211_vif *roc_vif;
+	struct delayed_work roc_complete_work;
+
+	struct cc33xx_vif *sched_vif;
+
+	u8 mac80211_scan_stopped;
+
+	/* The current band */
+	enum nl80211_band band;
+
+	/* in dBm */
+	int power_level;
+
+	struct cc33xx_stats stats;
+
+	__le32 *buffer_32;
+
+	/* Current chipset configuration */
+	struct cc33xx_conf_file conf;
+
+	bool enable_11a;
+
+	/* bands supported by this instance of cc33xx */
+	struct ieee80211_supported_band bands[WLCORE_NUM_BANDS];
+
+	/*
+	 * wowlan trigger was configured during suspend.
+	 * (currently, only "ANY" trigger is supported)
+	 */
+	bool keep_device_power;
+
+	/*
+	 * AP-mode - links indexed by HLID. The global and broadcast links
+	 * are always active.
+	 */
+	struct cc33xx_link links[CC33XX_MAX_LINKS];
+
+	/* number of currently active links */
+	int active_link_count;
+
+	/* AP-mode - a bitmap of links currently in PS mode according to FW */
+	unsigned long ap_fw_ps_map;
+
+	/* AP-mode - a bitmap of links currently in PS mode in mac80211 */
+	unsigned long ap_ps_map;
+
+	/* Quirks of specific hardware revisions */
+	unsigned int quirks;
+
+	/* number of currently active RX BA sessions */
+	int ba_rx_session_count;
+
+	/* Maximum number of supported RX BA sessions */
+	int ba_rx_session_count_max;
+
+	/* AP-mode - number of currently connected stations */
+	int active_sta_count;
+
+	/* last wlvif we transmitted from */
+	struct cc33xx_vif *last_wlvif;
+
+	/* work to fire when Tx is stuck */
+	struct delayed_work tx_watchdog_work;
+
+	u8 scan_templ_id_2_4;
+	u8 scan_templ_id_5;
+	u8 sched_scan_templ_id_2_4;
+	u8 sched_scan_templ_id_5;
+	u8 max_channels_5;
+
+	/* number of TX descriptors the HW supports. */
+	u32 num_tx_desc;
+	/* number of RX descriptors the HW supports. */
+	u32 num_rx_desc;
+	/* number of links the HW supports */
+	u8 num_links;
+	/* max stations a single AP can support */
+	u8 max_ap_stations;
+
+	/* translate HW Tx rates to standard rate-indices */
+	const u8 **band_rate_to_idx;
+
+	/* size of table for HW rates that can be received from chip */
+	u8 hw_tx_rate_tbl_size;
+
+	/* HW HT (11n) capabilities */
+	struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
+
+	/* the current dfs region */
+	enum nl80211_dfs_regions dfs_region;
+	bool radar_debug_mode;
+
+	/* RX Data filter rule state - enabled/disabled */
+	unsigned long rx_filter_enabled[BITS_TO_LONGS(CC33XX_MAX_RX_FILTERS)];//used in CONFIG PM AND W8 Code
+
+	/* mutex for protecting the tx_flush function */
+	struct mutex flush_mutex;
+
+	/* sleep auth value currently configured to FW */
+	int sleep_auth;
+
+	/*ble_enable value - if 0 ble not enabled , if 1 is enabled..cant be disabled after enable*/
+	int ble_enable;
+
+	/* sta role index - if 0 - wlan0 primary station interface, if 1 - wlan2 - secondary station interface*/
+
+	u8 sta_role_idx;
+
+	u16 max_cmd_size;
+
+	struct completion nvs_loading_complete;
+	struct completion command_complete;
+
+	/* interface combinations supported by the hw */
+	const struct ieee80211_iface_combination *iface_combinations;
+	u8 n_iface_combinations;
+
+	/* dynamic fw traces */
+	u32 dynamic_fw_traces;
+
+	/* buffer for sending commands to FW */
+	u8 cmd_buf[CC33XX_CMD_BUFFER_SIZE];
+
+	/* number of keys requiring extra spare mem-blocks */
+	int extra_spare_key_count;
+
+	u8 efuse_mac_address[ETH_ALEN];
+
+	u32 fuse_rom_structure_version;
+	u32 device_part_number;
+	u32 pg_version;
+	u8	disable_5g;
+	u8 	disable_6g;
+	
+	struct driver_fw_versions all_versions;
+
+	char* all_versions_str;
+
+	u8 antenna_selection;
+
+	/* burst mode cfg */
+	u8 burst_disable;
+
+};
+
+int wlcore_probe(struct cc33xx *wl, struct platform_device *pdev);
+int wlcore_remove(struct platform_device *pdev);
+struct ieee80211_hw *wlcore_alloc_hw(u32 aggr_buf_size);
+int wlcore_free_hw(struct cc33xx *wl);
+int wlcore_set_key(struct cc33xx *wl, enum set_key_cmd cmd,
+		   struct ieee80211_vif *vif,
+		   struct ieee80211_sta *sta,
+		   struct ieee80211_key_conf *key_conf);
+void wlcore_regdomain_config(struct cc33xx *wl);
+void wlcore_update_inconn_sta(struct cc33xx *wl, struct cc33xx_vif *wlvif,
+			      struct cc33xx_station *wl_sta, bool in_conn);
+bool cc33xx_is_mimo_supported(struct cc33xx *wl);
+
+static inline void
+wlcore_set_ht_cap(struct cc33xx *wl, enum nl80211_band band,
+		  struct ieee80211_sta_ht_cap *ht_cap)
+{
+	memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
+}
+
+/* Tell wlcore not to care about this element when checking the version */
+#define WLCORE_FW_VER_IGNORE	-1
+
+
+/* Firmware image load chunk size */
+#define CHUNK_SIZE	16384
+
+/* Quirks */
+
+/* Each RX/TX transaction requires an end-of-transaction transfer */
+#define WLCORE_QUIRK_END_OF_TRANSACTION		BIT(0)
+
+/* the first start_role(sta) sometimes doesn't work on wl12xx */
+#define WLCORE_QUIRK_START_STA_FAILS		BIT(1)
+
+/* wl127x and SPI don't support SDIO block size alignment */
+#define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN		BIT(2)
+
+/* means aggregated Rx packets are aligned to a SDIO block */
+#define WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN		BIT(3)
+
+/* Older firmwares did not implement the FW logger over bus feature */
+#define WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED	BIT(4)
+
+/* Older firmwares use an old NVS format */
+#define WLCORE_QUIRK_LEGACY_NVS			BIT(5)
+
+/* pad only the last frame in the aggregate buffer */
+#define WLCORE_QUIRK_TX_PAD_LAST_FRAME		BIT(7)
+
+/* extra header space is required for TKIP */
+#define WLCORE_QUIRK_TKIP_HEADER_SPACE		BIT(8)
+
+/* Some firmwares not support sched scans while connected */
+#define WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN	BIT(9)
+
+/* separate probe response templates for one-shot and sched scans */
+#define WLCORE_QUIRK_DUAL_PROBE_TMPL		BIT(10)
+
+/* Firmware requires reg domain configuration for active calibration */
+#define WLCORE_QUIRK_REGDOMAIN_CONF		BIT(11)
+
+/* The FW only support a zero session id for AP */
+#define WLCORE_QUIRK_AP_ZERO_SESSION_ID		BIT(12)
+
+/* TODO: move all these common registers and values elsewhere */
+#define HW_ACCESS_ELP_CTRL_REG		0x1FFFC
+
+/* ELP register commands */
+#define ELPCTRL_WAKE_UP             0x1
+#define ELPCTRL_WAKE_UP_WLAN_READY  0x5
+#define ELPCTRL_SLEEP               0x0
+/* ELP WLAN_READY bit */
+#define ELPCTRL_WLAN_READY          0x2
+
+/*************************************************************************
+
+    Interrupt Trigger Register (Host -> WiLink)
+
+**************************************************************************/
+
+/* Hardware to Embedded CPU Interrupts - first 32-bit register set */
+
+/*
+ * The host sets this bit to inform the Wlan
+ * FW that a TX packet is in the XFER
+ * Buffer #0.
+ */
+#define INTR_TRIG_TX_PROC0 BIT(2)
+
+/*
+ * The host sets this bit to inform the FW
+ * that it read a packet from RX XFER
+ * Buffer #0.
+ */
+#define INTR_TRIG_RX_PROC0 BIT(3)
+
+#define INTR_TRIG_DEBUG_ACK BIT(4)
+
+#define INTR_TRIG_STATE_CHANGED BIT(5)
+
+/* Hardware to Embedded CPU Interrupts - second 32-bit register set */
+
+/*
+ * The host sets this bit to inform the FW
+ * that it read a packet from RX XFER
+ * Buffer #1.
+ */
+#define INTR_TRIG_RX_PROC1 BIT(17)
+
+/*
+ * The host sets this bit to inform the Wlan
+ * hardware that a TX packet is in the XFER
+ * Buffer #1.
+ */
+#define INTR_TRIG_TX_PROC1 BIT(18)
+
+#define ACX_SLV_SOFT_RESET_BIT	BIT(1)
+#define SOFT_RESET_MAX_TIME	1000000
+#define SOFT_RESET_STALL_TIME	1000
+
+#define ECPU_CONTROL_HALT	0x00000101
+
+#define WELP_ARM_COMMAND_VAL	0x4
+
+enum CC33xx_FRAME_FORMAT {
+ CC33xx_B_SHORT = 0,
+ CC33xx_B_LONG,
+ CC33xx_LEGACY_OFDM,
+ CC33xx_HT_MF,
+ CC33xx_HT_GF,
+ CC33xx_HE_SU,
+ CC33xx_HE_MU,
+ CC33xx_HE_SU_ER,
+ CC33xx_HE_TB,
+ CC33xx_HE_TB_NDP_FB,
+ CC33xx_VHT
+};
+
+/* CC33xx HW Common Definitions */
+
+#define HOST_SYNC_PATTERN 	0x5C5C5C5C
+#define DEVICE_SYNC_PATTERN 0xABCDDCBA
+#define NAB_DATA_ADDR		0x0000BFF0
+#define NAB_CONTROL_ADDR	0x0000BFF8
+#define NAB_STATUS_ADDR		0x0000BFFC
+
+
+#define NAB_SEND_CMD        0x940d // 0x900D
+#define NAB_SEND_FLAGS      0x08
+#define CC33xx_INTERNAL_DESC_SIZE   200
+#define NAB_EXTRA_BYTES 4
+
+#define TX_RESULT_QUEUE_SIZE  108
+
+
+
+struct control_info_descriptor
+{
+    __le16 type 	:4;
+    __le16 length 	:12;
+};
+
+enum control_message_type
+{
+    CTRL_MSG_NONE = 0,
+    CTRL_MSG_EVENT = 1,
+    CTRL_MSG_COMMND_COMPLETE = 2
+};
+
+struct core_fw_status
+{
+    u8   txResultQueueIndex;
+    u8   reserved1[3];
+    u8   txResultQueue[TX_RESULT_QUEUE_SIZE];
+    __le32	link_ps_bitmap;                     /* A bitmap (where each bit represents a single HLID) to indicate PS/Active mode of the link */
+    __le32	link_fast_bitmap;                   /* A bitmap (where each bit represents a single HLID) to indicate if the station is in Fast mode */
+    __le32	link_suspend_bitmap;                /* A bitmap (where each bit represents a single HLID) to indicate if a links is suspended/aboout to be suspended*/
+    u8      TxFlowControlAcThreshold;           /* Host TX Flow Control descriptor per AC threshold */
+    u8      tx_ps_threshold;                    /* Host TX Flow Control descriptor PS link threshold */
+    u8      tx_suspend_threshold;               /* Host TX Flow Control descriptor Suspended link threshold */
+    u8      tx_slow_link_prio_threshold;        /* Host TX Flow Control descriptor Slow link threshold */
+    u8      tx_fast_link_prio_threshold;        /* Host TX Flow Control descriptor Fast link threshold */
+    u8      tx_slow_stop_threshold;             /* Host TX Flow Control descriptor Stop Slow link threshold */
+    u8      tx_fast_stop_threshold;             /* Host TX Flow Control descriptor Stop Fast link threshold */
+    u8      reserved2;
+    // Additional information can be added here
+} __packed;
+
+struct core_status {
+    //__le32 bloc_pad[92];
+    __le32 block_pad[28];
+    __le32 host_interrupt_status;
+    __le32 rx_status;
+    struct core_fw_status fwInfo;
+    __le32 tsf;
+} __packed;
+
+struct NAB_header{
+	__le32 sync_pattern;
+	__le16 opcode;
+	__le16 len;
+};
+
+/* rx_status lower bytes hold the rx byte count */
+#define RX_BYTE_COUNT_MASK 0xFFFF
+
+
+
+
+#define HINT_NEW_TX_RESULT						0x1
+#define HINT_COMMAND_COMPLETE 					0x2
+#define HINT_RX_DATA_PENDING 					0x4
+#define HINT_ROM_LOADER_INIT_COMPLETE 			0x8
+#define HINT_SECOND_LOADER_INIT_COMPLETE 		0x10
+#define HINT_FW_WAKEUP_COMPLETE 				0x20
+#define HINT_FW_INIT_COMPLETE  					0x40
+#define HINT_GENERAL_ERROR						0x80000000
+
+#define BOOT_TIME_INTERRUPTS (\
+	HINT_ROM_LOADER_INIT_COMPLETE    | \
+	HINT_SECOND_LOADER_INIT_COMPLETE | \
+	HINT_FW_WAKEUP_COMPLETE | \
+	HINT_FW_INIT_COMPLETE )
+
+struct NAB_tx_header{
+    __le32 sync;
+    __le16 opcode;
+    __le16 len;
+    __le16 desc_length;
+    u8     sd;
+    u8     flags;
+} __packed;
+
+struct NAB_rx_header{
+    __le32 cnys;
+    __le16 opcode;
+    __le16 len;
+    __le32 rx_desc;
+    __le32 reserved;
+} __packed;
+
+
+
+
+
+
+#endif /* __WLCORE_H__ */
diff --git drivers/net/wireless/ti/cc33xx/wlcore_i.h drivers/net/wireless/ti/cc33xx/wlcore_i.h
new file mode 100644
index 000000000000..aabbfeaf20a5
--- /dev/null
+++ drivers/net/wireless/ti/cc33xx/wlcore_i.h
@@ -0,0 +1,511 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file is part of cc33xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ */
+
+#ifndef __WLCORE_I_H__
+#define __WLCORE_I_H__
+
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <net/mac80211.h>
+
+#include "conf.h"
+#include "ini.h"
+
+struct cc33xx_family_data {
+	const char *name;
+	const char *nvs_name;	/* nvs file */
+	const char *cfg_name;	/* cfg file */
+};
+
+
+#define CC33XX_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
+#define CC33XX_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
+#define CC33XX_TX_SQN_POST_RECOVERY_PADDING 0xff
+/* Use smaller padding for GEM, as some  APs have issues when it's too big */
+#define CC33XX_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20
+
+
+#define CC33XX_CIPHER_SUITE_GEM 0x00147201
+
+#define CC33XX_BUSY_WORD_LEN (sizeof(u32))
+
+#define CC33XX_ELP_HW_STATE_ASLEEP 0
+#define CC33XX_ELP_HW_STATE_IRQ    1
+
+#define CC33XX_DEFAULT_BEACON_INT  100
+#define CC33XX_DEFAULT_DTIM_PERIOD 1
+
+#define CC33XX_MAX_ROLES           4
+#define CC33XX_INVALID_ROLE_ID     0xff
+#define CC33XX_INVALID_LINK_ID     0xff
+
+#define CC33XX_MAX_LINKS 21
+
+
+/* the driver supports the 2.4Ghz and 5Ghz bands */
+#define WLCORE_NUM_BANDS           2
+
+#define CC33XX_MAX_RATE_POLICIES 16
+
+/* Defined by FW as 0. Will not be freed or allocated. */
+#define CC33XX_SYSTEM_HLID         0
+
+/*
+ * When in AP-mode, we allow (at least) this number of packets
+ * to be transmitted to FW for a STA in PS-mode. Only when packets are
+ * present in the FW buffers it will wake the sleeping STA. We want to put
+ * enough packets for the driver to transmit all of its buffered data before
+ * the STA goes to sleep again. But we don't want to take too much memory
+ * as it might hurt the throughput of active STAs.
+ */
+#define CC33XX_PS_STA_MAX_PACKETS  2
+
+#define CC33XX_AP_BSS_INDEX        0
+
+enum wlcore_state {
+	WLCORE_STATE_OFF,
+	WLCORE_STATE_RESTARTING,
+	WLCORE_STATE_ON,
+};
+
+struct cc33xx;
+
+enum {
+	FW_VER_CHIP,
+	FW_VER_IF_TYPE,
+	FW_VER_MAJOR,
+	FW_VER_SUBTYPE,
+	FW_VER_MINOR,
+
+	NUM_FW_VER
+};
+
+#define NUM_TX_QUEUES              4
+
+#define CC33XX_MAX_CHANNELS 64
+struct cc33xx_scan {
+	struct cfg80211_scan_request *req;
+	unsigned long scanned_ch[BITS_TO_LONGS(CC33XX_MAX_CHANNELS)];
+	bool failed;
+	u8 state;
+	u8 ssid[IEEE80211_MAX_SSID_LEN+1];
+	size_t ssid_len;
+};
+
+struct cc33xx_if_operations {
+	void (*interface_claim)(struct device *child);
+	void (*interface_release)(struct device *child);
+	int __must_check (*read)(struct device *child, int addr, void *buf,
+				 size_t len, bool fixed);
+	int __must_check (*write)(struct device *child, int addr, void *buf,
+				  size_t len, bool fixed);
+	void (*reset)(struct device *child);
+	void (*init)(struct device *child);
+	int (*power)(struct device *child, bool enable);
+	void (*set_block_size) (struct device *child, unsigned int blksz);
+	size_t (*get_max_transaction_len) (struct device *child);
+	void (*set_irq_handler) (struct device *child, void* irq_handler);
+	void (*enable_irq) (struct device *child);
+	void (*disable_irq) (struct device *child);
+};
+
+struct wlcore_platdev_data {
+	struct cc33xx_if_operations *if_ops;
+	const struct cc33xx_family_data *family;
+	void (*irq_handler)(struct platform_device *pdev);
+	int  gpio_irq_num;
+
+	bool ref_clock_xtal;	/* specify whether the clock is XTAL or not */
+	u32 ref_clock_freq;	/* in Hertz */
+	u32 tcxo_clock_freq;	/* in Hertz, tcxo is always XTAL */
+	bool pwr_in_suspend;
+};
+
+#define MAX_NUM_KEYS 14
+#define MAX_KEY_SIZE 32
+
+struct cc33xx_ap_key {
+	u8 id;
+	u8 key_type;
+	u8 key_size;
+	u8 key[MAX_KEY_SIZE];
+	u8 hlid;
+	u32 tx_seq_32;
+	u16 tx_seq_16;
+};
+
+enum cc33xx_flags {
+	CC33XX_FLAG_GPIO_POWER,
+	CC33XX_FLAG_TX_QUEUE_STOPPED,
+	CC33XX_FLAG_TX_PENDING,
+	CC33XX_FLAG_IN_ELP,
+	CC33XX_FLAG_IRQ_RUNNING,
+	CC33XX_FLAG_FW_TX_BUSY,
+	CC33XX_FLAG_DUMMY_PACKET_PENDING,
+	CC33XX_FLAG_SUSPENDED,
+	CC33XX_FLAG_PENDING_WORK,
+	CC33XX_FLAG_SOFT_GEMINI,
+	CC33XX_FLAG_DRIVER_REMOVED,
+	CC33XX_FLAG_RECOVERY_IN_PROGRESS,
+	CC33XX_FLAG_VIF_CHANGE_IN_PROGRESS,
+	CC33XX_FLAG_IO_FAILED,
+	CC33XX_FLAG_REINIT_TX_WDOG,
+};
+
+enum cc33xx_vif_flags {
+	WLVIF_FLAG_INITIALIZED,
+	WLVIF_FLAG_STA_ASSOCIATED,
+	WLVIF_FLAG_STA_AUTHORIZED,
+	WLVIF_FLAG_IBSS_JOINED,
+	WLVIF_FLAG_AP_STARTED,
+	WLVIF_FLAG_IN_PS,
+	WLVIF_FLAG_STA_STATE_SENT,
+	WLVIF_FLAG_RX_STREAMING_STARTED,
+	WLVIF_FLAG_PSPOLL_FAILURE,
+	WLVIF_FLAG_CS_PROGRESS,
+	WLVIF_FLAG_AP_PROBE_RESP_SET,
+	WLVIF_FLAG_IN_USE,
+	WLVIF_FLAG_ACTIVE,
+	WLVIF_FLAG_BEACON_DISABLED,
+};
+
+struct cc33xx_vif;
+
+struct cc33xx_link {
+	/* AP-mode - TX queue per AC in link */
+	struct sk_buff_head tx_queue[NUM_TX_QUEUES];
+
+	/* accounting for allocated / freed packets in FW */
+	u8 allocated_pkts;
+	u8 prev_freed_pkts;
+
+	u8 addr[ETH_ALEN];
+
+	/* bitmap of TIDs where RX BA sessions are active for this link */
+	u8 ba_bitmap;
+
+	/* the last fw rate index we used for this link */
+	u8 fw_rate_idx;
+
+	/* the last fw rate [Mbps] we used for this link */
+	u8 fw_rate_mbps;
+
+	/* The wlvif this link belongs to. Might be null for global links */
+	struct cc33xx_vif *wlvif;
+
+	/*
+	 * total freed FW packets on the link - used for tracking the
+	 * AES/TKIP PN across recoveries. Re-initialized each time
+	 * from the cc33xx_station structure.
+	 */
+	u64 total_freed_pkts;
+};
+
+#define CC33XX_MAX_RX_FILTERS 5
+#define CC33XX_RX_FILTER_MAX_FIELDS 8
+
+#define CC33XX_RX_FILTER_ETH_HEADER_SIZE 14
+#define CC33XX_RX_FILTER_MAX_FIELDS_SIZE 95
+#define RX_FILTER_FIELD_OVERHEAD				\
+	(sizeof(struct cc33xx_rx_filter_field) - sizeof(u8 *))
+#define CC33XX_RX_FILTER_MAX_PATTERN_SIZE			\
+	(CC33XX_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD)
+
+#define CC33XX_RX_FILTER_FLAG_MASK                BIT(0)
+#define CC33XX_RX_FILTER_FLAG_IP_HEADER           0
+#define CC33XX_RX_FILTER_FLAG_ETHERNET_HEADER     BIT(1)
+
+enum rx_filter_action {
+	FILTER_DROP = 0,
+	FILTER_SIGNAL = 1,
+	FILTER_FW_HANDLE = 2
+};
+
+enum plt_mode {
+	PLT_OFF = 0,
+	PLT_ON = 1,
+	PLT_FEM_DETECT = 2,
+	PLT_CHIP_AWAKE = 3
+};
+
+struct cc33xx_rx_filter_field {
+	__le16 offset;
+	u8 len;
+	u8 flags;
+	u8 *pattern;
+} __packed;
+
+struct cc33xx_rx_filter {
+	u8 action;
+	int num_fields;
+	struct cc33xx_rx_filter_field fields[CC33XX_RX_FILTER_MAX_FIELDS];
+};
+
+struct cc33xx_station {
+	u8 hlid;
+	bool in_connection;
+
+	/*
+	 * total freed FW packets on the link to the STA - used for tracking the
+	 * AES/TKIP PN across recoveries. Re-initialized each time from the
+	 * cc33xx_station structure.
+	 * Used in both AP and STA mode.
+	 */
+	u64 total_freed_pkts;
+};
+
+struct cc33xx_vif {
+	struct cc33xx *wl;
+	struct list_head list;
+	unsigned long flags;
+	u8 bss_type;
+	u8 p2p; /* we are using p2p role */
+	u8 role_id;
+
+	/* sta/ibss specific */
+	u8 dev_role_id;
+	u8 dev_hlid;
+
+	union {
+		struct {
+			u8 hlid;
+
+			u8 basic_rate_idx;
+			u8 ap_rate_idx;
+			u8 p2p_rate_idx;
+
+			bool qos;
+			/* channel type we started the STA role with */
+			enum nl80211_channel_type role_chan_type;
+		} sta;
+		struct {
+			u8 global_hlid;
+			u8 bcast_hlid;
+
+			/* HLIDs bitmap of associated stations */
+			unsigned long sta_hlid_map[BITS_TO_LONGS(
+							CC33XX_MAX_LINKS)];
+
+			/* recoreded keys - set here before AP startup */
+			struct cc33xx_ap_key *recorded_keys[MAX_NUM_KEYS];
+
+			u8 mgmt_rate_idx;
+			u8 bcast_rate_idx;
+			u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT];
+		} ap;
+	};
+
+	/* the hlid of the last transmitted skb */
+	int last_tx_hlid;
+
+	/* counters of packets per AC, across all links in the vif */
+	int tx_queue_count[NUM_TX_QUEUES];
+
+	unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)];
+
+	u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+	u8 ssid_len;
+
+	/* The current band */
+	enum nl80211_band band;
+	int channel;
+	enum nl80211_channel_type channel_type;
+
+	u32 bitrate_masks[WLCORE_NUM_BANDS];
+	u32 basic_rate_set;
+
+	/*
+	 * currently configured rate set:
+	 *	bits  0-15 - 802.11abg rates
+	 *	bits 16-23 - 802.11n   MCS index mask
+	 * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
+	 */
+	u32 basic_rate;
+	u32 rate_set;
+
+	/* probe-req template for the current AP */
+	struct sk_buff *probereq;
+
+	/* Beaconing interval (needed for ad-hoc) */
+	u32 beacon_int;
+
+	/* Default key (for WEP) */
+	u32 default_key;
+
+	/* Our association ID */
+	u16 aid;
+
+	/* retry counter for PSM entries */
+	u8 psm_entry_retry;
+
+	/* in dBm */
+	int power_level;
+
+	int rssi_thold;
+	int last_rssi_event;
+
+	/* save the current encryption type for auto-arp config */
+	u8 encryption_type;
+	__be32 ip_addr;
+
+	/* RX BA constraint value */
+	bool ba_support;
+	bool ba_allowed;
+
+	bool wmm_enabled;
+
+	bool radar_enabled;
+
+	/* Rx Streaming */
+	struct work_struct rx_streaming_enable_work;
+	struct work_struct rx_streaming_disable_work;
+	struct timer_list rx_streaming_timer;
+
+	struct delayed_work channel_switch_work;
+	struct delayed_work connection_loss_work;
+
+	/* number of in connection stations */
+	int inconn_count;
+
+	/*
+	 * This vif's queues are mapped to mac80211 HW queues as:
+	 * VO - hw_queue_base
+	 * VI - hw_queue_base + 1
+	 * BE - hw_queue_base + 2
+	 * BK - hw_queue_base + 3
+	 */
+	int hw_queue_base;
+
+	/* do we have a pending auth reply? (and ROC) */
+	bool ap_pending_auth_reply;
+
+	/* time when we sent the pending auth reply */
+	unsigned long pending_auth_reply_time;
+
+	/* work for canceling ROC after pending auth reply */
+	struct delayed_work pending_auth_complete_work;
+
+	struct delayed_work roc_timeout_work;
+
+	/* update rate conrol */
+	enum ieee80211_sta_rx_bandwidth rc_update_bw;
+	struct ieee80211_sta_ht_cap rc_ht_cap;
+	struct work_struct rc_update_work;
+
+	/*
+	 * total freed FW packets on the link.
+	 * For STA this holds the PN of the link to the AP.
+	 * For AP this holds the PN of the broadcast link.
+	 */
+	u64 total_freed_pkts;
+
+	/* for MBSSID: this BSS is a nontransmitted BSS profile */ // Katya
+	/* Relevant for STA role */
+	/* Consider to add under .sta*/
+	bool nontransmitted;
+
+	/* for MBSSID: update transmitter BSSID */
+	u8 transmitter_bssid[ETH_ALEN];
+
+	/* for MBSSID: BSSID index */
+	u8 bssid_index;
+
+	/* for MBSSID: BSSID indicator */
+	u8 bssid_indicator;
+
+	/* for STA: if connection established and has HE support*/
+	u8 sta_has_he;
+	
+	/*
+	 * This struct must be last!
+	 * data that has to be saved acrossed reconfigs (e.g. recovery)
+	 * should be declared in this struct.
+	 */
+	struct {
+		u8 persistent[0];
+	};
+};
+
+
+static inline struct cc33xx_vif *cc33xx_vif_to_data(struct ieee80211_vif *vif)
+{
+	WARN_ON(!vif);
+	return (struct cc33xx_vif *)vif->drv_priv;
+}
+
+static inline
+struct ieee80211_vif *cc33xx_wlvif_to_vif(struct cc33xx_vif *wlvif)
+{
+	return container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
+}
+
+static inline bool wlcore_is_p2p_mgmt(struct cc33xx_vif *wlvif)
+{
+	return cc33xx_wlvif_to_vif(wlvif)->type == NL80211_IFTYPE_P2P_DEVICE;
+}
+
+#define cc33xx_for_each_wlvif(wl, wlvif) \
+		list_for_each_entry(wlvif, &wl->wlvif_list, list)
+
+#define cc33xx_for_each_wlvif_continue(wl, wlvif) \
+		list_for_each_entry_continue(wlvif, &wl->wlvif_list, list)
+
+#define cc33xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type)	\
+		cc33xx_for_each_wlvif(wl, wlvif)		\
+			if (wlvif->bss_type == _bss_type)
+
+#define cc33xx_for_each_wlvif_sta(wl, wlvif)	\
+		cc33xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS)
+
+#define cc33xx_for_each_wlvif_ap(wl, wlvif)	\
+		cc33xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS)
+
+int cc33xx_plt_start(struct cc33xx *wl, const enum plt_mode plt_mode);
+int cc33xx_plt_stop(struct cc33xx *wl);
+int cc33xx_recalc_rx_streaming(struct cc33xx *wl, struct cc33xx_vif *wlvif);
+void cc33xx_queue_recovery_work(struct cc33xx *wl);
+size_t cc33xx_copy_fwlog(struct cc33xx *wl, u8 *memblock, size_t maxlen);
+int cc33xx_rx_filter_alloc_field(struct cc33xx_rx_filter *filter,
+				 u16 offset, u8 flags,
+				 const u8 *pattern, u8 len);
+void cc33xx_rx_filter_free(struct cc33xx_rx_filter *filter);
+struct cc33xx_rx_filter *cc33xx_rx_filter_alloc(void);
+int cc33xx_rx_filter_get_fields_size(struct cc33xx_rx_filter *filter);
+void cc33xx_rx_filter_flatten_fields(struct cc33xx_rx_filter *filter,
+				     u8 *buf);
+void cc33xx_flush_deferred_work(struct cc33xx *wl);
+
+#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
+
+#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */
+#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */
+
+#define CC33XX_MAX_TXPWR 21 /* maximum power limit is 21dBm */
+#define CC33XX_MIN_TXPWR -10 /* minmum power limit is -10dBm */
+
+#define CC33XX_TX_QUEUE_LOW_WATERMARK  32
+#define CC33XX_TX_QUEUE_HIGH_WATERMARK 256
+
+#define CC33XX_RX_QUEUE_MAX_LEN 256
+
+/* cc33xx needs a 200ms sleep after power on, and a 20ms sleep before power
+   on in case is has been shut down shortly before */
+#define CC33XX_PRE_POWER_ON_SLEEP 20 /* in milliseconds */
+#define CC33XX_POWER_ON_SLEEP 200 /* in milliseconds */
+
+/* Macros to handle cc33xx.sta_rate_set */
+#define HW_BG_RATES_MASK	0xffff
+#define HW_HT_RATES_OFFSET	16
+#define HW_MIMO_RATES_OFFSET	24
+
+#endif /* __WLCORE_I_H__ */
