Project import generated by Copybara.

GitOrigin-RevId: e8e9ed640313d07db9cdcb851f1c803d7f4ab277
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..2bb151e
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,72 @@
+###############################################################################
+# Copyright (c) 2016,2017 MediaTek Inc.
+#
+# 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+#
+###############################################################################
+
+###############################################################################
+# Generally Android.mk can not get KConfig setting
+# we can use this way to get
+# include the final KConfig
+# but there is no $(AUTO_CONF) at the first time (no out folder) when make
+#
+#ifneq (,$(wildcard $(AUTO_CONF)))
+#include $(AUTO_CONF)
+#include $(CLEAR_VARS)
+#endif
+
+###############################################################################
+###############################################################################
+# Generally Android.mk can not get KConfig setting                            #
+#                                                                             #
+# do not have any KConfig checking in Android.mk                              #
+# do not have any KConfig checking in Android.mk                              #
+# do not have any KConfig checking in Android.mk                              #
+#                                                                             #
+# e.g. ifeq ($(CONFIG_MTK_COMBO_WIFI), m)                                     #
+#          xxxx                                                               #
+#      endif                                                                  #
+#                                                                             #
+# e.g. ifneq ($(filter "MT6632",$(CONFIG_MTK_COMBO_CHIP)),)                   #
+#          xxxx                                                               #
+#      endif                                                                  #
+#                                                                             #
+# All the KConfig checking should move to Makefile for each module            #
+# All the KConfig checking should move to Makefile for each module            #
+# All the KConfig checking should move to Makefile for each module            #
+#                                                                             #
+###############################################################################
+###############################################################################
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(MTK_BT_SUPPORT),yes)
+ifneq ($(filter MTK_MT76%, $(MTK_BT_CHIP)),)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := btmtksdio.ko
+LOCAL_REQUIRED_MODULES := mtreset_mt7663.ko
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_OWNER := mtk
+LOCAL_INIT_RC := init.btmtksdio.rc
+
+include $(MTK_KERNEL_MODULE)
+
+ifeq ($(KERNEL_USE_BAZEL),yes)
+ko_init_rc_files := $(addprefix $(TARGET_OUT_VENDOR_ETC)/init/,$(LOCAL_INIT_RC))
+$(ko_init_rc_files): $(TARGET_OUT_VENDOR_ETC)/init/% : $(LOCAL_PATH)/% | $(ACP)
+	@echo "Copy: $@"
+	$(copy-file-to-target)
+ALL_DEFAULT_INSTALLED_MODULES += $(ko_init_rc_files)
+endif
+
+endif
+endif
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..5ca9ea6
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,39 @@
+load("//build/kernel/kleaf:kernel.bzl", "kernel_module")
+
+package(
+    default_visibility = [
+        "//visibility:public",
+    ],
+)
+
+kernel_module(
+    name = "btmtksdio",
+    srcs = glob([
+        "*.c",
+        "*.h",
+        "Kbuild",
+        "Makefile",
+    ]) + [
+        "//vendor/mediatek/kernel_modules/connectivity/wlan/core/gen4-mt7663:mt7663_headers",
+    ],
+    outs = [
+        "btmtksdio.ko",
+    ],
+    deps = [
+        "//kernel_device_modules-5.15:mgk_device_modules",
+        "//vendor/mediatek/kernel_modules/connectivity/wlan/core/gen4-mt7663:wlan_drv_gen4",
+    ],
+    kernel_build = "//kernel_device_modules-5.15:mgk_kernel_build",
+)
+
+BTMTKSDIO_INSMOD_CFG_FILES = [
+    "init.insmod.btmtksdio.cfg",
+]
+
+filegroup(
+    name = "btmtksdio_insmod_cfgs",
+    srcs = BTMTKSDIO_INSMOD_CFG_FILES,
+    visibility = [
+        "//kernel_device_modules-5.15:__pkg__",
+    ],
+)
diff --git a/Kbuild b/Kbuild
new file mode 100644
index 0000000..45b4810
--- /dev/null
+++ b/Kbuild
@@ -0,0 +1,37 @@
+###############################################################################
+# Bluetooth character device driver
+
+###############################################################################
+# Necessary Check
+
+#ifeq ($(AUTOCONF_H),)
+#    $(error AUTOCONF_H is not defined)
+#endif
+CONFIG_CHIP_RESET_KO_SUPPORT=y
+
+#
+# mtreset
+#
+ifeq ($(CONFIG_CHIP_RESET_KO_SUPPORT), y)
+ccflags-y += -DCFG_SUPPORT_CHIP_RESET_KO=1
+ccflags-y += -I$(srctree)/$(src)/../../../wlan/core/gen4-mt7663/reset/include
+else
+ccflags-y += -DCFG_SUPPORT_CHIP_RESET_KO=0
+endif
+
+#ccflags-y += -imacros $(AUTOCONF_H)
+ccflags-y += -DCONFIG_MP_WAKEUP_SOURCE_SYSFS_STAT
+
+MODULE_NAME := btmtksdio
+obj-m += $(MODULE_NAME).o
+
+#ccflags-y += -D CREATE_NODE_DYNAMIC=1
+
+ifneq ($(TARGET_BUILD_VARIANT), user)
+ccflags-y += -DCFG_ENABLE_DEBUG_WRITE=1
+else
+ccflags-y += -DCFG_ENABLE_DEBUG_WRITE=0
+endif
+
+$(MODULE_NAME)-objs += btmtk_sdio.o \
+                       btmtk_main.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3b61e41
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+###############################################################################
+# Bluetooth character device driver
+
+###############################################################################
+# Necessary Check
+include $(wildcard $(KERNEL_SRC)/$(DEVICE_MODULES_REL_DIR)/Makefile.include)
+
+EXTRA_SYMBOLS += $(OUT_DIR)/../vendor/mediatek/kernel_modules/connectivity/wlan/core/gen4-mt7663/Module.symvers
+
+modules modules_install clean:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" $(@)
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..89dfb23
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+wangedward@google.com
+hharte@google.com
diff --git a/btmtk_config.h b/btmtk_config.h
new file mode 100644
index 0000000..bb3df11
--- /dev/null
+++ b/btmtk_config.h
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2016 MediaTek Inc.
+ *
+ *  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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef __BTMTK_CONFIG_H__
+#define __BTMTK_CONFIG_H__
+
+#include <linux/version.h>
+
+/* It's for reset procedure */
+#include <linux/of_gpio.h>
+#include <linux/mmc/host.h>
+
+/**
+ * Kernel configuration check
+ */
+#ifndef CONFIG_PM
+	#error "ERROR : CONFIG_PM should be turn on."
+#endif
+
+/**
+ * Support IC configuration
+ */
+#define SUPPORT_MT7662 1
+#define SUPPORT_MT7668 1
+#define SUPPORT_MT7663 1
+
+/**
+ * BTMTK LOG location, last char must be '/'
+ */
+/* #define BTMTK_LOG_PATH	"/data/misc/bluedroid/" */
+
+#ifndef LOWER_POWER_SINK
+#define LOWER_POWER_SINK 0
+#endif
+
+/**
+ * Fixed STPBT Major Device Id
+ */
+#define FIXED_STPBT_MAJOR_DEV_ID 111
+
+/**
+ * WoBLE by BLE RC
+ */
+ /*Linux build fail due to wake_lock, please set SUPPORT_ANDROID 0 for Linux*/
+/*#define SUPPORT_ANDROID 0 */
+#define BT_RC_VENDOR_DEFAULT 1
+#define BT_RC_VENDOR_S0 0
+
+#define WAIT_POWERKEY_TIMEOUT 5000
+
+/**
+ * Support toggle GPIO
+ */
+#define MT76x8_PMU_EN_PIN_NAME		"mt76x8_pmu_en_gpio"
+#define MT76x8_PMU_EN_DELAY_NAME	"mt76x8_pmu_en_delay"
+#define MT76x8_PMU_EN_DEFAULT_DELAY	(5) /* Default delay 5ms */
+
+/**
+ * L0 reset
+ */
+#define L0_RESET_TAG				"[SER][L0] "
+
+#endif /* __BTMTK_CONFIG_H__ */
diff --git a/btmtk_define.h b/btmtk_define.h
new file mode 100644
index 0000000..f10128e
--- /dev/null
+++ b/btmtk_define.h
@@ -0,0 +1,198 @@
+/*
+ *  Copyright (c) 2016 MediaTek Inc.
+ *
+ *  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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef __BTMTK_DEFINE_H__
+#define __BTMTK_DEFINE_H__
+
+#include "btmtk_config.h"
+
+/**
+ * Type definition
+ */
+#ifndef TRUE
+	#define TRUE 1
+#endif
+#ifndef FALSE
+	#define FALSE 0
+#endif
+
+#ifndef UNUSED
+	#define UNUSED(x) (void)(x)
+#endif
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+/**
+ * Log level definition
+ */
+#define BTMTK_LOG_LEVEL_ERROR		1
+#define BTMTK_LOG_LEVEL_WARNING		2
+#define BTMTK_LOG_LEVEL_INFO		3
+#define BTMTK_LOG_LEVEL_DEBUG		4
+#define BTMTK_LOG_LEVEL_MAX		BTMTK_LOG_LEVEL_DEBUG
+#define BTMTK_LOG_LEVEL_DEFAULT		BTMTK_LOG_LEVEL_INFO	/* default setting */
+extern u8 btmtk_log_lvl;
+
+#define BTMTK_ERR(fmt, ...)     \
+	do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_ERROR)		\
+		pr_info("[btmtk_err] %s: "fmt"\n", __func__, ##__VA_ARGS__); } while (0)
+#define BTMTK_WARN(fmt, ...)    \
+	do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_WARNING)	\
+		pr_info("[btmtk_warn] %s: "fmt"\n", __func__, ##__VA_ARGS__); } while (0)
+#define BTMTK_INFO(fmt, ...)    \
+	do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_INFO)		\
+		pr_info("[btmtk_info] %s: "fmt"\n", __func__, ##__VA_ARGS__); } while (0)
+#define BTMTK_DBG(fmt, ...)     \
+	do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_DEBUG)		\
+		pr_info("[btmtk_debug] %s: "fmt"\n", __func__, ##__VA_ARGS__); } while (0)
+
+#define BTMTK_WARN_LIMITTED(fmt, ...)     \
+	do {												\
+		if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_WARNING)	\
+			pr_info(KERN_WARNING "[btmtk_warn_limit] %s: "fmt"\n",	\
+				__func__, ##__VA_ARGS__);	\
+	} while (0)
+
+
+#define BTMTK_MAX_LOG_LEN		64	/* default length setting */
+
+#define BTSDIO_INFO_RAW(p, l, fmt, ...)								\
+do {												\
+	if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_INFO) {						\
+		int raw_count = 0;								\
+		char str[BTMTK_MAX_LOG_LEN * 3 + 1];						\
+		char *p_str = str;								\
+		const unsigned char *ptr = p;							\
+		for (raw_count = 0; raw_count < MIN(l, BTMTK_MAX_LOG_LEN); ++raw_count)		\
+			p_str += sprintf(p_str, " %02X", ptr[raw_count]);			\
+		*p_str = '\0';									\
+		pr_info("[btmtk_info]"fmt"\n", ##__VA_ARGS__);					\
+		pr_info(" %s:%d - Length(%d): %s\n", __func__, __LINE__, l, str);		\
+	}											\
+} while (0)
+
+#define BTSDIO_DEBUG_RAW(p, l, fmt, ...)							\
+do {												\
+	if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_DEBUG) {						\
+		int raw_count = 0;								\
+		char str[BTMTK_MAX_LOG_LEN * 3 + 1];						\
+		char *p_str = str;								\
+		const unsigned char *ptr = p;							\
+		for (raw_count = 0; raw_count < MIN(l, BTMTK_MAX_LOG_LEN); ++raw_count)		\
+			p_str += sprintf(p_str, " %02X", ptr[raw_count]);			\
+		*p_str = '\0';									\
+		pr_info("[btmtk_debug]"fmt"\n", ##__VA_ARGS__);					\
+		pr_info(" %s:%d - Length(%d): %s\n", __func__, __LINE__, l, str);		\
+	}											\
+} while (0)
+
+#define MTK_HCI_WRITE_CR_PKT		0x07
+#define MTK_HCI_READ_CR_PKT		0x08
+
+#define MTK_HCI_READ_CR_PKT_LENGTH	0x05
+#define MTK_HCI_WRITE_CR_PKT_LENGTH	0x09
+
+#define MTK_HCI_CMD_HEADER_LEN	(4)
+#define MTK_HCI_ACL_HEADER_LEN	(5)
+#define MTK_HCI_SCO_HEADER_LEN	(4)
+
+#define PRINT_DUMP_COUNT		20
+
+/**
+ * SYS control
+ */
+#define SYSCTL	0x400000
+
+/**
+ * WLAN
+ */
+#define WLAN	0x410000
+
+/**
+ * MCUCTL
+ */
+#define CLOCK_CTL	0x0708
+#define INT_LEVEL	0x0718
+#define COM_REG0	0x0730
+#define SEMAPHORE_00	0x07B0
+#define SEMAPHORE_01	0x07B4
+#define SEMAPHORE_02	0x07B8
+#define SEMAPHORE_03	0x07BC
+
+/**
+ * Timeout setting, mescs
+ */
+#define USB_CTRL_IO_TIMO	100
+#define USB_INTR_MSG_TIMO	2000
+
+
+/**
+ * USB request type definition
+ */
+#define DEVICE_VENDOR_REQUEST_OUT	0x40
+#define DEVICE_VENDOR_REQUEST_IN	0xc0
+#define DEVICE_CLASS_REQUEST_OUT	0x20
+#define DEVICE_CLASS_REQUEST_IN		0xa0
+
+#define BTUSB_MAX_ISOC_FRAMES	10
+#define BTUSB_INTR_RUNNING	0
+#define BTUSB_BULK_RUNNING	1
+#define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
+#define BTUSB_DID_ISO_RESUME	4
+
+/**
+ * ROM patch related
+ */
+#define PATCH_HCI_HEADER_SIZE	4
+#define PATCH_WMT_HEADER_SIZE	5
+#define PATCH_HEADER_SIZE	(PATCH_HCI_HEADER_SIZE + PATCH_WMT_HEADER_SIZE)
+#define UPLOAD_PATCH_UNIT	2048
+#define PATCH_INFO_SIZE		30
+#define PATCH_PHASE1		1
+#define PATCH_PHASE2		2
+#define PATCH_PHASE3		3
+#define PATCH_LEN_ILM		(192 * 1024)
+
+
+#define USB_IO_BUF_SIZE		(HCI_MAX_EVENT_SIZE > 256 ? HCI_MAX_EVENT_SIZE : 256)
+#define HCI_SNOOP_ENTRY_NUM	30
+#define HCI_SNOOP_BUF_SIZE	32
+#define FW_LOG_PKT		0xFF
+
+/**
+ * stpbt device node
+ */
+#define BUFFER_SIZE	(1024 * 4)	/* Size of RX Queue */
+
+/**
+ * fw log queue count
+ */
+#define FWLOG_QUEUE_COUNT 200
+#define FWLOG_ASSERT_QUEUE_COUNT 10000
+#define FWLOG_BLUETOOTH_KPI_QUEUE_COUNT 200
+
+/**
+ * Maximum rom patch file name length
+ */
+#define MAX_BIN_FILE_NAME_LEN 32
+
+
+/**
+ *  Firmware version size
+ */
+#define FW_VERSION_BUF_SIZE 32	/* 14 bytes for firmware version + 1 bytes for '0' */
+#define FW_VERSION_SIZE 15	/* 14 bytes for firmware version + 1 bytes for '0' */
+
+#endif /* __BTMTK_DEFINE_H__ */
diff --git a/btmtk_drv.h b/btmtk_drv.h
new file mode 100644
index 0000000..9181921
--- /dev/null
+++ b/btmtk_drv.h
@@ -0,0 +1,215 @@
+/*
+ *  Copyright (c) 2016 MediaTek Inc.
+ *
+ *  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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef _BTMTK_DRV_H_
+#define _BTMTK_DRV_H_
+
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define SUPPORT_FW_DUMP		1
+#define BTM_HEADER_LEN                  5
+#define BTM_UPLD_SIZE                   2312
+
+#define SUPPORT_CR_WR 1
+
+#define MTK_TXDATA_SIZE 2000
+#define MTK_RXDATA_SIZE 2000
+
+/* Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED	msecs_to_jiffies(5000)
+/* Time to wait for command response in millisecond */
+#define WAIT_UNTIL_CMD_RESP		msecs_to_jiffies(5000)
+
+/** For 7668 please storage cfg/bin file in ${firmware} */
+#define E2P_ACCESS_MODE_SWITCHER	"wifi.cfg"
+#define E2P_BIN_FILE			"EEPROM_MT%X.bin"
+
+#define E2P_MODE			"EfuseBufferModeCal"
+#define E2P_ACCESS_EPA			"BtUseExternalPA"
+#define E2P_ACCESS_DUPLEX		"BtDuplexMode"
+#define KEEP_FULL_PWR			"KeepFullPwr"
+#define PWR_KEEP_NO_FW_OWN		'1'
+#define PWR_SWITCH_DRIVER_FW_OWN	'0'
+#define EFUSE_MODE			0
+#define EFUSE_BIN_FILE_MODE		1
+#define EFUSE_AUTO_MODE			2
+
+
+enum rdwr_status {
+	RDWR_STATUS_SUCCESS = 0,
+	RDWR_STATUS_FAILURE = 1,
+	RDWR_STATUS_DONE = 2
+};
+
+#define FW_DUMP_MAX_NAME_LEN    8
+#define FW_DUMP_HOST_READY      0xEE
+#define FW_DUMP_DONE            0xFF
+#define FW_DUMP_READ_DONE       0xFE
+
+struct memory_type_mapping {
+	u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+	u8 *mem_ptr;
+	u32 mem_size;
+	u8 done_flag;
+};
+
+struct btmtk_thread {
+	struct task_struct *task;
+	wait_queue_head_t wait_q;
+	void *priv;
+	u8 thread_status;
+};
+
+struct btmtk_device {
+	void *card;
+	/* struct hci_dev *hcidev; */
+
+	u8 reset_progress;
+	u8 reset_dongle;
+	u8 tx_dnld_rdy;
+};
+
+struct btmtk_adapter {
+	void *hw_regs_buf;
+	u8 *hw_regs;
+	u32 int_count;
+};
+
+struct btmtk_private {
+	struct btmtk_device btmtk_dev;
+	struct btmtk_adapter *adapter;
+	struct btmtk_thread main_thread;
+	int (*hw_host_to_card)(struct btmtk_private *priv,
+				u8 *payload, u16 nb);
+
+	void (*start_reset_dongle_progress)(void);
+	int (*hw_sdio_reset_dongle)(void);
+	int (*hw_set_own_back)(int owntype);
+	int (*hw_process_int_status)(struct btmtk_private *priv);
+	void (*hci_snoop_save)(u8 type, u8 *buf, u32 len);
+	void (*firmware_dump)(struct btmtk_private *priv);
+	spinlock_t driver_lock;         /* spinlock used by driver */
+#ifdef CONFIG_DEBUG_FS
+	void *debugfs_data;
+#endif
+	bool surprise_removed;
+#if SUPPORT_FW_DUMP
+	struct semaphore fw_dump_semaphore;
+	struct task_struct *fw_dump_tsk;
+	struct task_struct *fw_dump_end_check_tsk;
+#endif
+	struct semaphore wr_mtx;
+	struct semaphore rd_mtx;
+	struct semaphore wr_fwlog_mtx;
+	bool no_fw_own;
+};
+
+#define MTK_VENDOR_PKT                 0xFE
+
+/* Vendor specific Bluetooth commands */
+#define BT_CMD_PSCAN_WIN_REPORT_ENABLE  0xFC03
+#define BT_CMD_ROUTE_SCO_TO_HOST        0xFC1D
+#define BT_CMD_SET_BDADDR               0xFC22
+#define BT_CMD_AUTO_SLEEP_MODE          0xFC23
+#define BT_CMD_HOST_SLEEP_CONFIG        0xFC59
+#define BT_CMD_HOST_SLEEP_ENABLE        0xFC5A
+#define BT_CMD_MODULE_CFG_REQ           0xFC5B
+#define BT_CMD_LOAD_CONFIG_DATA         0xFC61
+
+/* Sub-commands: Module Bringup/Shutdown Request/Response */
+#define MODULE_BRINGUP_REQ              0xF1
+#define MODULE_BROUGHT_UP               0x00
+#define MODULE_ALREADY_UP               0x0C
+
+#define MODULE_SHUTDOWN_REQ             0xF2
+
+/* Vendor specific Bluetooth events */
+#define BT_EVENT_AUTO_SLEEP_MODE        0x23
+#define BT_EVENT_HOST_SLEEP_CONFIG      0x59
+#define BT_EVENT_HOST_SLEEP_ENABLE      0x5A
+#define BT_EVENT_MODULE_CFG_REQ         0x5B
+#define BT_EVENT_POWER_STATE            0x20
+
+/* Bluetooth Power States */
+#define BT_PS_ENABLE                    0x02
+#define BT_PS_DISABLE                   0x03
+#define BT_PS_SLEEP                     0x01
+
+/* Host Sleep states */
+#define HS_ACTIVATED                    0x01
+#define HS_DEACTIVATED                  0x00
+
+/* Power Save modes */
+#define PS_SLEEP                        0x01
+#define PS_AWAKE                        0x00
+
+#define BT_CAL_HDR_LEN                  4
+#define BT_CAL_DATA_SIZE                28
+
+#define FW_DUMP_BUF_SIZE (1024*512)
+
+#define FW_DUMP_FILE_NAME_SIZE     64
+
+#define EVENT_COMPARE_SIZE     64
+
+
+/* stpbt device node */
+#define BT_NODE "stpbt"
+#define BT_DRIVER_NAME "BT_chrdev"
+
+struct btmtk_event {
+	u8 ec;          /* event counter */
+	u8 length;
+	u8 data[4];
+} __packed;
+
+/* Prototype of global function */
+
+struct btmtk_private *btmtk_add_card(void *card);
+int btmtk_remove_card(struct btmtk_private *priv);
+
+void btmtk_interrupt(struct btmtk_private *priv);
+
+bool btmtk_check_evtpkt(struct btmtk_private *priv, struct sk_buff *skb);
+int btmtk_process_event(struct btmtk_private *priv, struct sk_buff *skb);
+
+int btmtk_send_module_cfg_cmd(struct btmtk_private *priv, u8 subcmd);
+int btmtk_pscan_window_reporting(struct btmtk_private *priv, u8 subcmd);
+int btmtk_send_hscfg_cmd(struct btmtk_private *priv);
+int btmtk_enable_ps(struct btmtk_private *priv);
+int btmtk_prepare_command(struct btmtk_private *priv);
+void btmtk_firmware_dump(struct btmtk_private *priv);
+
+#define META_BUFFER_SIZE (1024*50)
+
+struct _OSAL_UNSLEEPABLE_LOCK_ {
+	spinlock_t lock;
+	unsigned long flag;
+};
+
+struct ring_buffer {
+	struct _OSAL_UNSLEEPABLE_LOCK_ spin_lock;
+	u8 buffer[META_BUFFER_SIZE];	/* MTKSTP_BUFFER_SIZE:1024 */
+	u32 read_p;		/* indicate the current read index */
+	u32 write_p;		/* indicate the current write index */
+};
+
+#define FIXED_STPBT_MAJOR_DEV_ID 111
+
+#define FW_DUMP_END_EVENT "coredump end"
+
+#endif
+
diff --git a/btmtk_main.c b/btmtk_main.c
new file mode 100644
index 0000000..1c6cc3d
--- /dev/null
+++ b/btmtk_main.c
@@ -0,0 +1,325 @@
+/*
+ *  Copyright (c) 2016 MediaTek Inc.
+ *
+ *  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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
+#include <linux/sched.h>
+#else
+#include <uapi/linux/sched/types.h>
+#endif
+
+#include "btmtk_define.h"
+#include "btmtk_drv.h"
+#include "btmtk_sdio.h"
+
+/*
+ * This function is called by interface specific interrupt handler.
+ * It updates Power Save & Host Sleep states, and wakes up the main
+ * thread.
+ */
+void btmtk_interrupt(struct btmtk_private *priv)
+{
+	priv->adapter->int_count++;
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+}
+EXPORT_SYMBOL_GPL(btmtk_interrupt);
+
+static int btmtk_tx_pkt(struct btmtk_private *priv, struct sk_buff *skb)
+{
+	int ret = 0;
+	u32 sdio_header_len = 0;
+
+	if (!skb) {
+		BTMTK_WARN("skb is NULL return -EINVAL");
+		return -EINVAL;
+	}
+
+	BTMTK_DBG("skb->len %d", skb->len);
+
+	if (!skb->data) {
+		BTMTK_WARN("skb->data is NULL return -EINVAL");
+		return -EINVAL;
+	}
+
+	if (!skb->len || ((skb->len + BTM_HEADER_LEN) > MTK_TXDATA_SIZE)) {
+		BTMTK_WARN("Tx Error: Bad skb length %d : %d",
+						skb->len, MTK_TXDATA_SIZE);
+		return -EINVAL;
+	}
+
+	if (priv->hci_snoop_save)
+		priv->hci_snoop_save(bt_cb(skb)->pkt_type, skb->data, skb->len);
+
+	sdio_header_len = skb->len + BTM_HEADER_LEN;
+	memset(txbuf, 0, MTK_TXDATA_SIZE);
+	txbuf[0] = (sdio_header_len & 0x0000ff);
+	txbuf[1] = (sdio_header_len & 0x00ff00) >> 8;
+	txbuf[2] = 0;
+	txbuf[3] = 0;
+	txbuf[4] = bt_cb(skb)->pkt_type;
+	memcpy(&txbuf[5], &skb->data[0], skb->len);
+	if (priv->hw_host_to_card)
+		ret = priv->hw_host_to_card(priv, txbuf, sdio_header_len);
+
+	BTMTK_DBG("end");
+	return ret;
+}
+
+static void btmtk_init_adapter(struct btmtk_private *priv)
+{
+	int buf_size;
+
+	buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);
+	priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!priv->adapter->hw_regs_buf) {
+		priv->adapter->hw_regs = NULL;
+		BTMTK_ERR("Unable to allocate buffer for h/w_regs.");
+	} else {
+		priv->adapter->hw_regs =
+			(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,
+					BTSDIO_DMA_ALIGN);
+		BTMTK_DBG("hw_regs_buf=%p hw_regs=%p",
+			priv->adapter->hw_regs_buf, priv->adapter->hw_regs);
+	}
+}
+
+static void btmtk_free_adapter(struct btmtk_private *priv)
+{
+	kfree(priv->adapter->hw_regs_buf);
+}
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ */
+
+static int btmtk_service_main_thread(void *data)
+{
+	struct btmtk_thread *thread = data;
+	struct btmtk_private *priv = thread->priv;
+	struct btmtk_adapter *adapter = NULL;
+	struct btmtk_sdio_card *card = NULL;
+#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
+	wait_queue_t wait;
+#else
+	struct wait_queue_entry wait;
+#endif
+	struct sk_buff *skb;
+	int ret = 0;
+	int i = 0;
+	ulong flags;
+#if (KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE)
+	struct sched_param param = { .sched_priority = 90 };/*RR 90 is the same as audio*/
+#endif
+	int reset_flag = 0;
+
+#if (KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE)
+	sched_setscheduler(current, SCHED_RR, &param);
+#else
+	sched_set_fifo(current);
+#endif
+	for (i = 0; i <= 1000; i++) {
+		if (kthread_should_stop()) {
+			BTMTK_INFO("main_thread: break from main thread for probe_ready");
+			break;
+		}
+
+		if (probe_ready)
+			break;
+
+		/* BTMTK_INFO("probe_ready %d delay 10ms~15ms", probe_ready);*/
+		usleep_range(10*1000, 15*1000);
+
+		if (i == 1000) {
+			BTMTK_WARN("probe_ready %d i = %d try too many times return",
+				probe_ready, i);
+			return 0;
+		}
+	}
+
+	if (priv->adapter)
+		adapter = priv->adapter;
+	else {
+		BTMTK_ERR("priv->adapter is NULL return");
+		return 0;
+	}
+
+	if (priv->btmtk_dev.card)
+		card = priv->btmtk_dev.card;
+	else {
+		BTMTK_ERR("priv->btmtk_dev.card is NULL return");
+		return 0;
+	}
+
+	thread->thread_status = 1;
+	init_waitqueue_entry(&wait, current);
+	for (;;) {
+		add_wait_queue(&thread->wait_q, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (kthread_should_stop()) {
+			remove_wait_queue(&thread->wait_q, &wait);
+			BTMTK_WARN("main_thread: break from main thread");
+			break;
+		}
+
+		if ((((!adapter->int_count) &&
+				(!priv->btmtk_dev.tx_dnld_rdy ||
+				skb_queue_empty(&card->tx_queue)))) &&
+				(!priv->btmtk_dev.reset_dongle)) {
+			BTMTK_DBG("main_thread is sleeping...");
+			schedule();
+		}
+
+		set_current_state(TASK_RUNNING);
+
+		remove_wait_queue(&thread->wait_q, &wait);
+
+		if (kthread_should_stop()) {
+			BTMTK_WARN("break after wake up");
+			break;
+		}
+
+		if (priv->btmtk_dev.reset_dongle) {
+			ret = priv->hw_sdio_reset_dongle();
+			if (ret) {
+				BTMTK_ERR(L0_RESET_TAG "hw reset dongle error <%d>", ret);
+			} else {
+				BTMTK_INFO(L0_RESET_TAG "hw reset dongle done");
+				reset_flag = 1;
+				break;
+			}
+		}
+
+		if (priv->btmtk_dev.reset_progress)
+			continue;
+
+		ret = priv->hw_set_own_back(DRIVER_OWN);
+		if (ret) {
+			BTMTK_ERR("set driver own return fail");
+			priv->start_reset_dongle_progress();
+			continue;
+		}
+
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		if (adapter->int_count) {
+			BTMTK_DBG("go int");
+			adapter->int_count = 0;
+			spin_unlock_irqrestore(&priv->driver_lock, flags);
+			if (priv->hw_process_int_status(priv)) {
+				priv->start_reset_dongle_progress();
+				continue;
+			}
+		} else {
+			BTMTK_DBG("go tx");
+			spin_unlock_irqrestore(&priv->driver_lock, flags);
+		}
+
+		if (!priv->btmtk_dev.tx_dnld_rdy) {
+			BTMTK_DBG("tx_dnld_rdy == 0, continue");
+			continue;
+		}
+
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		skb = skb_dequeue(&card->tx_queue);
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		if (skb) {
+			if (skb->len < 16)
+				btmtk_print_buffer_conent(skb->data, skb->len);
+			else
+				btmtk_print_buffer_conent(skb->data, 16);
+
+			ret = btmtk_tx_pkt(priv, skb);
+			if (ret && (ret != (-EINVAL))) {
+				BTMTK_ERR("tx pkt return fail %d", ret);
+				priv->start_reset_dongle_progress();
+				continue;
+			}
+
+			BTMTK_DBG("after btmtk_tx_pkt kfree_skb");
+			kfree_skb(skb);
+		}
+
+		if (skb_queue_empty(&card->tx_queue)) {
+			ret = priv->hw_set_own_back(FW_OWN);
+			if (ret) {
+				BTMTK_ERR("set fw own return fail");
+				priv->start_reset_dongle_progress();
+				continue;
+			}
+		}
+	}
+	BTMTK_WARN("end");
+	thread->thread_status = 0;
+
+	return 0;
+}
+
+struct btmtk_private *btmtk_add_card(void *data)
+{
+	struct btmtk_sdio_card *card = (struct btmtk_sdio_card *)data;
+	struct btmtk_private *priv;
+
+	BTMTK_INFO("begin");
+
+	priv = card->priv;
+
+	btmtk_init_adapter(priv);
+
+	BTMTK_INFO("Starting kthread...");
+	priv->main_thread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	init_waitqueue_head(&priv->main_thread.wait_q);
+	priv->main_thread.task = kthread_run(btmtk_service_main_thread,
+				&priv->main_thread, "btmtk_main_service");
+	if (IS_ERR(priv->main_thread.task))
+		goto err_thread;
+
+	priv->btmtk_dev.card = card;
+	priv->btmtk_dev.tx_dnld_rdy = true;
+
+	return priv;
+
+err_thread:
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(btmtk_add_card);
+
+int btmtk_remove_card(struct btmtk_private *priv)
+{
+	BTMTK_INFO("begin, stop main_thread");
+	if (!IS_ERR(priv->main_thread.task) && (priv->main_thread.thread_status)) {
+		kthread_stop(priv->main_thread.task);
+		BTMTK_INFO("wake_up_interruptible main_thread done");
+	}
+	BTMTK_INFO("stop main_thread done");
+#ifdef CONFIG_DEBUG_FS
+	/*btmtk_debugfs_remove(hdev);*/
+#endif
+
+	btmtk_free_adapter(priv);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_remove_card);
+
+MODULE_AUTHOR("Mediatek Ltd.");
+MODULE_DESCRIPTION("Mediatek Bluetooth driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/btmtk_sdio.c b/btmtk_sdio.c
new file mode 100644
index 0000000..a3701c8
--- /dev/null
+++ b/btmtk_sdio.c
@@ -0,0 +1,7896 @@
+/*
+ *  Copyright (c) 2016 MediaTek Inc.
+ *
+ *  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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#include "btmtk_config.h"
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/cdev.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/device.h>
+
+/* Define for proce node */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "btmtk_define.h"
+#include "btmtk_drv.h"
+#include "btmtk_sdio.h"
+
+/* Used for WoBLE on EINT */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/input.h>
+
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/reboot.h>
+
+typedef int (*sdio_card_probe)(struct sdio_func *func,
+					const struct sdio_device_id *id);
+
+static struct bt_stereo_clk stereo_clk;
+static u64 sys_clk_tmp;
+static unsigned int stereo_irq;
+struct _OSAL_UNSLEEPABLE_LOCK_ stereo_spin_lock;
+
+static dev_t g_devIDfwlog;
+static struct class *pBTClass;
+static struct device *pBTDev;
+struct device *pBTDevfwlog;
+static wait_queue_head_t inq;
+static wait_queue_head_t fw_log_inq;
+static struct fasync_struct *fasync;
+/*static int btmtk_woble_state = BTMTK_WOBLE_STATE_UNKNOWN;*/
+
+static int need_reset_stack;
+static int get_hci_reset;
+static int need_reopen;
+static int wlan_remove_done;
+
+static u8 user_rmmod;
+static int need_retry_load_woble;
+
+struct completion g_done;
+unsigned char probe_counter;
+struct btmtk_private *g_priv;
+#define STR_COREDUMP_END "coredump end\n\n"
+const u8 READ_ADDRESS_EVENT[] = { 0x0e, 0x0a, 0x01, 0x09, 0x10, 0x00 };
+
+static struct ring_buffer metabuffer;
+static struct ring_buffer fwlog_metabuffer;
+
+u8 probe_ready;
+/* record firmware version */
+static char fw_version_str[FW_VERSION_BUF_SIZE];
+static struct proc_dir_entry *g_proc_dir;
+
+static unsigned int btmtk_fops_state = BTMTK_FOPS_STATE_UNKNOWN;
+static DEFINE_MUTEX(btmtk_fops_state_mutex);
+#define FOPS_MUTEX_LOCK()	mutex_lock(&btmtk_fops_state_mutex)
+#define FOPS_MUTEX_UNLOCK()	mutex_unlock(&btmtk_fops_state_mutex)
+
+/** read_write for proc */
+static int btmtk_proc_show(struct seq_file *m, void *v);
+static int btmtk_proc_open(struct inode *inode, struct  file *file);
+static void btmtk_proc_create_new_entry(void);
+static int btmtk_sdio_trigger_fw_assert(void);
+
+static int btmtk_sdio_RegisterBTIrq(struct btmtk_sdio_card *data);
+static int btmtk_sdio_woble_input_init(struct btmtk_sdio_card *data);
+static void btmtk_sdio_woble_input_deinit(struct btmtk_sdio_card *data);
+/* bluetooth KPI feautre, bperf */
+u8 btmtk_bluetooth_kpi;
+u8 btmtk_log_lvl = BTMTK_LOG_LEVEL_DEFAULT;
+
+static char event_need_compare[EVENT_COMPARE_SIZE] = {0};
+static char event_need_compare_len;
+static char event_compare_status;
+/*add special header in the beginning of even, stack won't recognize these event*/
+
+/* timer for coredump end */
+struct task_struct *wait_dump_complete_tsk;
+#if !CFG_SUPPORT_CHIP_RESET_KO
+struct task_struct *wait_wlan_remove_tsk;
+int wlan_status = WLAN_STATUS_DEFAULT;
+#endif
+static int dump_data_counter;
+static int dump_data_length;
+static struct file *fw_dump_file;
+
+#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE)
+const struct file_operations BT_proc_fops = {
+	.open = btmtk_proc_open,
+	.read = seq_read,
+	.release = single_release,
+};
+#else
+const struct proc_ops BT_proc_fops = {
+	.proc_open = btmtk_proc_open,
+	.proc_read = seq_read,
+	.proc_release = single_release,
+};
+#endif
+
+static const struct btmtk_sdio_card_reg btmtk_reg_6630 = {
+	.cfg = 0x03,
+	.host_int_mask = 0x04,
+	.host_intstatus = 0x05,
+	.card_status = 0x20,
+	.sq_read_base_addr_a0 = 0x10,
+	.sq_read_base_addr_a1 = 0x11,
+	.card_fw_status0 = 0x40,
+	.card_fw_status1 = 0x41,
+	.card_rx_len = 0x42,
+	.card_rx_unit = 0x43,
+	.io_port_0 = 0x00,
+	.io_port_1 = 0x01,
+	.io_port_2 = 0x02,
+	.int_read_to_clear = false,
+	.func_num = 2,
+	.chip_id = 0x6630,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_6632 = {
+	.cfg = 0x03,
+	.host_int_mask = 0x04,
+	.host_intstatus = 0x05,
+	.card_status = 0x20,
+	.sq_read_base_addr_a0 = 0x10,
+	.sq_read_base_addr_a1 = 0x11,
+	.card_fw_status0 = 0x40,
+	.card_fw_status1 = 0x41,
+	.card_rx_len = 0x42,
+	.card_rx_unit = 0x43,
+	.io_port_0 = 0x00,
+	.io_port_1 = 0x01,
+	.io_port_2 = 0x02,
+	.int_read_to_clear = false,
+	.func_num = 2,
+	.chip_id = 0x6632,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_7668 = {
+	.cfg = 0x03,
+	.host_int_mask = 0x04,
+	.host_intstatus = 0x05,
+	.card_status = 0x20,
+	.sq_read_base_addr_a0 = 0x10,
+	.sq_read_base_addr_a1 = 0x11,
+	.card_fw_status0 = 0x40,
+	.card_fw_status1 = 0x41,
+	.card_rx_len = 0x42,
+	.card_rx_unit = 0x43,
+	.io_port_0 = 0x00,
+	.io_port_1 = 0x01,
+	.io_port_2 = 0x02,
+	.int_read_to_clear = false,
+	.func_num = 2,
+	.chip_id = 0x7668,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_7663 = {
+	.cfg = 0x03,
+	.host_int_mask = 0x04,
+	.host_intstatus = 0x05,
+	.card_status = 0x20,
+	.sq_read_base_addr_a0 = 0x10,
+	.sq_read_base_addr_a1 = 0x11,
+	.card_fw_status0 = 0x40,
+	.card_fw_status1 = 0x41,
+	.card_rx_len = 0x42,
+	.card_rx_unit = 0x43,
+	.io_port_0 = 0x00,
+	.io_port_1 = 0x01,
+	.io_port_2 = 0x02,
+	.int_read_to_clear = false,
+	.func_num = 2,
+	.chip_id = 0x7663,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_7666 = {
+	.cfg = 0x03,
+	.host_int_mask = 0x04,
+	.host_intstatus = 0x05,
+	.card_status = 0x20,
+	.sq_read_base_addr_a0 = 0x10,
+	.sq_read_base_addr_a1 = 0x11,
+	.card_fw_status0 = 0x40,
+	.card_fw_status1 = 0x41,
+	.card_rx_len = 0x42,
+	.card_rx_unit = 0x43,
+	.io_port_0 = 0x00,
+	.io_port_1 = 0x01,
+	.io_port_2 = 0x02,
+	.int_read_to_clear = false,
+	.func_num = 2,
+	.chip_id = 0x7666,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_6630 = {
+	.helper = "mtmk/sd8688_helper.bin",
+	.reg = &btmtk_reg_6630,
+	.support_pscan_win_report = false,
+	.sd_blksz_fw_dl = 64,
+	.supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_6632 = {
+	.helper = "mtmk/sd8688_helper.bin",
+	.reg = &btmtk_reg_6632,
+	.support_pscan_win_report = false,
+	.sd_blksz_fw_dl = 64,
+	.supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_7668 = {
+	.helper = "mtmk/sd8688_helper.bin",
+	.reg = &btmtk_reg_7668,
+	.support_pscan_win_report = false,
+	.sd_blksz_fw_dl = 64,
+	.supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_7663 = {
+	.helper = "mtmk/sd8688_helper.bin",
+	.reg = &btmtk_reg_7663,
+	.support_pscan_win_report = false,
+	.sd_blksz_fw_dl = 64,
+	.supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_7666 = {
+	.helper = "mtmk/sd8688_helper.bin",
+	.reg = &btmtk_reg_7666,
+	.support_pscan_win_report = false,
+	.sd_blksz_fw_dl = 64,
+	.supports_fw_dump = false,
+};
+
+static u8 hci_cmd_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 hci_cmd_snoop_len[HCI_SNOOP_ENTRY_NUM];
+static unsigned int hci_cmd_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+
+static u8 hci_event_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 hci_event_snoop_len[HCI_SNOOP_ENTRY_NUM];
+static unsigned int hci_event_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+
+static u8 hci_acl_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 hci_acl_snoop_len[HCI_SNOOP_ENTRY_NUM];
+static unsigned int hci_acl_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+
+static u8 fw_log_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 fw_log_snoop_len[HCI_SNOOP_ENTRY_NUM];
+static unsigned int fw_log_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+
+static u8 hci_cmd_snoop_index;
+static u8 hci_event_snoop_index;
+static u8 hci_acl_snoop_index;
+static u8 fw_log_snoop_index;
+
+unsigned char *txbuf;
+static unsigned char *rxbuf;
+static unsigned char *userbuf;
+static unsigned char *userbuf_fwlog;
+static u32 rx_length;
+static struct btmtk_sdio_card *g_card;
+
+static u32 reg_CHISR; /* Add for debug, need remove later */
+
+#ifndef SDIO_VENDOR_ID_MEDIATEK
+#define SDIO_VENDOR_ID_MEDIATEK 0x037A
+#endif
+
+static const struct sdio_device_id btmtk_sdio_ids[] = {
+	/* Mediatek SD8688 Bluetooth device */
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x6630),
+			.driver_data = (unsigned long) &btmtk_sdio_6630 },
+
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x6632),
+			.driver_data = (unsigned long) &btmtk_sdio_6632 },
+
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668),
+			.driver_data = (unsigned long) &btmtk_sdio_7668 },
+
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7663),
+			.driver_data = (unsigned long) &btmtk_sdio_7663 },
+
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7666),
+			.driver_data = (unsigned long) &btmtk_sdio_7666 },
+
+	{ }	/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(sdio, btmtk_sdio_ids);
+
+static int btmtk_clean_queue(void);
+static void btmtk_sdio_do_reset_or_wait_wlan_remove_done(void);
+static int btmtk_sdio_download_partial_rom_patch(u8 *fwbuf, int firmwarelen);
+static int btmtk_sdio_probe(struct sdio_func *func,
+					const struct sdio_device_id *id);
+static void btmtk_sdio_L0_hook_new_probe(sdio_card_probe pFn_Probe);
+static int btmtk_sdio_set_sleep(void);
+static int btmtk_sdio_set_audio(void);
+static int btmtk_sdio_send_hci_cmd(u8 cmd_type, u8 *cmd, int cmd_len,
+		const u8 *event, const int event_len,
+		int total_timeout);
+
+static int timestamp_threshold[BTMTK_SDIO_RX_CHECKPOINT_NUM];
+static unsigned int timestamp[BTMTK_SDIO_RX_CHECKPOINT_NUM][BTMTK_SDIO_TIMESTAMP_NUM];
+
+static inline unsigned long btmtk_kallsyms_lookup_name(const char *name)
+{
+	void *addr = NULL;
+
+//	addr = __symbol_get(name);
+//	if (addr)
+//		__symbol_put(name);
+	BTMTK_INFO("bt driver do chipreset, normal reboot");
+	kernel_restart(NULL);
+
+	return (unsigned long)addr;
+}
+
+static unsigned int btmtk_sdio_get_microseconds(void)
+{
+#if (KERNEL_VERSION(4, 19, 85) > LINUX_VERSION_CODE)
+	struct timeval tv = {0};
+
+	do_gettimeofday(&tv);
+	return tv.tv_sec * 1000000 + tv.tv_usec;
+#else
+	struct timespec64 ts;
+
+	ktime_get_real_ts64(&ts);
+	return ts.tv_sec * 1000000 + ts.tv_nsec/1000;
+#endif
+}
+
+static void btmtk_sdio_timestamp(unsigned int type)
+{
+	static unsigned int now;
+	static unsigned int ms_intr_now, ms_intr_last;
+	unsigned int ms_now;
+	unsigned int sum, max, min, avg;
+	unsigned int total, checkpoint_index, timestamp_index;
+	int threshold_zero[BTMTK_SDIO_RX_CHECKPOINT_NUM];
+
+	memset(threshold_zero, 0, sizeof(threshold_zero));
+	if (memcmp(timestamp_threshold, threshold_zero, sizeof(timestamp_threshold)) == 0)
+		return;
+
+	if (type == BTMTK_SDIO_RX_CHECKPOINT_INTR)
+		now++;
+	if (now == BTMTK_SDIO_TIMESTAMP_NUM)
+		now = 0;
+
+	ms_now = btmtk_sdio_get_microseconds();
+
+	if (type == BTMTK_SDIO_RX_CHECKPOINT_INTR) {
+		if (ms_intr_last == 0)
+			timestamp[type][now] = 0;
+		else
+			timestamp[type][now] = ms_now - ms_intr_last;
+		ms_intr_last = ms_now;
+		ms_intr_now = ms_now;
+	} else {
+		timestamp[type][now] = ms_now - ms_intr_now;
+	}
+
+	/* show statistics */
+	if (timestamp_threshold[type] != 0 && timestamp[type][now] > timestamp_threshold[type]) {
+		BTMTK_INFO("---------------------START---------------------");
+		BTMTK_INFO("Type(%d), Cur = %u", type, timestamp[type][now]);
+		for (checkpoint_index = 0; checkpoint_index < BTMTK_SDIO_RX_CHECKPOINT_NUM; checkpoint_index++) {
+			sum = 0;
+			total = 0;
+			max = 0;
+			min = 0xFFFFFFFF;
+			avg = 0;
+			for (timestamp_index = 0; timestamp_index < BTMTK_SDIO_TIMESTAMP_NUM; timestamp_index++) {
+				if (timestamp[checkpoint_index][timestamp_index] != 0) {
+					sum += timestamp[checkpoint_index][timestamp_index];
+					total++;
+					if (timestamp[checkpoint_index][timestamp_index] > max)
+						max = timestamp[checkpoint_index][timestamp_index];
+					if (timestamp[checkpoint_index][timestamp_index] < min)
+						min = timestamp[checkpoint_index][timestamp_index];
+				}
+			}
+			if (total > 0)
+				avg = sum/total;
+
+			BTMTK_INFO("Type(%d), Max = %u, Min = %u, Avg = %u",
+				checkpoint_index, max, min, avg);
+		}
+		BTMTK_INFO("----------------------END----------------------");
+	}
+}
+
+static int btmtk_fops_get_state(void)
+{
+	return btmtk_fops_state;
+}
+
+static void btmtk_fops_set_state(unsigned int new_state)
+{
+	static const char * const fstate_msg[BTMTK_FOPS_STATE_MAX] = {"UNKNOWN",
+		"INIT", "OPENING", "OPENED", "CLOSING", "CLOSED"};
+
+	BTMTK_INFO("%s: FOPS_%s(%d) -> FOPS_%s(%d)", __func__, fstate_msg[btmtk_fops_state],
+			btmtk_fops_state, fstate_msg[new_state], new_state);
+	btmtk_fops_state = new_state;
+}
+
+/*============================================================================*/
+/* Interface Functions : timer for coredump inform wifi */
+/*============================================================================*/
+static void btmtk_sdio_wakeup_mainthread_do_reset(void)
+{
+	if (g_priv) {
+		g_priv->btmtk_dev.reset_dongle = 1;
+		BTMTK_INFO("set reset_dongle %d", g_priv->btmtk_dev.reset_dongle);
+		wake_up_interruptible(&g_priv->main_thread.wait_q);
+	} else
+		BTMTK_ERR("g_priv is NULL");
+}
+
+#if !CFG_SUPPORT_CHIP_RESET_KO
+void btmtk_sdio_stop_wait_wlan_remove_tsk(void)
+{
+	if (wait_wlan_remove_tsk == NULL)
+		BTMTK_INFO("wait_wlan_remove_tsk is NULL");
+	else if (IS_ERR(wait_wlan_remove_tsk))
+		BTMTK_INFO("wait_wlan_remove_tsk is error");
+	else {
+		BTMTK_INFO("call kthread_stop wait_wlan_remove_tsk");
+		kthread_stop(wait_wlan_remove_tsk);
+		wait_wlan_remove_tsk = NULL;
+	}
+}
+
+int btmtk_sdio_notify_wlan_remove_start(void)
+{
+	/* notify_wlan_remove_start */
+	int ret = 0;
+	typedef void (*pnotify_wlan_remove_start) (int reserved);
+	char *notify_wlan_remove_start_func_name;
+	pnotify_wlan_remove_start pnotify_wlan_remove_start_func;
+
+	BTMTK_INFO("wlan_status %d", wlan_status);
+	if (wlan_status == WLAN_STATUS_CALL_REMOVE_START) {
+		/* do notify before, just return */
+		return ret;
+	}
+
+	notify_wlan_remove_start_func_name = "BT_rst_L0_notify_WF_step1";
+
+	pnotify_wlan_remove_start_func =
+		(pnotify_wlan_remove_start)btmtk_kallsyms_lookup_name
+			(notify_wlan_remove_start_func_name);
+
+	BTMTK_INFO(L0_RESET_TAG);
+	/* void notify_wlan_remove_start(void) */
+	if (pnotify_wlan_remove_start_func) {
+		BTMTK_INFO("do notify %s", notify_wlan_remove_start_func_name);
+		wlan_remove_done = 0;
+		pnotify_wlan_remove_start_func(0);
+		wlan_status = WLAN_STATUS_CALL_REMOVE_START;
+	} else {
+		ret = -1;
+		BTMTK_ERR("do not get %s", notify_wlan_remove_start_func_name);
+		wlan_status = WLAN_STATUS_IS_NOT_LOAD;
+		wlan_remove_done = 1;
+	}
+	return ret;
+}
+
+static int btmtk_sdio_wait_wlan_remove_thread(void *ptr)
+{
+	int  i = 0;
+
+	BTMTK_INFO("begin");
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL, return");
+		return 0;
+	}
+
+	g_priv->btmtk_dev.reset_progress = 1;
+	for (i = 0; i < 30; i++) {
+		if ((wait_wlan_remove_tsk && kthread_should_stop()) || wlan_remove_done) {
+			BTMTK_WARN("break wlan_remove_done %d", wlan_remove_done);
+			break;
+		}
+		msleep(500);
+	}
+
+	btmtk_sdio_wakeup_mainthread_do_reset();
+
+	while (!kthread_should_stop()) {
+		BTMTK_INFO("no one call stop");
+		msleep(500);
+	}
+
+	BTMTK_INFO("end");
+	return 0;
+}
+#endif
+
+static void btmtk_sdio_start_reset_dongle_progress(void)
+{
+	if (!g_card->bt_cfg.support_dongle_reset) {
+		BTMTK_WARN("debug mode do not do reset");
+	} else {
+		BTMTK_WARN("user mode do reset");
+#if !CFG_SUPPORT_CHIP_RESET_KO
+		btmtk_sdio_notify_wlan_remove_start();
+#endif
+		btmtk_sdio_do_reset_or_wait_wlan_remove_done();
+	}
+}
+
+/*============================================================================*/
+/* Interface Functions : timer for uncomplete coredump */
+/*============================================================================*/
+static void btmtk_sdio_do_reset_or_wait_wlan_remove_done(void)
+{
+	BTMTK_INFO("wlan_remove_done %d", wlan_remove_done);
+#if !CFG_SUPPORT_CHIP_RESET_KO
+	if (wlan_remove_done || (wlan_status == WLAN_STATUS_IS_NOT_LOAD))
+#endif
+		/* wifi inform bt already, reset chip */
+		btmtk_sdio_wakeup_mainthread_do_reset();
+#if !CFG_SUPPORT_CHIP_RESET_KO
+	else {
+		/* makesure wait thread is stopped */
+		btmtk_sdio_stop_wait_wlan_remove_tsk();
+		/* create thread wait wifi inform bt */
+		BTMTK_INFO("create btmtk_sdio_wait_wlan_remove_thread");
+		wait_wlan_remove_tsk = kthread_run(
+			btmtk_sdio_wait_wlan_remove_thread, NULL,
+			"btmtk_sdio_wait_wlan_remove_thread");
+		if (wait_wlan_remove_tsk == NULL)
+			BTMTK_ERR("btmtk_sdio_wait_wlan_remove_thread create fail");
+	}
+#endif
+}
+
+static int btmtk_sdio_wait_dump_complete_thread(void *ptr)
+{
+	int  i = 0;
+
+	BTMTK_INFO("begin");
+	for (i = 0; i < 60; i++) {
+		if (wait_dump_complete_tsk && kthread_should_stop()) {
+			BTMTK_WARN("thread is stopped, break");
+			break;
+		}
+		msleep(500);
+	}
+
+	if (!g_card->bt_cfg.support_dongle_reset) {
+		BTMTK_INFO("debug mode don't do reset");
+	} else {
+		BTMTK_INFO("user mode call do reset");
+		btmtk_sdio_do_reset_or_wait_wlan_remove_done();
+	}
+
+	if (i >= 60)
+		BTMTK_WARN("wait dump complete timeout");
+	wait_dump_complete_tsk = NULL;
+
+	BTMTK_INFO("end");
+	return 0;
+}
+
+static void btmtk_sdio_free_fw_cfg_struct(struct fw_cfg_struct *fw_cfg, int count)
+{
+	int i = 0;
+
+	for (i = 0; i < count; i++) {
+		if (fw_cfg[i].content) {
+			BTMTK_INFO("kfree %d", i);
+			kfree(fw_cfg[i].content);
+			fw_cfg[i].content = NULL;
+			fw_cfg[i].length = 0;
+		} else
+			fw_cfg[i].length = 0;
+	}
+}
+
+static void btmtk_sdio_free_bt_cfg(void)
+{
+	BTMTK_INFO("begin");
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card == NULL");
+		return;
+	}
+
+	btmtk_sdio_free_fw_cfg_struct(&g_card->bt_cfg.picus_filter, 1);
+	btmtk_sdio_free_fw_cfg_struct(g_card->bt_cfg.wmt_cmd, WMT_CMD_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->bt_cfg.vendor_cmd, VENDOR_CMD_COUNT);
+
+	kfree(g_card->bt_cfg.sys_log_file_name);
+	g_card->bt_cfg.sys_log_file_name = NULL;
+
+	kfree(g_card->bt_cfg.fw_dump_file_name);
+	g_card->bt_cfg.fw_dump_file_name = NULL;
+
+	kfree(g_card->bt_cfg_file_name);
+	g_card->bt_cfg_file_name = NULL;
+
+	memset(&g_card->bt_cfg, 0, sizeof(g_card->bt_cfg));
+
+	BTMTK_INFO("end");
+}
+
+static void btmtk_sdio_woble_free_setting(void)
+{
+	BTMTK_INFO("begin");
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card == NULL");
+		return;
+	}
+
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_apcf, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_apcf_fill_mac, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_apcf_fill_mac_location, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_radio_off, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_wakeup_type, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_radio_off_status_event, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_radio_off_comp_event, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_radio_on, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_radio_on_status_event, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_radio_on_comp_event, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_apcf_resume, WOBLE_SETTING_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->woble_setting_apcf_resume_event, WOBLE_SETTING_COUNT);
+
+	kfree(g_card->woble_setting_file_name);
+	g_card->woble_setting_file_name = NULL;
+
+	if (g_card->bt_cfg.support_woble_by_eint) {
+		if (g_card->wobt_irq != 0 && atomic_read(&(g_card->irq_enable_count)) == 1) {
+			BTMTK_INFO("disable BT IRQ:%d", g_card->wobt_irq);
+			atomic_dec(&(g_card->irq_enable_count));
+			disable_irq_nosync(g_card->wobt_irq);
+		} else
+			BTMTK_INFO("irq_enable count:%d", atomic_read(&(g_card->irq_enable_count)));
+		free_irq(g_card->wobt_irq, g_card);
+	}
+
+	BTMTK_INFO("end");
+}
+
+static void btmtk_sdio_initialize_cfg_items(void)
+{
+	BTMTK_INFO("begin");
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card == NULL");
+		return;
+	}
+
+	g_card->bt_cfg.dongle_reset_gpio_pin = 0;
+	g_card->bt_cfg.save_fw_dump_in_kernel = 0;
+	g_card->bt_cfg.support_dongle_reset = 0;
+	g_card->bt_cfg.support_full_fw_dump = 1;
+	g_card->bt_cfg.support_legacy_woble = 0;
+	g_card->bt_cfg.support_unify_woble = 0;
+	g_card->bt_cfg.unify_woble_type = 0;
+	g_card->bt_cfg.support_woble_by_eint = 0;
+	g_card->bt_cfg.support_woble_for_bt_disable = 0;
+	g_card->bt_cfg.support_woble_wakelock = 0;
+	g_card->bt_cfg.reset_stack_after_woble = 0;
+	g_card->bt_cfg.sys_log_file_name = NULL;
+	g_card->bt_cfg.fw_dump_file_name = NULL;
+	g_card->bt_cfg.support_auto_picus = 0;
+	btmtk_sdio_free_fw_cfg_struct(&g_card->bt_cfg.picus_filter, 1);
+	btmtk_sdio_free_fw_cfg_struct(g_card->bt_cfg.wmt_cmd, WMT_CMD_COUNT);
+	btmtk_sdio_free_fw_cfg_struct(g_card->bt_cfg.vendor_cmd, VENDOR_CMD_COUNT);
+
+	BTMTK_INFO("end");
+}
+
+static bool btmtk_sdio_parse_bt_cfg_file(char *item_name, char *text,
+					char *searchcontent)
+{
+	bool ret = true;
+	int temp_len = 0;
+	char search[32];
+	char *ptr = NULL, *p = NULL;
+	char *temp = text;
+
+	if (text == NULL) {
+		BTMTK_ERR("text param is invalid!");
+		ret = false;
+		goto out;
+	}
+
+	memset(search, 0, sizeof(search));
+	(void)snprintf(search, sizeof(search), "%s", item_name); /* EX: SUPPORT_UNIFY_WOBLE */
+	p = ptr = strstr(searchcontent, search);
+
+	if (!ptr) {
+		BTMTK_ERR("Can't find %s", item_name);
+		ret = false;
+		goto out;
+	}
+
+	if (p > searchcontent) {
+		p--;
+		while ((*p == ' ') && (p != searchcontent))
+			p--;
+		if (*p == '#') {
+			BTMTK_ERR("It's invalid bt cfg item");
+			ret = false;
+			goto out;
+		}
+	}
+
+	p = ptr + strlen(item_name) + 1;
+	ptr = p;
+
+	for (;;) {
+		switch (*p) {
+		case '\n':
+			goto textdone;
+		default:
+			*temp++ = *p++;
+			break;
+		}
+	}
+
+textdone:
+	temp_len = p - ptr;
+	*temp = '\0';
+
+out:
+	return ret;
+}
+
+static void btmtk_sdio_bt_cfg_item_value_to_bool(char *item_value, bool *value)
+{
+	unsigned long text_value = 0;
+
+	if (item_value == NULL) {
+		BTMTK_ERR("item_value is NULL!");
+		return;
+	}
+
+	if (kstrtoul(item_value, 10, &text_value) == 0) {
+		if (text_value == 1)
+			*value = true;
+		else
+			*value = false;
+	} else {
+		BTMTK_WARN("kstrtoul failed!");
+	}
+}
+
+static int btmtk_sdio_load_fw_cfg_setting(char *block_name, struct fw_cfg_struct *save_content,
+			int save_content_count, u8 *searchconetnt, enum fw_cfg_index_len index_length)
+{
+	int ret = 0;
+	int i = 0;
+	long parsing_result = 0;
+	u8 *search_result = NULL;
+	u8 *search_end = NULL;
+	u8 search[32];
+	u8 temp[260]; /* save for total hex number */
+	u8 *next_number = NULL;
+	u8 *next_block = NULL;
+	u8 number[8];
+	unsigned int temp_len;
+
+	memset(search, 0, sizeof(search));
+	memset(temp, 0, sizeof(temp));
+	memset(number, 0, sizeof(number));
+
+	/* search block name */
+	for (i = 0; i < save_content_count; i++) {
+		temp_len = 0;
+		if (index_length == FW_CFG_INX_LEN_2) /* EX: APCF01 */
+			(void)snprintf(search, sizeof(search), "%s%02d:", block_name, i);
+		else if (index_length == FW_CFG_INX_LEN_3) /* EX: APCF001 */
+			(void)snprintf(search, sizeof(search), "%s%03d:", block_name, i);
+		else
+			(void)snprintf(search, sizeof(search), "%s:", block_name);
+		search_result = strstr(searchconetnt, search);
+		if (search_result) {
+			memset(temp, 0, sizeof(temp));
+			temp_len = 0;
+			search_result += strlen(search); /* move to first number */
+
+			do {
+				next_number = NULL;
+				search_end = strstr(search_result, ",");
+
+				if (!search_end) {
+					BTMTK_INFO("can not find search end, break");
+					break;
+				}
+
+				if ((search_end - search_result) > sizeof(number))
+					break;
+
+				memset(number, 0, sizeof(number));
+				memcpy(number, search_result, search_end - search_result);
+
+				if (number[0] == 0x20) /* space */
+					ret = kstrtol(number + 1, 0, &parsing_result);
+				else
+					ret = kstrtol(number, 0, &parsing_result);
+
+				if (ret == 0) {
+					if (temp_len >= sizeof(temp)) {
+						BTMTK_ERR("%s data over %zu", search, sizeof(temp));
+						break;
+					}
+					temp[temp_len] = parsing_result;
+					temp_len++;
+					/* find next number */
+					next_number = strstr(search_end, "0x");
+					if (!next_number) {
+						BTMTK_DBG("not find next apcf number temp_len %d, break, search %s",
+							temp_len, search);
+						break;
+					}
+					/* find next block */
+					next_block = strstr(search_end, ":");
+					if (!next_block)
+						BTMTK_DBG("not find next block");
+				} else {
+					BTMTK_DBG("kstrtol ret = %d, search %s", ret, search);
+					break;
+				}
+
+				if ((next_number > next_block) && (next_block != 0)) {
+					BTMTK_DBG("find next apcf number is over to next block ");
+					BTMTK_DBG("temp_len %d, break, search %s",
+						temp_len, search);
+					break;
+				}
+
+				search_result = search_end + 1;
+			} while (1);
+		} else
+			BTMTK_DBG("%s is not found", search);
+
+		if (temp_len) {
+			BTMTK_INFO("%s found", search);
+			BTMTK_DBG("kzalloc i=%d temp_len=%d", i, temp_len);
+			save_content[i].content = kzalloc(temp_len, GFP_KERNEL);
+			memcpy(save_content[i].content, temp, temp_len);
+			save_content[i].length = temp_len;
+			BTMTK_DBG("x  save_content[%d].length %d temp_len=%d",
+				i, save_content[i].length, temp_len);
+		}
+
+	}
+	return ret;
+}
+
+static bool btmtk_sdio_load_bt_cfg_item(struct bt_cfg_struct *bt_cfg_content,
+					char *searchcontent)
+{
+	bool ret = true;
+	char text[128]; /* save for search text */
+	unsigned long text_value = 0;
+
+	memset(text, 0, sizeof(text));
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_UNIFY_WOBLE, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_unify_woble);
+		BTMTK_INFO("bt_cfg_content->support_unify_woble = %d",
+				bt_cfg_content->support_unify_woble);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_UNIFY_WOBLE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_UNIFY_WOBLE_TYPE, text, searchcontent);
+	if (ret) {
+		if (kstrtoul(text, 10, &text_value) == 0)
+			bt_cfg_content->unify_woble_type = text_value;
+		else
+			BTMTK_WARN("kstrtoul failed %s!", BT_UNIFY_WOBLE_TYPE);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_UNIFY_WOBLE_TYPE);
+	}
+
+	BTMTK_INFO("bt_cfg_content->unify_woble_type = %d", bt_cfg_content->unify_woble_type);
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_LEGACY_WOBLE, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_legacy_woble);
+		BTMTK_INFO("bt_cfg_content->support_legacy_woble = %d",
+			bt_cfg_content->support_legacy_woble);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_LEGACY_WOBLE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_WOBLE_BY_EINT, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_by_eint);
+		BTMTK_INFO("bt_cfg_content->support_woble_by_eint = %d",
+					bt_cfg_content->support_woble_by_eint);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_WOBLE_BY_EINT);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_DONGLE_RESET_PIN, text, searchcontent);
+	if (ret) {
+		if (kstrtoul(text, 10, &text_value) == 0)
+			bt_cfg_content->dongle_reset_gpio_pin = text_value;
+		else
+			BTMTK_WARN("kstrtoul failed %s!", BT_DONGLE_RESET_PIN);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_DONGLE_RESET_PIN);
+	}
+
+	BTMTK_INFO("bt_cfg_content->dongle_reset_gpio_pin = %d",
+			bt_cfg_content->dongle_reset_gpio_pin);
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_SAVE_FW_DUMP_IN_KERNEL, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->save_fw_dump_in_kernel);
+		BTMTK_INFO("bt_cfg_content->save_fw_dump_in_kernel = %d",
+				bt_cfg_content->save_fw_dump_in_kernel);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_SAVE_FW_DUMP_IN_KERNEL);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_RESET_DONGLE, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_dongle_reset);
+		BTMTK_INFO("bt_cfg_content->support_dongle_reset = %d",
+				bt_cfg_content->support_dongle_reset);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_RESET_DONGLE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_FULL_FW_DUMP, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_full_fw_dump);
+		BTMTK_INFO("bt_cfg_content->support_full_fw_dump = %d",
+				bt_cfg_content->support_full_fw_dump);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_FULL_FW_DUMP);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_WOBLE_WAKELOCK, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_wakelock);
+		BTMTK_INFO("bt_cfg_content->support_woble_wakelock = %d",
+				bt_cfg_content->support_woble_wakelock);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_WOBLE_WAKELOCK);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_WOBLE_FOR_BT_DISABLE, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_for_bt_disable);
+		BTMTK_INFO("bt_cfg_content->support_woble_for_bt_disable = %d",
+				bt_cfg_content->support_woble_for_bt_disable);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_WOBLE_FOR_BT_DISABLE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_RESET_STACK_AFTER_WOBLE, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->reset_stack_after_woble);
+		BTMTK_INFO("%s: bt_cfg_content->reset_stack_after_woble = %d", __func__,
+				bt_cfg_content->reset_stack_after_woble);
+	} else {
+		BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_RESET_STACK_AFTER_WOBLE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_SYS_LOG_FILE, text, searchcontent);
+	if (ret) {
+		if (bt_cfg_content->sys_log_file_name != NULL) {
+			kfree(bt_cfg_content->sys_log_file_name);
+			bt_cfg_content->sys_log_file_name = NULL;
+		}
+		bt_cfg_content->sys_log_file_name = kzalloc(strlen(text) + 1, GFP_KERNEL);
+		if (bt_cfg_content->sys_log_file_name == NULL) {
+			ret = false;
+			return ret;
+		}
+		memcpy(bt_cfg_content->sys_log_file_name, text, strlen(text));
+		bt_cfg_content->sys_log_file_name[strlen(text)] = '\0';
+		BTMTK_INFO("bt_cfg_content->sys_log_file_name = %s",
+				bt_cfg_content->sys_log_file_name);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_SYS_LOG_FILE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_FW_DUMP_FILE, text, searchcontent);
+	if (ret) {
+		if (bt_cfg_content->fw_dump_file_name != NULL) {
+			kfree(bt_cfg_content->fw_dump_file_name);
+			bt_cfg_content->fw_dump_file_name = NULL;
+		}
+		bt_cfg_content->fw_dump_file_name = kzalloc(strlen(text) + 1, GFP_KERNEL);
+		if (bt_cfg_content->fw_dump_file_name == NULL) {
+			ret = false;
+			return ret;
+		}
+		memcpy(bt_cfg_content->fw_dump_file_name, text, strlen(text));
+		bt_cfg_content->fw_dump_file_name[strlen(text)] = '\0';
+		BTMTK_INFO("bt_cfg_content->fw_dump_file_name = %s",
+				bt_cfg_content->fw_dump_file_name);
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_FW_DUMP_FILE);
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_AUTO_PICUS, text, searchcontent);
+	if (ret) {
+		btmtk_sdio_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_auto_picus);
+		BTMTK_INFO("bt_cfg_content->support_auto_picus = %d",
+				bt_cfg_content->support_auto_picus);
+		if (bt_cfg_content->support_auto_picus == true) {
+			ret = btmtk_sdio_load_fw_cfg_setting(BT_AUTO_PICUS_FILTER,
+					&bt_cfg_content->picus_filter, 1, searchcontent, FW_CFG_INX_LEN_NONE);
+			if (ret)
+				BTMTK_WARN("search item %s is invalid!", BT_AUTO_PICUS_FILTER);
+		}
+	} else {
+		BTMTK_WARN("search item %s is invalid!", BT_AUTO_PICUS);
+	}
+
+	ret = btmtk_sdio_load_fw_cfg_setting(BT_WMT_CMD, bt_cfg_content->wmt_cmd,
+				WMT_CMD_COUNT, searchcontent, FW_CFG_INX_LEN_3);
+	if (ret)
+		BTMTK_WARN("search item %s is invalid!", BT_WMT_CMD);
+
+	ret = btmtk_sdio_load_fw_cfg_setting(BT_VENDOR_CMD, bt_cfg_content->vendor_cmd,
+				VENDOR_CMD_COUNT, searchcontent, FW_CFG_INX_LEN_3);
+	if (ret)
+		BTMTK_WARN("search item %s is invalid!", BT_VENDOR_CMD);
+
+	/* release setting file memory */
+	if (g_card) {
+		kfree(g_card->setting_file);
+		g_card->setting_file = NULL;
+	}
+	return ret;
+}
+
+static void btmtk_sdio_load_woble_setting_callback(const struct firmware *fw_data, void *context)
+{
+	struct btmtk_sdio_card *card = (struct btmtk_sdio_card *)context;
+	int err = 0;
+	unsigned char *image = NULL;
+
+	if (!fw_data) {
+		BTMTK_ERR("fw_data is NULL callback request_firmware fail or can't find file!!");
+
+		/* Request original woble_setting.bin */
+		memcpy(g_card->woble_setting_file_name,
+				WOBLE_SETTING_FILE_NAME,
+				sizeof(WOBLE_SETTING_FILE_NAME));
+		BTMTK_INFO("begin load orignial woble_setting_file_name = %s",
+				g_card->woble_setting_file_name);
+		if (need_retry_load_woble < BTMTK_LOAD_WOBLE_RETRY_COUNT) {
+			need_retry_load_woble++;
+			err = request_firmware_nowait(THIS_MODULE, true, g_card->woble_setting_file_name,
+				&g_card->func->dev, GFP_KERNEL, g_card, btmtk_sdio_load_woble_setting_callback);
+			if (err < 0) {
+				BTMTK_ERR("request %s file fail(%d)",
+					g_card->woble_setting_file_name, err);
+			}
+		} else {
+			BTMTK_ERR("request %s file fail(%d), need_load_origin_woble = %d",
+				g_card->woble_setting_file_name, err, need_retry_load_woble);
+		}
+		return;
+	}
+
+	BTMTK_INFO("woble_setting callback request_firmware size %zu success", fw_data->size);
+	image = kzalloc(fw_data->size + 1, GFP_KERNEL);
+	if (image == NULL) {
+		BTMTK_ERR("kzalloc size %zu failed!!", fw_data->size);
+		goto LOAD_END;
+	}
+
+	memcpy(image, fw_data->data, fw_data->size);
+	image[fw_data->size] = '\0';
+
+	err = btmtk_sdio_load_fw_cfg_setting("APCF",
+			card->woble_setting_apcf, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("APCF_ADD_MAC",
+			card->woble_setting_apcf_fill_mac, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("APCF_ADD_MAC_LOCATION",
+			card->woble_setting_apcf_fill_mac_location, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("RADIOOFF",
+			card->woble_setting_radio_off, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("RADIOOFF_STATUS_EVENT",
+			card->woble_setting_radio_off_status_event, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	switch (g_card->bt_cfg.unify_woble_type) {
+	case 0:
+		err = btmtk_sdio_load_fw_cfg_setting("WAKEUP_TYPE_LEGACY",
+				card->woble_setting_wakeup_type, WOBLE_SETTING_COUNT,
+				image, FW_CFG_INX_LEN_2);
+		break;
+	case 1:
+		err = btmtk_sdio_load_fw_cfg_setting("WAKEUP_TYPE_WAVEFORM",
+				card->woble_setting_wakeup_type, WOBLE_SETTING_COUNT,
+				image, FW_CFG_INX_LEN_2);
+		break;
+	case 2:
+		err = btmtk_sdio_load_fw_cfg_setting("WAKEUP_TYPE_IR",
+				card->woble_setting_wakeup_type, WOBLE_SETTING_COUNT,
+				image, FW_CFG_INX_LEN_2);
+		break;
+	default:
+		BTMTK_WARN("%s: unify_woble_type unknown(%d)", __func__, g_card->bt_cfg.unify_woble_type);
+	}
+	if (err) {
+		BTMTK_WARN("Parse unify_woble_type(%d) failed", g_card->bt_cfg.unify_woble_type);
+		goto LOAD_END;
+	}
+
+	err = btmtk_sdio_load_fw_cfg_setting("RADIOOFF_COMPLETE_EVENT",
+			card->woble_setting_radio_off_comp_event, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("RADIOON",
+			card->woble_setting_radio_on, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("RADIOON_STATUS_EVENT",
+			card->woble_setting_radio_on_status_event, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("RADIOON_COMPLETE_EVENT",
+			card->woble_setting_radio_on_comp_event, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("APCF_RESMUE",
+		card->woble_setting_apcf_resume, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+	if (err)
+		goto LOAD_END;
+
+	err = btmtk_sdio_load_fw_cfg_setting("APCF_COMPLETE_EVENT",
+			card->woble_setting_apcf_resume_event, WOBLE_SETTING_COUNT, image, FW_CFG_INX_LEN_2);
+
+LOAD_END:
+	kfree(image);
+	release_firmware(fw_data);
+	if (err)
+		BTMTK_WARN("result fail");
+	else
+		BTMTK_INFO("result success");
+}
+
+static int btmtk_sdio_load_code_from_setting_files(char *setting_file_name,
+				struct btmtk_sdio_card *data, u32 *code_len)
+{
+	int err = 0;
+	const struct firmware *fw_entry = NULL;
+	*code_len = 0;
+
+	if (data == NULL || setting_file_name == NULL) {
+		BTMTK_ERR("invalid parameter!!");
+		err = -1;
+		return err;
+	}
+
+	err = request_firmware(&fw_entry, setting_file_name, &data->func->dev);
+	if (err != 0) {
+		BTMTK_ERR("request %s file fail(%d)", setting_file_name, err);
+		if (fw_entry)
+			release_firmware(fw_entry);
+		err = -2;
+		return err;
+	}
+
+	if (data->setting_file != NULL) {
+		kfree(data->setting_file);
+		data->setting_file = NULL;
+	}
+
+	if (fw_entry) {
+		/* alloc setting file memory */
+		data->setting_file = kzalloc(fw_entry->size + 1, GFP_KERNEL);
+		BTMTK_INFO("setting file request_firmware size %zu success", fw_entry->size);
+	} else {
+		BTMTK_ERR("fw_entry is NULL request_firmware may fail!! error code = %d", err);
+		err = -2;
+		return err;
+	}
+
+	if (data->setting_file == NULL) {
+		BTMTK_ERR("kzalloc size %zu failed!!", fw_entry->size);
+		release_firmware(fw_entry);
+		err = -3;
+		return err;
+	}
+
+	memcpy(data->setting_file, fw_entry->data, fw_entry->size);
+	data->setting_file[fw_entry->size] = '\0';
+
+	*code_len = fw_entry->size;
+	release_firmware(fw_entry);
+
+	BTMTK_INFO("cfg_file len (%d) assign done", *code_len);
+	return err;
+}
+
+static int btmtk_sdio_load_setting_files(char *bin_name, struct device *dev,
+					struct btmtk_sdio_card *data)
+{
+	int err = 0;
+	char *ptr_name = NULL;
+	u32 code_len = 0;
+
+	BTMTK_INFO("begin setting_file_name = %s", bin_name);
+	ptr_name = strstr(bin_name, "woble_setting");
+	if (ptr_name) {
+		err = request_firmware_nowait(THIS_MODULE, true, bin_name,
+			&data->func->dev, GFP_KERNEL, data, btmtk_sdio_load_woble_setting_callback);
+
+		if (err < 0)
+			BTMTK_ERR("request_firmware_nowait fail(%d)", err);
+		else
+			BTMTK_INFO("request %s file success(%d)", bin_name, err);
+	} else if (strstr(bin_name, BT_CFG_NAME_PREFIX) && strstr(bin_name, BT_CFG_NAME_SUFFIX)) {
+		err = btmtk_sdio_load_code_from_setting_files(bin_name, data, &code_len);
+		if (err != 0) {
+			if (err != -2) {
+				BTMTK_ERR("btmtk_sdio_load_code_from_cfg_files %s failed!!", bin_name);
+				return err;
+			}
+
+			err = btmtk_sdio_load_code_from_setting_files(BT_CFG_NAME, data, &code_len);
+			if (err != 0) {
+				BTMTK_ERR("btmtk_sdio_load_code_from_cfg_files %s failed!!", BT_CFG_NAME);
+				return err;
+			}
+			(void)snprintf(g_card->bt_cfg_file_name, MAX_BIN_FILE_NAME_LEN, "%s", BT_CFG_NAME);
+		}
+
+		if (btmtk_sdio_load_bt_cfg_item(&data->bt_cfg, data->setting_file)) {
+			BTMTK_ERR("btmtk_sdio_load_bt_cfg_item error!!");
+			err = -1;
+			return err;
+		}
+	} else
+		BTMTK_WARN("bin_name is not defined");
+
+	return err;
+}
+
+static int btmtk_sdio_send_cfg_power(void)
+{
+	int ret = -1;
+	uint8_t set_radio[] = {0x79, 0xFC, 0x08, 0x07, 0x80, 0x00, 0x06, 0x07, 0x07, 0x00, 0x00};
+	uint8_t set_radio_e[] = {0x0E, 0x04, 0x01, 0x79, 0xFC, 0x00};
+
+	/* BT default power */
+	set_radio[3] = g_card->power_cfg.BTLevel;
+	/* BLE default power */
+	set_radio[7] = g_card->power_cfg.BLELevel;
+	/* TX MAX power */
+	set_radio[8] = g_card->power_cfg.BTLevel;
+	/* TX power sub level */
+	set_radio[9] = g_card->power_cfg.SubLevel;
+	/* BR/EDR power diff mode */
+	set_radio[10] = g_card->power_cfg.PowerMode;
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, set_radio,
+		set_radio[2] + 3,
+		set_radio_e, sizeof(set_radio_e), COMP_EVENT_TIMO);
+
+	BTMTK_WARN("set customer radio(BT/BLE default power: %d/%d MAX power: %d) %s",
+			set_radio[3], set_radio[7], set_radio[8],
+			ret < 0 ? "fail" : "OK");
+
+	return ret;
+
+}
+
+static void btmtk_sdio_loading_power_file(void)
+{
+	char text[100]; /* save for search text */
+	int ret = 0, powerFileSize = 0;
+	unsigned int text_value;
+
+	ret = btmtk_sdio_load_code_from_setting_files(POWER_SETTING_FILE, g_card, &powerFileSize);
+	if (ret != 0) {
+		BTMTK_WARN("btmtk_sdio_load_code_from_cfg_files %s failed!!", POWER_SETTING_FILE);
+		return;
+	}
+
+	memset(text, 0, 100);
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_POWER_MODE, text, g_card->setting_file);
+	if (ret && (sscanf(text, " = %d", &text_value) > 0) && (text_value == 0 || text_value == 1))
+		g_card->power_cfg.PowerMode = text_value;
+	else {
+		BTMTK_WARN("%s: search or kstrtoul failed %s!", __func__, BT_POWER_MODE);
+		goto parse_end;
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BT_POWER_LEVEL, text, g_card->setting_file);
+	if (ret && (sscanf(text, " = %d", &text_value) > 0) && text_value <= 7)
+		g_card->power_cfg.BTLevel = text_value;
+	else {
+		BTMTK_WARN("%s: search or kstrtoul failed %s!", __func__, BT_POWER_LEVEL);
+		goto parse_end;
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(BLE_POWER_LEVEL, text, g_card->setting_file);
+	if (ret && (sscanf(text, " = %d", &text_value) > 0) && text_value <= 7)
+		g_card->power_cfg.BLELevel = text_value;
+	else {
+		BTMTK_WARN("%s: search or kstrtoul failed %s!", __func__, BLE_POWER_LEVEL);
+		goto parse_end;
+	}
+
+	ret = btmtk_sdio_parse_bt_cfg_file(SUB_POWER_LEVEL, text, g_card->setting_file);
+	if (ret && (sscanf(text, " = %d", &text_value) > 0) && text_value <= 3)
+		g_card->power_cfg.SubLevel = text_value;
+	else {
+		BTMTK_WARN("%s: search or kstrtoul failed %s!", __func__, SUB_POWER_LEVEL);
+		goto parse_end;
+	}
+
+	BTMTK_INFO("PowerMode=%d, BTLevel=%d, BLELevel=%d, SubLevel=%d",
+		g_card->power_cfg.PowerMode, g_card->power_cfg.BTLevel,
+		g_card->power_cfg.BLELevel,  g_card->power_cfg.SubLevel);
+
+	g_card->power_cfg.powerCfg = 1;
+
+parse_end:
+	/* release setting file memory */
+	if (g_card && g_card->setting_file) {
+		kfree(g_card->setting_file);
+		g_card->setting_file = NULL;
+	}
+
+	BTMTK_INFO("end");
+}
+
+static void SetRandomAddress(u8 btaddr[BD_ADDRESS_SIZE])
+{
+	int iRandom = 0;
+
+	BTMTK_INFO("Enable random generation\n");
+
+	/* first random */
+	get_random_bytes(&iRandom, sizeof(int));
+	btaddr[5] = (u8)(((iRandom>>24|iRandom>>16) & (0xFE)) | (0x02)); /* Must use private bit(1) and no BCMC bit(0) */
+
+	/* second random */
+	get_random_bytes(&iRandom, sizeof(int));
+	btaddr[4] = (u8)((iRandom>>8) & 0xFF);
+
+	/* third random */
+	get_random_bytes(&iRandom, sizeof(int));
+	btaddr[3] = (u8)((iRandom>>8) & 0xFF);
+
+	btaddr[2] = 0x76;
+	btaddr[1] = 0x63;
+
+	/* firth random */
+	get_random_bytes(&iRandom, sizeof(int));
+	btaddr[0] = (u8)(iRandom & 0xFF);
+
+	return;
+}
+
+static void btmtk_load_bt_mac_file(void)
+{
+	u8  defalutAddr[BD_ADDRESS_SIZE] = {0};
+	u32 tempAddr;
+	int i, ret = 0, addrFileSize = 0;
+
+	ret = btmtk_sdio_load_code_from_setting_files(BT_MACADDR_FILE, g_card, &addrFileSize);
+	if (ret != 0) {
+		BTMTK_WARN("btmtk_sdio_load_code_from_cfg_files %s failed!!", BT_MACADDR_FILE);
+		return;
+	}
+
+	for (i = 0; i < BD_ADDRESS_SIZE; ++i) {
+		ret = sscanf(g_card->setting_file + 2*i, "%02X", &tempAddr);
+		if(ret != 1) {
+			BTMTK_WARN("%s: Parser Mac error, ret=%d, i=%d!", __func__, ret, i);
+			goto AddrParse_end;
+		}
+		g_card->mac_btAddr[BD_ADDRESS_SIZE - i - 1] = (u8)(tempAddr & 0xff);
+	}
+
+	/*mac_bt is Zero Address--->use randomlize address*/
+	if(!memcmp(g_card->mac_btAddr, defalutAddr, BD_ADDRESS_SIZE))
+		SetRandomAddress(g_card->mac_btAddr);
+
+	g_card->mac_bt_flag = true;
+
+AddrParse_end:
+	/* release setting file memory */
+	if (g_card && g_card->setting_file) {
+		kfree(g_card->setting_file);
+		g_card->setting_file = NULL;
+	}
+
+	BTMTK_INFO("end");
+}
+
+static void btmtk_set_mac_power(void)
+{
+	int ret;
+	uint8_t set_bdaddr[] = {0x1A, 0xFC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	uint8_t set_bdaddr_e[] = {0x0E, 0x04, 0x01, 0x1A, 0xFC, 0x00};
+
+	memcpy(set_bdaddr + 3, g_card->mac_btAddr, BD_ADDRESS_SIZE);
+
+	if(g_card->mac_bt_flag == true) {
+
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, set_bdaddr,
+			set_bdaddr[2] + 3,
+			set_bdaddr_e, sizeof(set_bdaddr_e), COMP_EVENT_TIMO);
+		BTMTK_WARN("set User BDAddress(%02X-%02X-%02X-%02X-%02X-%02X) %s",
+				set_bdaddr[8], set_bdaddr[7], set_bdaddr[6],
+				set_bdaddr[5], set_bdaddr[4], set_bdaddr[3],
+				ret < 0 ? "fail" : "OK");
+	}
+
+	if (g_card->power_cfg.powerCfg == 1)
+		ret = btmtk_sdio_send_cfg_power();
+
+}
+
+static inline void btmtk_sdio_woble_wake_lock(struct btmtk_sdio_card *data)
+{
+	if (!data->woble_ws)
+		return;
+
+	if (data->bt_cfg.support_unify_woble && data->bt_cfg.support_woble_wakelock) {
+		BTMTK_INFO("wake lock");
+		__pm_stay_awake(data->woble_ws);
+	}
+}
+
+static inline void btmtk_sdio_woble_wake_unlock(struct btmtk_sdio_card *data)
+{
+	if (!data->woble_ws)
+		return;
+
+	if (data->bt_cfg.support_unify_woble && data->bt_cfg.support_woble_wakelock) {
+		BTMTK_INFO("wake unlock");
+		__pm_relax(data->woble_ws);
+	}
+}
+
+u32 LOCK_UNSLEEPABLE_LOCK(struct _OSAL_UNSLEEPABLE_LOCK_ *pUSL)
+{
+	spin_lock_irqsave(&(pUSL->lock), pUSL->flag);
+	return 0;
+}
+
+u32 UNLOCK_UNSLEEPABLE_LOCK(struct _OSAL_UNSLEEPABLE_LOCK_ *pUSL)
+{
+	spin_unlock_irqrestore(&(pUSL->lock), pUSL->flag);
+	return 0;
+}
+
+static int btmtk_sdio_writesb(u32 offset, u8 *val, int len)
+{
+	u32 ret = 0;
+	u32 retry_count = 0;
+
+	if (g_card->func == NULL) {
+		BTMTK_ERR("g_card->func is NULL");
+		return -EIO;
+	}
+
+	do {
+		sdio_claim_host(g_card->func);
+		ret = sdio_writesb(g_card->func, offset, val, len);
+		sdio_release_host(g_card->func);
+		retry_count++;
+		if (retry_count > BTMTK_SDIO_RETRY_COUNT) {
+			BTMTK_ERR("ret:%d", ret);
+			break;
+		}
+	} while (ret);
+
+	return ret;
+}
+
+
+static int btmtk_sdio_readsb(u32 offset, u8 *val, int len)
+{
+	u32 ret = 0;
+	u32 retry_count = 0;
+
+	if (g_card->func == NULL) {
+		BTMTK_ERR("g_card->func is NULL");
+		return -EIO;
+	}
+
+	do {
+		sdio_claim_host(g_card->func);
+		ret = sdio_readsb(g_card->func, val, offset, len);
+		sdio_release_host(g_card->func);
+		retry_count++;
+		if (retry_count > BTMTK_SDIO_RETRY_COUNT) {
+			BTMTK_ERR("ret:%d", ret);
+			break;
+		}
+	} while (ret);
+
+	return ret;
+}
+
+static int btmtk_sdio_writeb(u32 offset, u8 val)
+{
+	u32 ret = 0;
+	u32 retry_count = 0;
+
+	if (g_card->func == NULL) {
+		BTMTK_ERR("g_card->func is NULL");
+		return -EIO;
+	}
+
+	do {
+		sdio_claim_host(g_card->func);
+		sdio_writeb(g_card->func, val, offset, &ret);
+		sdio_release_host(g_card->func);
+		retry_count++;
+		if (retry_count > BTMTK_SDIO_RETRY_COUNT) {
+			BTMTK_ERR("ret:%d", ret);
+			break;
+		}
+	} while (ret);
+
+	return ret;
+}
+
+#if LOWER_POWER_SINK
+static inline void btmtk_sdio_lp_wake_lock(struct btmtk_sdio_card *data)
+{
+	__pm_stay_awake(data->lp_ws);
+}
+
+static inline void btmtk_sdio_lp_wake_unlock(struct btmtk_sdio_card *data)
+{
+	__pm_relax(data->lp_ws);
+}
+#endif
+
+static int btmtk_sdio_discard_opcode(struct sk_buff_head* queue, char *opcode)
+{
+	struct sk_buff *skb_opcode = NULL;
+	if(!queue || !opcode) {
+		BTMTK_ERR("%s invalid parameter!!", __func__);
+		return -EINVAL;
+	}
+
+	skb_opcode = alloc_skb(2, GFP_ATOMIC);
+	if (!skb_opcode) {
+		BTMTK_ERR("%s allocate skb failed!!", __func__);
+		return -ENOMEM;
+	}
+	memcpy(skb_put(skb_opcode, 2), opcode, 2);
+	skb_queue_tail(queue, skb_opcode);
+	return 0;
+}
+
+static int btmtk_sdio_readb(u32 offset, u8 *val)
+{
+	u32 ret = 0;
+	u32 retry_count = 0;
+
+	if (g_card->func == NULL) {
+		BTMTK_ERR("g_card->func is NULL");
+		return -EIO;
+	}
+
+	do {
+		sdio_claim_host(g_card->func);
+		*val = sdio_readb(g_card->func, offset, &ret);
+		sdio_release_host(g_card->func);
+		retry_count++;
+		if (retry_count > BTMTK_SDIO_RETRY_COUNT) {
+			BTMTK_ERR("ret:%d", ret);
+			break;
+		}
+	} while (ret);
+
+	return ret;
+}
+
+static int btmtk_sdio_writel(u32 offset, u32 val)
+{
+	u32 ret = 0;
+	u32 retry_count = 0;
+
+	if (g_card->func == NULL) {
+		BTMTK_ERR("g_card->func is NULL");
+		return -EIO;
+	}
+
+	do {
+		sdio_claim_host(g_card->func);
+		sdio_writel(g_card->func, val, offset, &ret);
+		sdio_release_host(g_card->func);
+		retry_count++;
+		if (retry_count > BTMTK_SDIO_RETRY_COUNT) {
+			BTMTK_ERR("ret:%d", ret);
+			break;
+		}
+	} while (ret);
+
+	return ret;
+}
+
+static int btmtk_sdio_readl(u32 offset,  u32 *val)
+{
+	u32 ret = 0;
+	u32 retry_count = 0;
+
+	if (g_card->func == NULL) {
+		BTMTK_ERR("g_card->func is NULL");
+		return -EIO;
+	}
+
+	do {
+		sdio_claim_host(g_card->func);
+		*val = sdio_readl(g_card->func, offset, &ret);
+		sdio_release_host(g_card->func);
+		retry_count++;
+		if (retry_count > BTMTK_SDIO_RETRY_COUNT) {
+			BTMTK_ERR("ret:%d", ret);
+			break;
+		}
+	} while (ret);
+
+	return ret;
+}
+
+static void btmtk_sdio_print_debug_sr(void)
+{
+	u32 ret = 0;
+	u32 CCIR_Value = 0;
+	u32 CHLPCR_Value = 0;
+	u32 CSDIOCSR_Value = 0;
+	u32 CHISR_Value = 0;
+	u32 CHIER_Value = 0;
+	u32 CTFSR_Value = 0;
+	u32 CRPLR_Value = 0;
+	u32 SWPCDBGR_Value = 0;
+	unsigned char X0_Value = 0;
+	unsigned char F8_Value = 0;
+	unsigned char F9_Value = 0;
+	unsigned char FA_Value = 0;
+	unsigned char FB_Value = 0;
+	unsigned char FC_Value = 0;
+	unsigned char FD_Value = 0;
+	unsigned char FE_Value = 0;
+	unsigned char FF_Value = 0;
+
+	ret = btmtk_sdio_readl(CCIR, &CCIR_Value);
+	ret = btmtk_sdio_readl(CHLPCR, &CHLPCR_Value);
+	ret = btmtk_sdio_readl(CSDIOCSR, &CSDIOCSR_Value);
+	ret = btmtk_sdio_readl(CHISR, &CHISR_Value);
+	ret = btmtk_sdio_readl(CHIER, &CHIER_Value);
+	ret = btmtk_sdio_readl(CTFSR, &CTFSR_Value);
+	ret = btmtk_sdio_readl(CRPLR, &CRPLR_Value);
+	ret = btmtk_sdio_readl(SWPCDBGR, &SWPCDBGR_Value);
+	sdio_claim_host(g_card->func);
+	X0_Value = sdio_f0_readb(g_card->func, 0x00, &ret);
+	F8_Value = sdio_f0_readb(g_card->func, 0xF8, &ret);
+	F9_Value = sdio_f0_readb(g_card->func, 0xF9, &ret);
+	FA_Value = sdio_f0_readb(g_card->func, 0xFA, &ret);
+	FB_Value = sdio_f0_readb(g_card->func, 0xFB, &ret);
+	FC_Value = sdio_f0_readb(g_card->func, 0xFC, &ret);
+	FD_Value = sdio_f0_readb(g_card->func, 0xFD, &ret);
+	FE_Value = sdio_f0_readb(g_card->func, 0xFE, &ret);
+	FF_Value = sdio_f0_readb(g_card->func, 0xFF, &ret);
+	sdio_release_host(g_card->func);
+	BTMTK_INFO("CCIR: 0x%x, CHLPCR: 0x%x, CSDIOCSR: 0x%x, CHISR: 0x%x",
+		CCIR_Value, CHLPCR_Value, CSDIOCSR_Value, CHISR_Value);
+	BTMTK_INFO("CHIER: 0x%x, CTFSR: 0x%x, CRPLR: 0x%x, SWPCDBGR: 0x%x",
+		CHIER_Value, CTFSR_Value, CRPLR_Value, SWPCDBGR_Value);
+	BTMTK_INFO("CCCR 00: 0x%x, F8: 0x%x, F9: 0x%x, FA: 0x%x, FB: 0x%x",
+		X0_Value, F8_Value, F9_Value, FA_Value, FB_Value);
+	BTMTK_INFO("FC: 0x%x, FD: 0x%x, FE: 0x%x, FE: 0x%x",
+		FC_Value, FD_Value, FE_Value, FF_Value);
+}
+
+
+static void btmtk_sdio_hci_snoop_save(u8 type, u8 *buf, u32 len)
+{
+	u32 copy_len = HCI_SNOOP_BUF_SIZE;
+
+	if (buf == NULL || len == 0)
+		return;
+
+	if (len < HCI_SNOOP_BUF_SIZE)
+		copy_len = len;
+
+	switch (type) {
+	case HCI_COMMAND_PKT:
+		hci_cmd_snoop_len[hci_cmd_snoop_index] = copy_len & 0xff;
+		memset(hci_cmd_snoop_buf[hci_cmd_snoop_index], 0, HCI_SNOOP_BUF_SIZE);
+		memcpy(hci_cmd_snoop_buf[hci_cmd_snoop_index], buf, copy_len & 0xff);
+		hci_cmd_snoop_timestamp[hci_cmd_snoop_index] = btmtk_sdio_get_microseconds();
+
+		if (hci_cmd_snoop_index > 0)
+			hci_cmd_snoop_index--;
+		else
+			hci_cmd_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+		break;
+	case HCI_ACLDATA_PKT:
+		hci_acl_snoop_len[hci_acl_snoop_index] = copy_len & 0xff;
+		memset(hci_acl_snoop_buf[hci_acl_snoop_index], 0, HCI_SNOOP_BUF_SIZE);
+		memcpy(hci_acl_snoop_buf[hci_acl_snoop_index], buf, copy_len & 0xff);
+		hci_acl_snoop_timestamp[hci_acl_snoop_index] = btmtk_sdio_get_microseconds();
+
+		if (hci_acl_snoop_index > 0)
+			hci_acl_snoop_index--;
+		else
+			hci_acl_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+		break;
+	case HCI_EVENT_PKT:
+		if (buf[0] == 0x3E) /* Not save BLE Event */
+			break;
+		hci_event_snoop_len[hci_event_snoop_index] = copy_len;
+		memset(hci_event_snoop_buf[hci_event_snoop_index], 0,
+			HCI_SNOOP_BUF_SIZE);
+		memcpy(hci_event_snoop_buf[hci_event_snoop_index], buf, copy_len);
+		hci_event_snoop_timestamp[hci_event_snoop_index] = btmtk_sdio_get_microseconds();
+
+		if (hci_event_snoop_index > 0)
+			hci_event_snoop_index--;
+		else
+			hci_event_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+		break;
+	case FW_LOG_PKT:
+		fw_log_snoop_len[fw_log_snoop_index] = copy_len;
+		memset(fw_log_snoop_buf[fw_log_snoop_index], 0,
+			HCI_SNOOP_BUF_SIZE);
+		memcpy(fw_log_snoop_buf[fw_log_snoop_index], buf, copy_len);
+		fw_log_snoop_timestamp[fw_log_snoop_index] = btmtk_sdio_get_microseconds();
+
+		if (fw_log_snoop_index > 0)
+			fw_log_snoop_index--;
+		else
+			fw_log_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+		break;
+	default:
+		BTSDIO_INFO_RAW(buf, len, "type(%d):", type);
+	}
+}
+
+static void btmtk_sdio_hci_snoop_print(void)
+{
+	unsigned int counter, index;
+
+	BTMTK_INFO("HCI Command Dump");
+	index = hci_cmd_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (hci_cmd_snoop_len[index] != 0)
+			BTSDIO_INFO_RAW(hci_cmd_snoop_buf[index], hci_cmd_snoop_len[index],
+				"	time(%u):", hci_cmd_snoop_timestamp[index]);
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+
+	BTMTK_INFO("HCI Event Dump");
+	index = hci_event_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (hci_event_snoop_len[index] != 0)
+			BTSDIO_INFO_RAW(hci_event_snoop_buf[index], hci_event_snoop_len[index],
+				"	time(%u):", hci_event_snoop_timestamp[index]);
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+
+	BTMTK_INFO("HCI ACL Dump");
+	index = hci_acl_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (hci_acl_snoop_len[index] != 0)
+			BTSDIO_INFO_RAW(hci_acl_snoop_buf[index], hci_acl_snoop_len[index],
+				"	time(%u):", hci_acl_snoop_timestamp[index]);
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+
+	BTMTK_INFO("FW LOG Dump");
+	index = fw_log_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (fw_log_snoop_len[index] != 0)
+			BTSDIO_INFO_RAW(fw_log_snoop_buf[index], fw_log_snoop_len[index],
+				"	time(%u):", fw_log_snoop_timestamp[index]);
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+}
+
+static void btmtk_sdio_hci_snoop_init(void)
+{
+	hci_cmd_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	hci_event_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	hci_acl_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	fw_log_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+
+	memset(hci_cmd_snoop_buf, 0, HCI_SNOOP_ENTRY_NUM * HCI_SNOOP_BUF_SIZE * sizeof(u8));
+	memset(hci_cmd_snoop_len, 0, HCI_SNOOP_ENTRY_NUM * sizeof(u8));
+	memset(hci_cmd_snoop_timestamp, 0, HCI_SNOOP_ENTRY_NUM * sizeof(unsigned int));
+
+	memset(hci_event_snoop_buf, 0, HCI_SNOOP_ENTRY_NUM * HCI_SNOOP_BUF_SIZE * sizeof(u8));
+	memset(hci_event_snoop_len, 0, HCI_SNOOP_ENTRY_NUM * sizeof(u8));
+	memset(hci_event_snoop_timestamp, 0, HCI_SNOOP_ENTRY_NUM * sizeof(unsigned int));
+
+	memset(hci_acl_snoop_buf, 0, HCI_SNOOP_ENTRY_NUM * HCI_SNOOP_BUF_SIZE * sizeof(u8));
+	memset(hci_acl_snoop_len, 0, HCI_SNOOP_ENTRY_NUM * sizeof(u8));
+	memset(hci_acl_snoop_timestamp, 0, HCI_SNOOP_ENTRY_NUM * sizeof(unsigned int));
+
+	memset(fw_log_snoop_buf, 0, HCI_SNOOP_ENTRY_NUM * HCI_SNOOP_BUF_SIZE * sizeof(u8));
+	memset(fw_log_snoop_len, 0, HCI_SNOOP_ENTRY_NUM * sizeof(u8));
+	memset(fw_log_snoop_timestamp, 0, HCI_SNOOP_ENTRY_NUM * sizeof(unsigned int));
+}
+
+struct sk_buff *btmtk_create_send_data(struct sk_buff *skb)
+{
+	struct sk_buff *queue_skb = NULL;
+	u32 sdio_header_len = skb->len + BTM_HEADER_LEN;
+
+	if (skb_headroom(skb) < (BTM_HEADER_LEN)) {
+		queue_skb = bt_skb_alloc(sdio_header_len, GFP_ATOMIC);
+		if (queue_skb == NULL) {
+			BTMTK_ERR("bt_skb_alloc fail return");
+			return 0;
+		}
+
+		queue_skb->data[0] = (sdio_header_len & 0x0000ff);
+		queue_skb->data[1] = (sdio_header_len & 0x00ff00) >> 8;
+		queue_skb->data[2] = 0;
+		queue_skb->data[3] = 0;
+		queue_skb->data[4] = bt_cb(skb)->pkt_type;
+		queue_skb->len = sdio_header_len;
+		memcpy(&queue_skb->data[5], &skb->data[0], skb->len);
+		kfree_skb(skb);
+	} else {
+		queue_skb = skb;
+		skb_push(queue_skb, BTM_HEADER_LEN);
+		queue_skb->data[0] = (sdio_header_len & 0x0000ff);
+		queue_skb->data[1] = (sdio_header_len & 0x00ff00) >> 8;
+		queue_skb->data[2] = 0;
+		queue_skb->data[3] = 0;
+		queue_skb->data[4] = bt_cb(skb)->pkt_type;
+	}
+
+	BTMTK_INFO("end");
+	return queue_skb;
+}
+
+static void btmtk_sdio_set_no_fw_own(struct btmtk_private *priv, bool no_fw_own)
+{
+	if (priv) {
+		priv->no_fw_own = no_fw_own;
+		BTMTK_DBG("set no_fw_own %d", priv->no_fw_own);
+	} else
+		BTMTK_DBG("priv is NULL");
+}
+
+static int btmtk_sdio_set_own_back(int owntype)
+{
+	/*Set driver own*/
+	int ret = 0, retry_ret = 0;
+	u32 u32LoopCount = 0;
+	u32 u32ReadCRValue = 0;
+	u32 ownValue = 0;
+	u32 set_checkretry = 30;
+	int i = 0;
+
+	BTMTK_DBG("owntype %d", owntype);
+
+	if (user_rmmod)
+		set_checkretry = 1;
+
+
+	if (owntype == FW_OWN && (g_priv)) {
+		if (g_priv->no_fw_own) {
+			ret = btmtk_sdio_readl(SWPCDBGR, &u32ReadCRValue);
+			BTMTK_DBG("no_fw_own is on, just return, u32ReadCRValue = 0x%08X, ret = %d",
+				u32ReadCRValue, ret);
+			return ret;
+		}
+	}
+
+	ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+
+	BTMTK_DBG("btmtk_sdio_readl  CHLPCR done");
+	if (owntype == DRIVER_OWN) {
+		if ((u32ReadCRValue&0x100) == 0x100) {
+			BTMTK_DBG("already driver own 0x%0x, return", u32ReadCRValue);
+			goto set_own_end;
+		}
+	} else if (owntype == FW_OWN) {
+		if ((u32ReadCRValue&0x100) == 0) {
+			BTMTK_DBG("already FW own 0x%0x, return", u32ReadCRValue);
+			goto set_own_end;
+		}
+	}
+
+setretry:
+
+	if (owntype == DRIVER_OWN)
+		ownValue = 0x00000200;
+	else
+		ownValue = 0x00000100;
+
+	BTMTK_DBG("write CHLPCR 0x%x", ownValue);
+	ret = btmtk_sdio_writel(CHLPCR, ownValue);
+	if (ret) {
+		ret = -EINVAL;
+		goto done;
+	}
+	BTMTK_DBG("write CHLPCR 0x%x done", ownValue);
+
+	u32LoopCount = 1000;
+
+	if (owntype == DRIVER_OWN) {
+		do {
+			usleep_range(100, 200);
+			ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+			u32LoopCount--;
+			BTMTK_DBG("DRIVER_OWN btmtk_sdio_readl CHLPCR 0x%x", u32ReadCRValue);
+		} while ((u32LoopCount > 0) &&
+			((u32ReadCRValue&0x100) != 0x100));
+
+		if ((u32LoopCount == 0) && (0x100 != (u32ReadCRValue&0x100))
+				&& (set_checkretry > 0)) {
+			BTMTK_WARN("retry set_check driver own, CHLPCR 0x%x", u32ReadCRValue);
+			for (i = 0; i < 3; i++) {
+				ret = btmtk_sdio_readl(SWPCDBGR, &u32ReadCRValue);
+				BTMTK_WARN("ret %d,SWPCDBGR 0x%x, and not sleep!", ret, u32ReadCRValue);
+			}
+			btmtk_sdio_print_debug_sr();
+
+			set_checkretry--;
+			msleep(20);
+			goto setretry;
+		}
+	} else {
+		do {
+			usleep_range(100, 200);
+			ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+			u32LoopCount--;
+			BTMTK_DBG("FW_OWN btmtk_sdio_readl CHLPCR 0x%x", u32ReadCRValue);
+		} while ((u32LoopCount > 0) && ((u32ReadCRValue&0x100) != 0));
+
+		if ((u32LoopCount == 0) &&
+				((u32ReadCRValue&0x100) != 0) &&
+				(set_checkretry > 0)) {
+			BTMTK_WARN("retry set_check FW own, CHLPCR 0x%x", u32ReadCRValue);
+			set_checkretry--;
+			goto setretry;
+		}
+	}
+
+	BTMTK_DBG("CHLPCR(0x%x), is 0x%x", CHLPCR, u32ReadCRValue);
+
+	if (owntype == DRIVER_OWN) {
+		if ((u32ReadCRValue&0x100) == 0x100)
+			BTMTK_DBG("check %04x, is 0x100 driver own success", CHLPCR);
+		else {
+			BTMTK_DBG("check %04x, is %x shuld be 0x100", CHLPCR, u32ReadCRValue);
+			ret = -EINVAL;
+			goto done;
+		}
+	} else {
+		if (0x0 == (u32ReadCRValue&0x100))
+			BTMTK_DBG("check %04x, bit 8 is 0 FW own success", CHLPCR);
+		else{
+			BTMTK_DBG("bit 8 should be 0, %04x bit 8 is %04x", u32ReadCRValue,
+				(u32ReadCRValue&0x100));
+			ret = -EINVAL;
+			goto done;
+		}
+	}
+
+done:
+	if (owntype == DRIVER_OWN) {
+		if (ret) {
+			BTMTK_ERR("set driver own fail");
+			for (i = 0; i < 8; i++) {
+				retry_ret = btmtk_sdio_readl(SWPCDBGR, &u32ReadCRValue);
+				BTMTK_ERR("ret %d,SWPCDBGR 0x%x, then sleep 200ms", retry_ret, u32ReadCRValue);
+				msleep(200);
+			}
+		} else
+			BTMTK_DBG("set driver own success");
+	} else if (owntype == FW_OWN) {
+		if (ret)
+			BTMTK_ERR("set FW own fail");
+		else
+			BTMTK_DBG("set FW own success");
+	} else
+		BTMTK_ERR("unknown type %d", owntype);
+
+set_own_end:
+	if (ret)
+		btmtk_sdio_print_debug_sr();
+
+	return ret;
+}
+
+static int btmtk_sdio_enable_interrupt(int enable)
+{
+	u32 ret = 0;
+	u32 cr_value = 0;
+
+	if (enable)
+		cr_value |= C_FW_INT_EN_SET;
+	else
+		cr_value |= C_FW_INT_EN_CLEAR;
+
+	ret = btmtk_sdio_writel(CHLPCR, cr_value);
+	BTMTK_DBG("enable %d write CHLPCR 0x%08x", enable, cr_value);
+
+	return ret;
+}
+
+static int btmtk_sdio_get_rx_unit(struct btmtk_sdio_card *card)
+{
+	u8 reg;
+	int ret;
+
+	ret = btmtk_sdio_readb(card->reg->card_rx_unit, &reg);
+	if (!ret)
+		card->rx_unit = reg;
+
+	return ret;
+}
+
+static int btmtk_sdio_enable_host_int_mask(
+				struct btmtk_sdio_card *card,
+				u8 mask)
+{
+	int ret;
+
+	ret = btmtk_sdio_writeb(card->reg->host_int_mask, mask);
+	if (ret) {
+		BTMTK_ERR("Unable to enable the host interrupt!");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_disable_host_int_mask(
+				struct btmtk_sdio_card *card,
+				u8 mask)
+{
+	u8 host_int_mask;
+	int ret;
+
+	ret = btmtk_sdio_readb(card->reg->host_int_mask, &host_int_mask);
+	if (ret)
+		return -EIO;
+
+	host_int_mask &= ~mask;
+
+	ret = btmtk_sdio_writeb(card->reg->host_int_mask, host_int_mask);
+	if (ret < 0) {
+		BTMTK_ERR("Unable to disable the host interrupt!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*for debug*/
+int btmtk_print_buffer_conent(u8 *buf, u32 Datalen)
+{
+	int i = 0;
+	int print_finish = 0;
+	/*Print out txbuf data for debug*/
+	for (i = 0; i <= (Datalen-1); i += 16) {
+		if ((i+16) <= Datalen) {
+			BTMTK_DBG("%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X %02X%02X%02X%02X%02X %02X",
+				buf[i], buf[i+1], buf[i+2], buf[i+3],
+				buf[i+4], buf[i+5], buf[i+6], buf[i+7],
+				buf[i+8], buf[i+9], buf[i+10], buf[i+11],
+				buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
+		} else {
+			for (; i < (Datalen); i++)
+				BTMTK_DBG("%02X", buf[i]);
+
+			print_finish = 1;
+		}
+
+		if (print_finish)
+			break;
+	}
+	return 0;
+}
+
+static int btmtk_sdio_send_tx_data(u8 *buffer, int tx_data_len)
+{
+	int ret = 0;
+	u8 MultiBluckCount = 0;
+	u8 redundant = 0;
+
+	MultiBluckCount = tx_data_len/SDIO_BLOCK_SIZE;
+	redundant = tx_data_len % SDIO_BLOCK_SIZE;
+
+	if (redundant)
+		tx_data_len = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
+
+	ret = btmtk_sdio_writesb(CTDR, buffer, tx_data_len);
+	return ret;
+}
+
+static int btmtk_sdio_recv_rx_data(void)
+{
+	int ret = 0;
+	u32 u32ReadCRValue = 0;
+	int retry_count = 500;
+	u32 sdio_header_length = 0;
+
+	memset(rxbuf, 0, MTK_RXDATA_SIZE);
+
+	do {
+		ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+		BTMTK_DBG("loop Get CHISR 0x%08X", u32ReadCRValue);
+		reg_CHISR = u32ReadCRValue;
+		rx_length = (u32ReadCRValue & RX_PKT_LEN) >> 16;
+		if (rx_length == 0xFFFF) {
+			BTMTK_WARN("0xFFFF==rx_length, error return -EIO");
+			ret = -EIO;
+			break;
+		}
+
+		if ((RX_DONE&u32ReadCRValue) && rx_length) {
+			if (rx_length > MTK_RXDATA_SIZE) {
+				BTMTK_ERR("rx_length %d is bigger than MTK_RXDATA_SIZE %d",
+					rx_length, MTK_RXDATA_SIZE);
+				ret = -EIO;
+				break;
+			}
+
+			BTMTK_DBG("u32ReadCRValue = %08X", u32ReadCRValue);
+			u32ReadCRValue &= 0xFFFB;
+			ret = btmtk_sdio_writel(CHISR, u32ReadCRValue);
+			BTMTK_DBG("write = %08X", u32ReadCRValue);
+
+
+			ret = btmtk_sdio_readsb(CRDR, rxbuf, rx_length);
+			sdio_header_length = (rxbuf[1] << 8);
+			sdio_header_length |= rxbuf[0];
+
+			if (sdio_header_length != rx_length) {
+				BTMTK_ERR("sdio header length %d, rx_length %d mismatch",
+					sdio_header_length, rx_length);
+				break;
+			}
+
+			if (sdio_header_length == 0) {
+				BTMTK_WARN("get sdio_header_length = %d", sdio_header_length);
+				continue;
+			}
+
+			break;
+		}
+
+		retry_count--;
+		if (retry_count <= 0) {
+			BTMTK_WARN("retry_count = %d,timeout", retry_count);
+			btmtk_sdio_print_debug_sr();
+			ret = -EIO;
+			break;
+		}
+
+		/* msleep(1); */
+		mdelay(3);
+		BTMTK_DBG("retry_count = %d,wait", retry_count);
+
+		if (ret)
+			break;
+	} while (1);
+
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static int btmtk_sdio_send_wmt_cmd(u8 *cmd, int cmd_len,
+		const u8 *event, const int event_len)
+{
+	int ret = 0;
+	u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+
+	BTMTK_INFO("begin");
+	mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + cmd_len + 1;
+
+	memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+	memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE + 1, cmd, cmd_len);
+	txbuf[MTK_SDIO_PACKET_HEADER_SIZE] = 0x1;
+
+	btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE + cmd_len + 1);
+	btmtk_sdio_recv_rx_data();
+
+	if (event && event_len) {
+		/*compare rx data is wmt reset correct response or not*/
+		if (memcmp(event, rxbuf + MTK_SDIO_PACKET_HEADER_SIZE + 1, event_len) != 0) {
+			ret = -EIO;
+			BTMTK_WARN("fail");
+		}
+	} else {
+		BTSDIO_INFO_RAW(cmd, cmd_len, "%s: CMD:", __func__);
+		BTSDIO_INFO_RAW(rxbuf + MTK_SDIO_PACKET_HEADER_SIZE + 1,
+			rx_length - MTK_SDIO_PACKET_HEADER_SIZE - 1,
+			"EVT:");
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_send_wmt_cfg(void)
+{
+	int ret = 0;
+	int index = 0;
+
+	BTMTK_INFO("begin");
+
+	for (index = 0; index < WMT_CMD_COUNT; index++) {
+		if (g_card->bt_cfg.wmt_cmd[index].content && g_card->bt_cfg.wmt_cmd[index].length) {
+			ret = btmtk_sdio_send_wmt_cmd(g_card->bt_cfg.wmt_cmd[index].content,
+					g_card->bt_cfg.wmt_cmd[index].length,
+					NULL, 0);
+			if (ret) {
+				BTMTK_ERR("Send wmt cmd failed(%d)! Index: %d", ret, index);
+				return ret;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_send_wmt_reset(void)
+{
+	int ret = 0;
+	u8 wmt_event[8] = {4, 0xE4, 5, 2, 7, 1, 0, 0};
+	u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+	u8 mtksdio_wmt_reset[9] = {1, 0x6F, 0xFC, 5, 1, 7, 1, 0, 4};
+
+	BTMTK_INFO("begin");
+	mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) +
+		sizeof(mtksdio_wmt_reset);
+
+	memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+	memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_reset,
+		sizeof(mtksdio_wmt_reset));
+
+	btmtk_sdio_send_tx_data(txbuf,
+		MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_reset));
+	btmtk_sdio_recv_rx_data();
+
+	/*compare rx data is wmt reset correct response or not*/
+	if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,
+			sizeof(wmt_event)) != 0) {
+		ret = -EIO;
+		BTMTK_WARN("fail");
+	}
+
+	return ret;
+}
+
+static u32 btmtk_sdio_bt_memRegister_read(u32 cr)
+{
+	int retrytime = 3;
+	u32 result = 0;
+	u8 wmt_event[15] = {0x04, 0xE4, 0x10, 0x02,
+				0x08, 0x0C/*0x1C*/, 0x00, 0x00,
+				0x00, 0x00, 0x01, 0x00,
+				0x00, 0x00, 0x80};
+	/* msleep(1000); */
+	u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+	u8 mtksdio_wmt_cmd[16] = {0x1, 0x6F, 0xFC, 0x0C,
+				0x01, 0x08, 0x08, 0x00,
+				0x02, 0x01, 0x00, 0x01,
+				0x00, 0x00, 0x00, 0x00};
+	mtksdio_packet_header[0] = sizeof(mtksdio_packet_header)
+				+ sizeof(mtksdio_wmt_cmd);
+	BTMTK_INFO("read cr %x", cr);
+
+	memcpy(&mtksdio_wmt_cmd[12], &cr, sizeof(cr));
+
+	memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+	memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_cmd,
+		sizeof(mtksdio_wmt_cmd));
+
+	btmtk_sdio_send_tx_data(txbuf,
+		MTK_SDIO_PACKET_HEADER_SIZE + sizeof(mtksdio_wmt_cmd));
+	btmtk_print_buffer_conent(txbuf,
+		MTK_SDIO_PACKET_HEADER_SIZE + sizeof(mtksdio_wmt_cmd));
+
+	do {
+		usleep_range(10*1000, 15*1000);
+		btmtk_sdio_recv_rx_data();
+		retrytime--;
+		if (retrytime <= 0)
+			break;
+
+		BTMTK_INFO("retrytime %d", retrytime);
+	} while (!rxbuf[0]);
+
+	btmtk_print_buffer_conent(rxbuf, rx_length);
+	/* compare rx data is wmt reset correct response or not */
+#if 0
+	if (memcmp(wmt_event,
+			rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,
+			sizeof(wmt_event)) != 0) {
+		ret = -EIO;
+		BTMTK_INFO("fail");
+	}
+#endif
+	memcpy(&result, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE + sizeof(wmt_event),
+		sizeof(result));
+	BTMTK_INFO("ger cr 0x%x value 0x%x", cr, result);
+	return result;
+}
+
+static int btmtk_sdio_send_hci_reset(bool is_closed)
+{
+	int ret = 0;
+	u8 event[] = {0x0e, 0x04, 0x01, 0x03, 0x0c, 0x00};
+	u8 cmd[] = {0x03, 0x0c, 0x00};
+
+	BTMTK_INFO("begin\n");
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+		cmd, sizeof(cmd),
+		event, sizeof(event), COMP_EVENT_TIMO);
+	if (ret != 0)
+		BTMTK_ERR("ret = %d\n", ret);
+
+	if (is_closed == true)
+		get_hci_reset = 0;
+	return ret;
+}
+
+/* 1:on ,  0:off */
+static int btmtk_sdio_bt_set_power(u8 onoff)
+{
+	int ret = 0;
+	int count = 0;
+	u8 event[] = {0xE4, 0x05, 0x02, 0x06, 0x01, 0x00, 0x00};
+	u8 cmd[] = {0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x01};
+
+	BTMTK_INFO("onoff %d", onoff);
+	cmd[8] = onoff;
+
+	do {
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+			event, sizeof(event), COMP_EVENT_TIMO);
+		if (ret)
+			g_card->dongle_state = BT_SDIO_DONGLE_STATE_ERROR;
+		else
+			g_card->dongle_state =
+				onoff ? BT_SDIO_DONGLE_STATE_POWER_ON : BT_SDIO_DONGLE_STATE_POWER_OFF;
+	} while (++count < SET_POWER_NUM && ret);
+
+	return ret;
+}
+
+static int btmtk_sdio_send_vendor_cfg(void)
+{
+	int ret = 0;
+	int index = 0;
+
+	BTMTK_INFO("begin");
+
+	for (index = 0; index < VENDOR_CMD_COUNT; index++) {
+		if (g_card->bt_cfg.vendor_cmd[index].content && g_card->bt_cfg.vendor_cmd[index].length) {
+			ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+					g_card->bt_cfg.vendor_cmd[index].content,
+					g_card->bt_cfg.vendor_cmd[index].length,
+					NULL, 0, COMP_EVENT_TIMO);
+			if (ret) {
+				BTMTK_ERR("Send vendor cmd failed(%d)! Index: %d", ret, index);
+				return ret;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_setup_picus_param(struct fw_cfg_struct *picus_setting)
+{
+	u8 dft_cmd[] = {0x5F, 0xFC, 0x2E, 0x50, 0x01, 0x0A, 0x00,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00};
+	u8 *cmd = NULL;
+	int len = 0;
+	u8 event[] = { 0x0E, 0x04, 0x01, 0x5F, 0xFC, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	if (picus_setting->content && picus_setting->length) {
+		cmd = picus_setting->content;
+		len = picus_setting->length;
+	} else {
+		cmd = dft_cmd;
+		len = sizeof(dft_cmd);
+	}
+
+	BTSDIO_INFO_RAW(cmd, len, "%s: Filter CMD:", __func__);
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+			cmd, len,
+			event, sizeof(event), COMP_EVENT_TIMO);
+	if (ret) {
+		BTMTK_ERR("Send picus filter cmd failed(%d)!", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_picus_operation(bool enable)
+{
+
+	u8 cmd[] = {0xBE, 0xFC, 0x01, 0x15};
+	u8 event[] = {0x0E, 0x04, 0x01, 0xBE, 0xFC, 0x00};
+	int ret = -1;	/* if successful, 0 */
+
+	if (enable == false)
+		cmd[3] = 0x00;
+
+	BTSDIO_INFO_RAW(cmd, (int)sizeof(cmd), "%s: Enable CMD:", __func__);
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+			cmd, sizeof(cmd),
+			event, sizeof(event), COMP_EVENT_TIMO);
+	if (ret) {
+		BTMTK_ERR("Send picus enable cmd failed(%d)!", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+/*get 1 byte only*/
+static int btmtk_efuse_read(u16 addr, u8 *value)
+{
+	uint8_t efuse_r[] = {0x6F, 0xFC, 0x0E,
+				0x01, 0x0D, 0x0A, 0x00, 0x02, 0x04,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00};/*4 sub block number(sub block 0~3)*/
+
+	uint8_t efuse_r_event[] = {0xE4, 0x1E, 0x02, 0x0D, 0x1A, 0x00, 02, 04};
+	/*check event
+	 *04 E4 LEN(1B) 02 0D LEN(2Byte) 02 04 ADDR(2Byte) VALUE(4B) ADDR(2Byte) VALUE(4Byte)
+	 *ADDR(2Byte) VALUE(4B)  ADDR(2Byte) VALUE(4Byte)
+	 */
+	int ret = 0;
+	uint8_t sub_block_addr_in_event = 0;
+	uint16_t sub_block = (addr / 16) * 4;
+	uint8_t temp = 0;
+
+	efuse_r[9] = sub_block & 0xFF;
+	efuse_r[10] = (sub_block & 0xFF00) >> 8;
+	efuse_r[11] = (sub_block + 1) & 0xFF;
+	efuse_r[12] = ((sub_block + 1) & 0xFF00) >> 8;
+	efuse_r[13] = (sub_block + 2) & 0xFF;
+	efuse_r[14] = ((sub_block + 2) & 0xFF00) >> 8;
+	efuse_r[15] = (sub_block + 3) & 0xFF;
+	efuse_r[16] = ((sub_block + 3) & 0xFF00) >> 8;
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+		efuse_r, sizeof(efuse_r),
+		efuse_r_event, sizeof(efuse_r_event), COMP_EVENT_TIMO);
+	if (ret) {
+		BTMTK_WARN("btmtk_sdio_send_hci_cmd error");
+		BTMTK_WARN("rx_length %d", rx_length);
+		return ret;
+	}
+
+	if (memcmp(rxbuf + MTK_SDIO_PACKET_HEADER_SIZE + 1, efuse_r_event, sizeof(efuse_r_event)) == 0) {
+		/*compare rxbuf format ok, compare addr*/
+		BTMTK_DBG("compare rxbuf format ok");
+		if (efuse_r[9] == rxbuf[9 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[10] == rxbuf[10 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[11] == rxbuf[15 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[12] == rxbuf[16 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[13] == rxbuf[21 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[14] == rxbuf[22 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[15] == rxbuf[27 + MTK_SDIO_PACKET_HEADER_SIZE] &&
+			efuse_r[16] == rxbuf[28 + MTK_SDIO_PACKET_HEADER_SIZE]) {
+
+			BTMTK_DBG("address compare ok");
+			/*Get value*/
+			sub_block_addr_in_event = ((addr / 16) / 4);/*cal block num*/
+			temp = addr % 16;
+			BTMTK_DBG("address in block %d", temp);
+			switch (temp) {
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+				*value = rxbuf[11 + temp + MTK_SDIO_PACKET_HEADER_SIZE];
+				break;
+			case 4:
+			case 5:
+			case 6:
+			case 7:
+				*value = rxbuf[17 + temp + MTK_SDIO_PACKET_HEADER_SIZE];
+				break;
+			case 8:
+			case 9:
+			case 10:
+			case 11:
+				*value = rxbuf[22 + temp + MTK_SDIO_PACKET_HEADER_SIZE];
+				break;
+
+			case 12:
+			case 13:
+			case 14:
+			case 15:
+				*value = rxbuf[34 + temp + MTK_SDIO_PACKET_HEADER_SIZE];
+				break;
+			}
+
+
+		} else {
+			BTMTK_WARN("address compare fail");
+			ret = -1;
+		}
+
+
+	} else {
+		BTMTK_WARN("compare rxbuf format fail");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int btmtk_check_auto_mode(struct btmtk_sdio_card *card)
+{
+	u16 addr = 1;
+	u8 value = 0;
+
+	if (card->efuse_mode != EFUSE_AUTO_MODE)
+		return 0;
+
+	if (btmtk_efuse_read(addr, &value)) {
+		BTMTK_WARN("read fail");
+		BTMTK_WARN("Use EEPROM Bin file mode");
+		card->efuse_mode = EFUSE_BIN_FILE_MODE;
+		return -EIO;
+	}
+
+	if (value == 0x76) {
+		BTMTK_WARN("get efuse[1]: 0x%02x", value);
+		BTMTK_WARN("use efuse mode");
+		card->efuse_mode = EFUSE_MODE;
+	} else {
+		BTMTK_WARN("get efuse[1]: 0x%02x", value);
+		BTMTK_WARN("Use EEPROM Bin file mode");
+		card->efuse_mode = EFUSE_BIN_FILE_MODE;
+	}
+
+	return 0;
+}
+
+static int btmtk_sdio_send_init_cmds(struct btmtk_sdio_card *card)
+{
+	if (btmtk_sdio_bt_set_power(1)) {
+		BTMTK_ERR("power on failed");
+		return -EIO;
+	}
+
+	if (btmtk_check_auto_mode(card)) {
+		BTMTK_ERR("check auto mode failed");
+		return -EIO;
+	}
+
+	if (btmtk_sdio_set_sleep()) {
+		BTMTK_ERR("set sleep failed");
+		return -EIO;
+	}
+
+	if (btmtk_sdio_set_audio()) {
+		BTMTK_ERR("set audio failed");
+		return -EIO;
+	}
+
+	if (btmtk_sdio_send_vendor_cfg()) {
+		BTMTK_ERR("send vendor cmd failed");
+		return -EIO;
+	}
+
+	if (g_card->bt_cfg.support_auto_picus == true) {
+		if (btmtk_sdio_setup_picus_param(&g_card->bt_cfg.picus_filter)) {
+			BTMTK_ERR("send setup_picus_param cmd failed");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int btmtk_sdio_send_deinit_cmds(void)
+{
+	if (btmtk_sdio_bt_set_power(0)) {
+		BTMTK_ERR("power off failed");
+		return -EIO;
+	}
+	return 0;
+}
+
+static void btmtk_parse_efuse_mode(uint8_t *buf, size_t buf_size, struct btmtk_sdio_card *card)
+{
+	char *p_buf = NULL;
+	char *ptr = NULL, *p = NULL;
+
+	card->efuse_mode = EFUSE_MODE;
+	if (!buf) {
+		BTMTK_WARN("buf is null");
+		return;
+	} else if (buf_size < (strlen(E2P_MODE) + 2)) {
+		BTMTK_WARN("incorrect buf size(%d)", (int)buf_size);
+		return;
+	}
+
+	p_buf = kmalloc(buf_size + 1, GFP_KERNEL);
+	if (!p_buf)
+		return;
+	memcpy(p_buf, buf, buf_size);
+	p_buf[buf_size] = '\0';
+
+	/* find string */
+	p = ptr = strstr(p_buf, E2P_MODE);
+	if (!ptr) {
+		BTMTK_ERR("Can't find %s", E2P_MODE);
+		goto out;
+	}
+
+	if (p > p_buf) {
+		p--;
+		while ((*p == ' ') && (p != p_buf))
+			p--;
+		if (*p == '#') {
+			BTMTK_ERR("It's not EEPROM - Bin file mode");
+			goto out;
+		}
+	}
+
+	/* check access mode */
+	ptr += (strlen(E2P_MODE) + 1);
+	BTMTK_WARN("It's EEPROM bin mode: %c", *ptr);
+	card->efuse_mode = *ptr - '0';
+	if (card->efuse_mode > EFUSE_AUTO_MODE)
+		card->efuse_mode = EFUSE_MODE;
+out:
+	kfree(p_buf);
+}
+
+static void btmtk_set_eeprom2ctrler(uint8_t *buf,
+						size_t buf_size,
+						int device)
+{
+	int ret = -1;
+	uint8_t set_bdaddr[] = {0x1A, 0xFC, 0x06,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	uint8_t set_bdaddr_e[] = {0x0E, 0x04, 0x01,
+			0x1A, 0xFC, 0x00};
+	uint8_t set_radio[] = {0x79, 0xFC, 0x08,
+			0x07, 0x80, 0x00, 0x06, 0x07, 0x07, 0x00, 0x00};
+	uint8_t set_radio_e[] = {0x0E, 0x04, 0x01,
+			0x79, 0xFC, 0x00};
+	uint8_t set_pwr_offset[] = {0x93, 0xFC, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	uint8_t set_pwr_offset_e[] = {0x0E, 0x04, 0x01,
+			0x93, 0xFC, 0x00};
+	uint8_t set_xtal[] = {0x0E, 0xFC, 0x02, 0x00, 0x00};
+	uint8_t set_xtal_e[] = {0x0E, 0x04, 0x01,
+			0x0E, 0xFC, 0x00};
+	uint16_t offset = 0;
+	uint16_t efuse_chip_addr = 1;
+	uint8_t  efuse_chip_value = 0;
+
+	BTMTK_INFO("start, device: 0x%x", device);
+
+	if (!buf) {
+		BTMTK_WARN("buf is null");
+		return;
+	} else if (device == 0x7668 && buf_size < 0x389) {
+		BTMTK_WARN("incorrect buf size(%d)", (int)buf_size);
+		return;
+	} else if (device == 0x7663 && buf_size < 0x389) {
+		BTMTK_WARN("incorrect buf size(%d)", (int)buf_size);
+		return;
+	}
+
+	/* set BD address */
+	if (device == 0x7668)
+		offset = 0x384;
+	else if (device == 0x7663)
+		offset = 0x131;
+	else
+		offset = 0x1A;
+
+	set_bdaddr[3] = *(buf + offset + 5);
+	set_bdaddr[4] = *(buf + offset + 4);
+	set_bdaddr[5] = *(buf + offset + 3);
+	set_bdaddr[6] = *(buf + offset + 2);
+	set_bdaddr[7] = *(buf + offset + 1);
+	set_bdaddr[8] = *(buf + offset);
+
+	/*first check mact_bt file, then check efuse, finally check randomize*/
+	if(g_card->mac_bt_flag == true) {
+		BTMTK_WARN("BDAddr is setted by user mac_bt file, not set");
+	}else if(btmtk_efuse_read(efuse_chip_addr, &efuse_chip_value) == 0 &&
+		efuse_chip_value == 0x76) {
+		BTMTK_WARN("use efuse address, not need set address");
+	}else {
+		SetRandomAddress(set_bdaddr + 3);
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, set_bdaddr,
+			set_bdaddr[2] + 3,
+			set_bdaddr_e, sizeof(set_bdaddr_e), COMP_EVENT_TIMO);
+		BTMTK_WARN("set BDAddress(%02X-%02X-%02X-%02X-%02X-%02X) %s",
+				set_bdaddr[8], set_bdaddr[7], set_bdaddr[6],
+				set_bdaddr[5], set_bdaddr[4], set_bdaddr[3],
+				ret < 0 ? "fail" : "OK");
+	}
+	/* radio setting - BT power */
+	if (device == 0x7668) {
+		offset = 0x382;
+		/* BT default power */
+		set_radio[3] = (*(buf + offset) & 0x07);
+		/* BLE default power */
+		set_radio[7] = (*(buf + offset + 1) & 0x07);
+		/* TX MAX power */
+		set_radio[8] = (*(buf + offset) & 0x70) >> 4;
+		/* TX power sub level */
+		set_radio[9] = (*(buf + offset + 1) & 0x30) >> 4;
+		/* BR/EDR power diff mode */
+		set_radio[10] = (*(buf + offset + 1) & 0xc0) >> 6;
+	} else if (device == 0x7663) {
+		offset = 0x137;
+		/* BT default power */
+		set_radio[3] = (*(buf + offset) & 0x07);
+		/* BLE default power */
+		set_radio[7] = (*(buf + offset + 1) & 0x07);
+		/* TX MAX power */
+		set_radio[8] = (*(buf + offset) & 0x70) >> 4;
+		/* TX power sub level */
+		set_radio[9] = (*(buf + offset + 1) & 0x30) >> 4;
+		/* BR/EDR power diff mode */
+		set_radio[10] = (*(buf + offset + 1) & 0xc0) >> 6;
+	} else {
+		offset = 0x132;
+		/* BT default power */
+		set_radio[3] = *(buf + offset);
+		/* BLE default power(no this for 7662 in table) */
+		set_radio[7] = *(buf + offset);
+		/* TX MAX power */
+		set_radio[8] = *(buf + offset + 1);
+	}
+
+	if (!(device == 0x7663 && g_card->power_cfg.powerCfg == 1)) {
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, set_radio,
+			set_radio[2] + 3,
+			set_radio_e, sizeof(set_radio_e), COMP_EVENT_TIMO);
+		BTMTK_WARN("set radio(BT/BLE default power: %d/%d MAX power: %d) %s",
+			set_radio[3], set_radio[7], set_radio[8],
+			ret < 0 ? "fail" : "OK");
+	}
+
+	/*
+	 * BT TX power compensation for low, middle and high
+	 * channel
+	 */
+	if (device == 0x7668) {
+		offset = 0x36D;
+		/* length */
+		set_pwr_offset[2] = 6;
+		/* Group 0 CH 0 ~ CH14 */
+		set_pwr_offset[3] = *(buf + offset);
+		/* Group 1 CH15 ~ CH27 */
+		set_pwr_offset[4] = *(buf + offset + 1);
+		/* Group 2 CH28 ~ CH40 */
+		set_pwr_offset[5] = *(buf + offset + 2);
+		/* Group 3 CH41 ~ CH53 */
+		set_pwr_offset[6] = *(buf + offset + 3);
+		/* Group 4 CH54 ~ CH66 */
+		set_pwr_offset[7] = *(buf + offset + 4);
+		/* Group 5 CH67 ~ CH84 */
+		set_pwr_offset[8] = *(buf + offset + 5);
+	} else if (device == 0x7663) {
+		offset = 0x180;
+		/* length */
+		set_pwr_offset[2] = 16;
+		/* Group 0 CH 0 ~ CH6 */
+		set_pwr_offset[3] = *(buf + offset);
+		/* Group 1 CH7 ~ CH11 */
+		set_pwr_offset[4] = *(buf + offset + 1);
+		/* Group 2 CH12 ~ CH16 */
+		set_pwr_offset[5] = *(buf + offset + 2);
+		/* Group 3 CH17 ~ CH21 */
+		set_pwr_offset[6] = *(buf + offset + 3);
+		/* Group 4 CH22 ~ CH26 */
+		set_pwr_offset[7] = *(buf + offset + 4);
+		/* Group 5 CH27 ~ CH31 */
+		set_pwr_offset[8] = *(buf + offset + 5);
+		/* Group 6 CH32 ~ CH36 */
+		set_pwr_offset[9] = *(buf + offset + 6);
+		/* Group 7 CH37 ~ CH41 */
+		set_pwr_offset[10] = *(buf + offset + 7);
+		/* Group 8 CH42 ~ CH46 */
+		set_pwr_offset[11] = *(buf + offset + 8);
+		/* Group 9 CH47 ~ CH51 */
+		set_pwr_offset[12] = *(buf + offset + 9);
+		/* Group 10 CH52 ~ CH56 */
+		set_pwr_offset[13] = *(buf + offset + 10);
+		/* Group 11 CH57 ~ CH61 */
+		set_pwr_offset[14] = *(buf + offset + 11);
+		/* Group 12 CH62 ~ CH66 */
+		set_pwr_offset[15] = *(buf + offset + 12);
+		/* Group 13 CH67 ~ CH71 */
+		set_pwr_offset[16] = *(buf + offset + 13);
+		/* Group 14 CH72 ~ CH76 */
+		set_pwr_offset[17] = *(buf + offset + 14);
+		/* Group 15 CH77 ~ CH78 */
+		set_pwr_offset[18] = *(buf + offset + 15);
+	} else {
+		offset = 0x139;
+		/* length */
+		set_pwr_offset[2] = 3;
+		/* low channel */
+		set_pwr_offset[3] = *(buf + offset);
+		/* middle channel */
+		set_pwr_offset[4] = *(buf + offset + 1);
+		/* high channel */
+		set_pwr_offset[5] = *(buf + offset + 2);
+	}
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, set_pwr_offset,
+		set_pwr_offset[2] + 3,
+		set_pwr_offset_e, sizeof(set_pwr_offset_e), COMP_EVENT_TIMO);
+	BTMTK_WARN("set power offset(%02X %02X %02X %02X %02X %02X) %s",
+			set_pwr_offset[3], set_pwr_offset[4],
+			set_pwr_offset[5], set_pwr_offset[6],
+			set_pwr_offset[7], set_pwr_offset[8],
+			ret < 0 ? "fail" : "OK");
+
+	/* XTAL setting */
+	if (device == 0x7668) {
+		offset = 0xF4;
+		/* BT default power */
+		set_xtal[3] = *(buf + offset);
+		set_xtal[4] = *(buf + offset + 1);
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, set_xtal,
+			set_xtal[2] + 3,
+			set_xtal_e, sizeof(set_xtal_e), COMP_EVENT_TIMO);
+		BTMTK_WARN("set XTAL(0x%02X %02X) %s",
+				set_xtal[3], set_xtal[4],
+				ret < 0 ? "fail" : "OK");
+	}
+	BTMTK_INFO("end");
+}
+
+static void btmtk_set_pa(uint8_t pa)
+{
+	int ret = -1;
+	uint8_t epa[] = {0x70, 0xFD, 0x09,
+		0x00,
+		0x07, 0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00};
+	uint8_t epa_e[] = {0x0E, 0x04, 0x01,
+		0x70, 0xFD, 0x00};
+
+	epa[3] = pa;
+	if (pa > 1) {
+		BTMTK_WARN("Incorrect format");
+		return;
+	}
+	if (pa == 1) {
+		BTMTK_WARN("ePA mode, change power level to level 9.");
+		epa[4] = 0x09;
+		epa[8] = 0x09;
+		epa[9] = 0x09;
+	}
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, epa, sizeof(epa),
+		epa_e, sizeof(epa_e), COMP_EVENT_TIMO);
+	BTMTK_WARN("set PA(%d) %s",
+		pa, ret < 0 ? "fail" : "OK");
+}
+
+static void btmtk_set_duplex(uint8_t duplex)
+{
+	int ret = -1;
+	uint8_t ant[] = {0x71, 0xFD, 0x01, 0x00};
+	uint8_t ant_e[] = {0x0E, 0x04, 0x01,
+		0x71, 0xFD, 0x00};
+
+	ant[3] = duplex;
+	if (duplex > 1) {
+		BTMTK_WARN("Incorrect format");
+		return;
+	}
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, ant, sizeof(ant),
+		ant_e, sizeof(ant_e), COMP_EVENT_TIMO);
+	BTMTK_WARN("set Duplex(%d) %s",
+		duplex, ret < 0 ? "fail" : "OK");
+}
+
+static void btmtk_set_pa_and_duplex(uint8_t *buf, size_t buf_size)
+{
+	char *p_buf = NULL;
+	char *ptr = NULL, *p = NULL;
+
+	if (!buf) {
+		BTMTK_WARN("buf is null");
+		return;
+	} else if (buf_size < (strlen(E2P_MODE) + 2)) {
+		BTMTK_WARN("incorrect buf size(%d)", (int)buf_size);
+		return;
+	}
+	p_buf = kmalloc(buf_size + 1, GFP_KERNEL);
+	if (!p_buf)
+		return;
+
+	memcpy(p_buf, buf, buf_size);
+	p_buf[buf_size] = '\0';
+	/* find string */
+	p = ptr = strstr(p_buf, E2P_ACCESS_EPA);
+	if (!ptr) {
+		BTMTK_WARN("Can't find %s", E2P_ACCESS_EPA);
+		g_card->pa_setting = -1;
+		g_card->duplex_setting = -1;
+		goto out;
+	}
+	if (p > p_buf) {
+		p--;
+		while ((*p == ' ') && (p != p_buf))
+			p--;
+		if (*p == '#') {
+			BTMTK_WARN("It's no pa setting");
+			g_card->pa_setting = -1;
+			g_card->duplex_setting = -1;
+			goto out;
+		}
+	}
+	/* check access mode */
+	ptr += (strlen(E2P_ACCESS_EPA) + 1);
+	if (*ptr != '0') {
+		BTMTK_WARN("ePA mode: %c", *ptr);
+		g_card->pa_setting = 1;
+	} else {
+		BTMTK_WARN("iPA mode: %c", *ptr);
+		g_card->pa_setting = 0;
+	}
+
+	p = ptr = strstr(p_buf, E2P_ACCESS_DUPLEX);
+	if (!ptr) {
+		BTMTK_WARN("Can't find %s", E2P_ACCESS_DUPLEX);
+		g_card->duplex_setting = -1;
+		goto out;
+	}
+	if (p > p_buf) {
+		p--;
+		while ((*p == ' ') && (p != p_buf))
+			p--;
+		if (*p == '#') {
+			BTMTK_WARN("It's no duplex setting");
+			g_card->duplex_setting = -1;
+			goto out;
+		}
+	}
+	/* check access mode */
+	ptr += (strlen(E2P_ACCESS_DUPLEX) + 1);
+	if (*ptr != '0') {
+		BTMTK_WARN("TDD mode: %c", *ptr);
+		g_card->duplex_setting = 1;
+	} else {
+		BTMTK_WARN("FDD mode: %c", *ptr);
+		g_card->duplex_setting = 0;
+	}
+
+out:
+	kfree(p_buf);
+}
+
+void btmtk_set_keep_full_pwr(uint8_t *buf, size_t buf_size)
+{
+	char *p_buf = NULL;
+	char *ptr = NULL;
+
+	g_card->is_KeepFullPwr = false;
+
+	if (!buf) {
+		BTMTK_ERR("buf is null");
+		return;
+	} else if (buf_size < (strlen(KEEP_FULL_PWR) + 2)) {
+		BTMTK_ERR("incorrect buf size(%d)", (int)buf_size);
+		return;
+	}
+
+	p_buf = kmalloc(buf_size + 1, GFP_KERNEL);
+	if (!p_buf)
+		return;
+
+	memcpy(p_buf, buf, buf_size);
+	p_buf[buf_size] = '\0';
+
+	/* find string */
+	ptr = strstr(p_buf, KEEP_FULL_PWR);
+	if (!ptr) {
+		BTMTK_ERR("Can't find %s", KEEP_FULL_PWR);
+		goto out;
+	}
+
+	/* check always driver own */
+	ptr += (strlen(KEEP_FULL_PWR) + 1);
+
+	if (*ptr == PWR_KEEP_NO_FW_OWN) {
+		/* always driver own is set*/
+		BTMTK_INFO("Read KeepFullPwr on: %c", *ptr);
+		g_card->is_KeepFullPwr = true;
+	} else if (*ptr == PWR_SWITCH_DRIVER_FW_OWN) {
+		/* always driver own is not set */
+		BTMTK_INFO("Read KeepFullPwr off: %c", *ptr);
+	} else {
+		BTMTK_WARN("It's not the correct own setting: %c", *ptr);
+	}
+
+out:
+	kfree(p_buf);
+}
+
+static void btmtk_eeprom_bin_file(struct btmtk_sdio_card *card)
+{
+	char *cfg_file = NULL;
+	char bin_file[32];
+
+	const struct firmware *cfg_fw = NULL;
+	const struct firmware *bin_fw = NULL;
+
+	int ret = -1;
+	int chipid = card->func->device;
+
+	BTMTK_INFO("%X series", chipid);
+	cfg_file = E2P_ACCESS_MODE_SWITCHER;
+	(void)sprintf(bin_file, E2P_BIN_FILE, chipid);
+
+	usleep_range(10*1000, 15*1000);
+
+	/* request configure file */
+	ret = request_firmware(&cfg_fw, cfg_file, &card->func->dev);
+	if (ret < 0) {
+		if (ret == -EAGAIN) {
+			cfg_fw = NULL;
+			BTMTK_WARN("try to load configure file again");
+			ret = request_firmware(&cfg_fw, cfg_file, &card->func->dev);
+			if (ret < 0) {
+				BTMTK_WARN("load configure file again but still fail(%d)", ret);
+				goto exit;
+			}
+			BTMTK_WARN("load configure file again and success(%d)", ret);
+		} else {
+			if (ret == -ENOENT)
+				BTMTK_WARN("Configure file not found, ignore EEPROM bin file");
+			else
+				BTMTK_WARN("request configure file fail(%d)", ret);
+			goto exit;
+		}
+	}
+
+	if (cfg_fw) {
+		btmtk_set_pa_and_duplex((uint8_t *)cfg_fw->data, cfg_fw->size);
+		btmtk_set_keep_full_pwr((uint8_t *)cfg_fw->data, cfg_fw->size);
+	} else {
+		BTMTK_ERR("cfg_fw is null");
+		card->efuse_mode = EFUSE_MODE;
+		goto exit;
+	}
+
+	btmtk_parse_efuse_mode((uint8_t *)cfg_fw->data, cfg_fw->size, card);
+	if (card->efuse_mode == EFUSE_MODE) {
+		if (card->bin_file_buffer != NULL) {
+			kfree(card->bin_file_buffer);
+			card->bin_file_buffer = NULL;
+			card->bin_file_size = 0;
+		}
+		goto exit;
+	}
+
+	usleep_range(10*1000, 15*1000);
+
+	/* open bin file for EEPROM */
+	ret = request_firmware(&bin_fw, bin_file, &card->func->dev);
+	if (ret < 0) {
+		BTMTK_WARN("request bin file fail(%d)", ret);
+		goto exit;
+	}
+
+	card->bin_file_buffer = kmalloc(bin_fw->size, GFP_KERNEL);
+	if (card->bin_file_buffer == NULL)
+		goto exit;
+
+	memcpy(card->bin_file_buffer, bin_fw->data, bin_fw->size);
+	card->bin_file_size = bin_fw->size;
+
+exit:
+	if (cfg_fw)
+		release_firmware(cfg_fw);
+	if (bin_fw)
+		release_firmware(bin_fw);
+}
+
+/* 1:on ,  0:off */
+static int btmtk_sdio_set_sleep(void)
+{
+	int ret = 0;
+	u8 event[] = {0x0E, 0x04, 0x01, 0x7A, 0xFC, 0x00};
+	u8 cmd[] = {0x7A, 0xFC, 0x07,
+		/*3:sdio, 5:usb*/0x03,
+		/*host non sleep duration*/0x80, 0x02,
+		/*host non sleep duration*/0x80, 0x02, 0x00, 0x00};
+	BTMTK_INFO("begin");
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+		event, sizeof(event), COMP_EVENT_TIMO);
+
+	return ret;
+}
+
+static int btmtk_sdio_skb_enq_fwlog(void *src, u32 len, u8 type, struct sk_buff_head *queue)
+{
+	struct sk_buff *skb_tmp = NULL;
+	int retry = 10;
+
+	do {
+		/* If we need hci type, len + 1 */
+		skb_tmp = alloc_skb(type ? len + 1 : len, GFP_ATOMIC);
+		if (skb_tmp != NULL)
+			break;
+		else if (retry <= 0) {
+			BTMTK_ERR("alloc_skb return 0, error");
+			return -ENOMEM;
+		}
+		BTMTK_ERR("alloc_skb return 0, error, retry = %d", retry);
+	} while (retry-- > 0);
+
+	if (type) {
+		memcpy(&skb_tmp->data[0], &type, 1);
+		memcpy(&skb_tmp->data[1], src, len);
+		skb_tmp->len = len + 1;
+	} else {
+		memcpy(skb_tmp->data, src, len);
+		skb_tmp->len = len;
+	}
+
+	LOCK_UNSLEEPABLE_LOCK(&(fwlog_metabuffer.spin_lock));
+	skb_queue_tail(queue, skb_tmp);
+	UNLOCK_UNSLEEPABLE_LOCK(&(fwlog_metabuffer.spin_lock));
+	return 0;
+}
+
+static int btmtk_sdio_dispatch_fwlog(u8 *buf, int len)
+{
+	static u8 fwlog_picus_blocking_warn;
+	static u8 fwlog_fwdump_blocking_warn;
+	int ret = 0;
+
+	if ((buf[0] == 0xFF && buf[2] == 0x50) ||
+		(buf[0] == 0xFF && buf[1] == 0x05)) {
+		if (skb_queue_len(&g_card->fwlog_fops_queue) < FWLOG_QUEUE_COUNT) {
+			BTMTK_DBG("This is picus data");
+			if (btmtk_sdio_skb_enq_fwlog(buf, len, 0, &g_card->fwlog_fops_queue) == 0)
+				wake_up_interruptible(&fw_log_inq);
+
+			fwlog_picus_blocking_warn = 0;
+		} else {
+			if (fwlog_picus_blocking_warn == 0) {
+				fwlog_picus_blocking_warn = 1;
+				BTMTK_WARN("fwlog queue size is full(picus)");
+			}
+		}
+	} else if (buf[0] == 0x6f && buf[1] == 0xfc) {
+		/* Coredump */
+		if (skb_queue_len(&g_card->fwlog_fops_queue) < FWLOG_ASSERT_QUEUE_COUNT) {
+			BTMTK_DBG("Receive coredump, move data to fwlogqueue for picus");
+			if (btmtk_sdio_skb_enq_fwlog(buf, len, 0, &g_card->fwlog_fops_queue) == 0)
+				wake_up_interruptible(&fw_log_inq);
+			fwlog_fwdump_blocking_warn = 0;
+		} else {
+			if (fwlog_fwdump_blocking_warn == 0) {
+				fwlog_fwdump_blocking_warn = 1;
+				BTMTK_WARN("fwlog queue size is full(coredump)");
+			}
+		}
+	}
+	return ret;
+}
+
+static int btmtk_sdio_dispatch_data_bluetooth_kpi(u8 *buf, int len, u8 type)
+{
+	static u8 fwlog_blocking_warn;
+	int ret = 0;
+
+	if (!btmtk_bluetooth_kpi)
+		return ret;
+
+	if (skb_queue_len(&g_card->fwlog_fops_queue) < FWLOG_BLUETOOTH_KPI_QUEUE_COUNT) {
+		/* sent event to queue, picus tool will log it for bluetooth KPI feature */
+		if (btmtk_sdio_skb_enq_fwlog(buf, len, type, &g_card->fwlog_fops_queue) == 0) {
+			wake_up_interruptible(&fw_log_inq);
+			fwlog_blocking_warn = 0;
+		}
+	} else {
+		if (fwlog_blocking_warn == 0) {
+			fwlog_blocking_warn = 1;
+			BTMTK_WARN("fwlog queue size is full(bluetooth_kpi)");
+		}
+	}
+	return ret;
+}
+
+static void btmtk_sdio_assert_cmd_check(u8 *buf, int len)
+{
+	u8 fw_coredump_cmd[] = {0x01, 0x5b, 0xfd, 0x00};
+	u8 fw_coredump_cmd2[] = {0x01, 0x6F, 0xFC, 0x05,
+		0x01, 0x02, 0x01, 0x00, 0x08};
+
+	if ((sizeof(fw_coredump_cmd) == len && !memcmp(buf, fw_coredump_cmd, len)) ||
+		(sizeof(fw_coredump_cmd2) == len && !memcmp(buf, fw_coredump_cmd2, len))) {
+		/* For no coredump data case after assert cmd */
+		BTMTK_INFO("assert cmd, try to start wait dump thread");
+		if (!wait_dump_complete_tsk) {
+			wait_dump_complete_tsk = kthread_run(btmtk_sdio_wait_dump_complete_thread,
+				NULL, "btmtk_sdio_wait_dump_complete_thread");
+
+			msleep(100);
+			if (!wait_dump_complete_tsk)
+				BTMTK_ERR("wait_dump_complete_tsk is NULL");
+
+#if !CFG_SUPPORT_CHIP_RESET_KO
+			btmtk_sdio_notify_wlan_remove_start();
+#endif
+		}
+	}
+}
+
+static int btmtk_sdio_host_to_card(struct btmtk_private *priv,
+				u8 *payload, u16 nb)
+{
+	struct btmtk_sdio_card *card = priv->btmtk_dev.card;
+	int ret = 0;
+	int i = 0;
+	u8 MultiBluckCount = 0;
+	u8 redundant = 0;
+	int len = 0;
+
+	if (payload != txbuf) {
+		memset(txbuf, 0, MTK_TXDATA_SIZE);
+		memcpy(txbuf, payload, nb);
+	}
+
+	if (!card || !card->func) {
+		BTMTK_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+
+	len = nb - MTK_SDIO_PACKET_HEADER_SIZE;
+
+	btmtk_sdio_dispatch_data_bluetooth_kpi(&txbuf[MTK_SDIO_PACKET_HEADER_SIZE], len, 0);
+
+	MultiBluckCount = nb/SDIO_BLOCK_SIZE;
+	redundant = nb % SDIO_BLOCK_SIZE;
+
+	if (redundant)
+		nb = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
+
+	if (nb < 16)
+		btmtk_print_buffer_conent(txbuf, nb);
+	else
+		btmtk_print_buffer_conent(txbuf, 16);
+
+	do {
+		/* Transfer data to card */
+		ret = btmtk_sdio_writesb(CTDR, txbuf, nb);
+		if (ret < 0) {
+			i++;
+			BTMTK_ERR("i=%d writesb failed: %d", i, ret);
+			ret = -EIO;
+			if (i > MAX_WRITE_IOMEM_RETRY)
+				goto exit;
+		}
+	} while (ret);
+
+	/* check assert cmd for start up coredump thread */
+	btmtk_sdio_assert_cmd_check(&txbuf[MTK_SDIO_PACKET_HEADER_SIZE], len);
+
+	priv->btmtk_dev.tx_dnld_rdy = false;
+
+exit:
+
+	return ret;
+}
+
+static int btmtk_sdio_set_audio_slave(void)
+{
+	int ret = 0;
+	u8 *cmd = NULL;
+	u8 event[] = { 0x0E, 0x04, 0x01, 0x72, 0xFC, 0x00 };
+#ifdef MTK_CHIP_PCM /* For PCM setting */
+	u8 cmd_pcm[] = { 0x72, 0xFC, 0x04, 0x03, 0x10, 0x00, 0x4A };
+#if SUPPORT_MT7668
+	u8 cmd_7668[] = { 0x72, 0xFC, 0x04, 0x03, 0x10, 0x00, 0x4A };
+#endif
+#if SUPPORT_MT7663
+	u8 cmd_7663[] = { 0x72, 0xFC, 0x04, 0x49, 0x00, 0x80, 0x00 };
+#endif
+#else /* For I2S setting */
+#if SUPPORT_MT7668
+	u8 cmd_7668[] = { 0x72, 0xFC, 0x04, 0x03, 0x10, 0x00, 0x02 };
+#endif
+#if SUPPORT_MT7663
+	u8 cmd_7663[] = { 0x72, 0xFC, 0x04, 0x49, 0x00, 0x80, 0x00 };
+#endif
+#endif
+	u8 mode = 0;
+
+	BTMTK_INFO();
+#if SUPPORT_MT7668
+	if (is_mt7668(g_card)) {
+		cmd = cmd_7668;
+		mode = 0x08;
+	}
+#endif
+#if SUPPORT_MT7663
+	if (is_mt7663(g_card)) {
+		cmd = cmd_7663;
+		mode = 0x03;
+	}
+#endif
+#ifdef MTK_CHIP_PCM
+	if (!cmd) {
+		BTMTK_ERR("cmd == null, set default PCM setting");
+		cmd = cmd_pcm;
+		mode |= 0x20;
+	} else
+		mode |= 0x10;
+#endif
+
+	BTMTK_INFO("set mode = 0x%x", mode);
+
+	if (cmd) {
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, cmd[2] + 3,
+			event, sizeof(event), COMP_EVENT_TIMO);
+	} else
+		BTMTK_ERR("No any audio cmd applied!!");
+
+	return ret;
+}
+
+static int btmtk_sdio_read_pin_mux_setting(u32 *value)
+{
+	int ret = 0;
+	u8 cmd[] = { 0xD1, 0xFC, 0x04, 0x54, 0x30, 0x02, 0x81 };
+	u8 event[] = { 0x0E, 0x08, 0x01, 0xD1, 0xFC };
+
+	BTMTK_INFO();
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+		event, sizeof(event), COMP_EVENT_TIMO);
+
+	if (ret)
+		return ret;
+
+	*value = (rxbuf[14] << 24) + (rxbuf[13] << 16) + (rxbuf[12] << 8) + rxbuf[11];
+	BTMTK_DBG("value=%08x", *value);
+	return ret;
+}
+
+static int btmtk_sdio_write_pin_mux_setting(u32 value)
+{
+	int ret = 0;
+	u8 *cmd = NULL;
+	u8 event[] = {0x0E, 0x04, 0x01, 0xD0, 0xFC, 0x00};
+	u8 cmd_7668[] = {0xD0, 0xFC, 0x08,
+		0x54, 0x30, 0x02, 0x81, 0x00, 0x00, 0x00, 0x00};
+	u8 cmd_7663[] = {0xD0, 0xFC, 0x08,
+		0x54, 0x50, 0x00, 0x78, 0x00, 0x10, 0x11, 0x01};
+
+#if SUPPORT_MT7668
+	if (is_mt7668(g_card))
+		cmd = cmd_7668;
+#endif
+#if SUPPORT_MT7663
+	if (is_mt7663(g_card))
+		cmd = cmd_7663;
+#endif
+	if (!cmd) {
+		BTMTK_INFO("not supported");
+		return 0;
+	}
+
+	BTMTK_INFO("begin, value = 0x%08x", value);
+
+	cmd[7] = (value & 0x000000FF);
+	cmd[8] = ((value & 0x0000FF00) >> 8);
+	cmd[9] = ((value & 0x00FF0000) >> 16);
+	cmd[10] = ((value & 0xFF000000) >> 24);
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, cmd[2] + 3,
+		event, sizeof(event), COMP_EVENT_TIMO);
+
+	return ret;
+}
+
+static int btmtk_sdio_set_audio_pin_mux(void)
+{
+	int ret = 0;
+	u32 pinmux = 0;
+
+	ret = btmtk_sdio_read_pin_mux_setting(&pinmux);
+	if (ret) {
+		BTMTK_ERR("btmtk_sdio_read_pin_mux_setting error(%d)", ret);
+		return ret;
+	}
+
+#if SUPPORT_MT7668
+	if (is_mt7668(g_card)) {
+		pinmux &= 0x0000FFFF;
+		pinmux |= 0x22220000;
+	}
+#endif
+#if SUPPORT_MT7663
+	if (is_mt7663(g_card)) {
+		pinmux &= 0xF0000FFF;
+		pinmux |= 0x01111000;
+	}
+#endif
+	ret = btmtk_sdio_write_pin_mux_setting(pinmux);
+
+	if (ret) {
+		BTMTK_ERR("btmtk_sdio_write_pin_mux_setting error(%d)", ret);
+		return ret;
+	}
+
+	pinmux = 0;
+	ret = btmtk_sdio_read_pin_mux_setting(&pinmux);
+	if (ret) {
+		BTMTK_ERR("btmtk_sdio_read_pin_mux_setting error(%d)", ret);
+		return ret;
+	}
+	BTMTK_INFO("confirm pinmux %04x", pinmux);
+
+	return ret;
+}
+
+static int btmtk_send_rom_patch(u8 *fwbuf, u32 fwlen, int mode)
+{
+	int ret = 0;
+	u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+	int stp_len = 0;
+	u8 mtkdata_header[MTKDATA_HEADER_SIZE] = {0};
+
+	int copy_len = 0;
+	int Datalen = fwlen;
+	u32 u32ReadCRValue = 0;
+
+
+	BTMTK_DBG("fwlen %d, mode = %d", fwlen, mode);
+	if (fwlen < Datalen) {
+		BTMTK_ERR("file size = %d,is not corect", fwlen);
+		return -ENOENT;
+	}
+
+	stp_len = Datalen + MTKDATA_HEADER_SIZE;
+
+
+	mtkdata_header[0] = 0x2;/*ACL data*/
+	mtkdata_header[1] = 0x6F;
+	mtkdata_header[2] = 0xFC;
+
+	mtkdata_header[3] = ((Datalen+4+1)&0x00FF);
+	mtkdata_header[4] = ((Datalen+4+1)&0xFF00)>>8;
+
+	mtkdata_header[5] = 0x1;
+	mtkdata_header[6] = 0x1;
+
+	mtkdata_header[7] = ((Datalen+1)&0x00FF);
+	mtkdata_header[8] = ((Datalen+1)&0xFF00)>>8;
+
+	mtkdata_header[9] = mode;
+
+/* 0 and 1 is packet length, include MTKSTP_HEADER_SIZE */
+	mtksdio_packet_header[0] =
+		(Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF;
+	mtksdio_packet_header[1] =
+		((Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF00)>>8;
+	mtksdio_packet_header[2] = 0;
+	mtksdio_packet_header[3] = 0;
+
+/*
+ * mtksdio_packet_header[2] and mtksdio_packet_header[3]
+ * are reserved
+ */
+	BTMTK_DBG("result %02x  %02x",
+		((Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF00)>>8,
+		(Datalen+4+MTKSTP_HEADER_SIZE+6));
+
+	memcpy(txbuf+copy_len, mtksdio_packet_header,
+		MTK_SDIO_PACKET_HEADER_SIZE);
+	copy_len += MTK_SDIO_PACKET_HEADER_SIZE;
+
+	memcpy(txbuf+copy_len, mtkdata_header, MTKDATA_HEADER_SIZE);
+	copy_len += MTKDATA_HEADER_SIZE;
+
+	memcpy(txbuf+copy_len, fwbuf, Datalen);
+	copy_len += Datalen;
+
+	BTMTK_DBG("txbuf %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+		txbuf[0], txbuf[1], txbuf[2], txbuf[3], txbuf[4],
+		txbuf[5], txbuf[6], txbuf[7], txbuf[8], txbuf[9]);
+
+
+	ret = btmtk_sdio_readl(CHIER, &u32ReadCRValue);
+	BTMTK_DBG("CHIER u32ReadCRValue %x, ret %d", u32ReadCRValue, ret);
+
+	ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+	BTMTK_DBG("CHLPCR u32ReadCRValue %x, ret %d", u32ReadCRValue, ret);
+
+	ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+	BTMTK_DBG("0CHISR u32ReadCRValue %x, ret %d", u32ReadCRValue, ret);
+	ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+	BTMTK_DBG("00CHISR u32ReadCRValue %x, ret %d", u32ReadCRValue, ret);
+
+	btmtk_sdio_send_tx_data(txbuf, copy_len);
+
+	ret = btmtk_sdio_recv_rx_data();
+
+	return ret;
+}
+
+/*
+ * type: cmd:1, ACL:2
+ * -------------------------------------------------
+ * mtksdio hedaer 4 byte| wmt header  |
+ *
+ *
+ * data len should less than 512-4-4
+ */
+static int btmtk_sdio_send_wohci(u8 type, u32 len, u8 *data)
+{
+	u32 ret = 0;
+	u32 push_in_data_len = 0;
+	u32 MultiBluckCount = 0;
+	u32 redundant = 0;
+	u8 mtk_wmt_header[MTKWMT_HEADER_SIZE] = {0};
+	u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+	u8 mtk_tx_data[512] = {0};
+
+	mtk_wmt_header[0] = type;
+	mtk_wmt_header[1] = 0x6F;
+	mtk_wmt_header[2] = 0xFC;
+	mtk_wmt_header[3] = len;
+
+	mtksdio_packet_header[0] =
+		(len+MTKWMT_HEADER_SIZE+MTK_SDIO_PACKET_HEADER_SIZE)&0xFF;
+	mtksdio_packet_header[1] =
+		((len+MTKWMT_HEADER_SIZE+MTK_SDIO_PACKET_HEADER_SIZE)&0xFF00)
+		>>8;
+	mtksdio_packet_header[2] = 0;
+	mtksdio_packet_header[3] = 0;
+/*
+ * mtksdio_packet_header[2] and mtksdio_packet_header[3]
+ * are reserved
+ */
+
+	memcpy(mtk_tx_data, mtksdio_packet_header,
+		sizeof(mtksdio_packet_header));
+	push_in_data_len += sizeof(mtksdio_packet_header);
+
+	memcpy(mtk_tx_data+push_in_data_len, mtk_wmt_header,
+		sizeof(mtk_wmt_header));
+	push_in_data_len += sizeof(mtk_wmt_header);
+
+	memcpy(mtk_tx_data+push_in_data_len, data, len);
+	push_in_data_len += len;
+	memcpy(txbuf, mtk_tx_data, push_in_data_len);
+
+	MultiBluckCount = push_in_data_len/4;
+	redundant = push_in_data_len % 4;
+	if (redundant)
+		push_in_data_len = (MultiBluckCount+1)*4;
+
+	ret = btmtk_sdio_writesb(CTDR, txbuf, push_in_data_len);
+	BTMTK_INFO("return  0x%0x", ret);
+	return ret;
+}
+
+/*
+ * data event:
+ * return
+ * 0:
+ * patch download is not complete/get patch semaphore fail
+ * 1:
+ * patch download is complete by others
+ * 2
+ * patch download is not coplete
+ * 3:(for debug)
+ * release patch semaphore success
+ */
+static int btmtk_sdio_need_load_rom_patch(void)
+{
+	u32 ret = -1;
+	u8 cmd[] = {0x1, 0x17, 0x1, 0x0, 0x1};
+	u8 event[] = {0x2, 0x17, 0x1, 0x0};
+
+	do {
+		ret = btmtk_sdio_send_wohci(HCI_COMMAND_PKT, sizeof(cmd), cmd);
+
+		if (ret) {
+			BTMTK_ERR("btmtk_sdio_send_wohci return fail ret %d", ret);
+			break;
+		}
+
+		ret = btmtk_sdio_recv_rx_data();
+		if (ret)
+			break;
+
+		if (rx_length == 12) {
+			if (memcmp(rxbuf+7, event, sizeof(event)) == 0)
+				return rxbuf[11];
+
+			BTMTK_ERR("receive event content is not correct, print receive data");
+			btmtk_print_buffer_conent(rxbuf, rx_length);
+		}
+	} while (0);
+	BTMTK_ERR("return ret %d", ret);
+	return ret;
+}
+
+static int btmtk_sdio_set_write_clear(void)
+{
+	u32 u32ReadCRValue = 0;
+	u32 ret = 0;
+
+	ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue);
+	if (ret) {
+		BTMTK_ERR("read CHCR error");
+		ret = EINVAL;
+		return ret;
+	}
+
+	u32ReadCRValue |= 0x00000002;
+	btmtk_sdio_writel(CHCR, u32ReadCRValue);
+	BTMTK_INFO("write CHCR 0x%08X", u32ReadCRValue);
+	ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue);
+	BTMTK_INFO("read CHCR 0x%08X", u32ReadCRValue);
+	if (u32ReadCRValue&0x00000002)
+		BTMTK_INFO("write clear");
+	else
+		BTMTK_INFO("read clear");
+
+	return ret;
+}
+
+static int btmtk_sdio_set_audio(void)
+{
+	int ret = 0;
+
+	ret = btmtk_sdio_set_audio_slave();
+	if (ret) {
+		BTMTK_ERR("btmtk_sdio_set_audio_slave error(%d)", ret);
+		return ret;
+	}
+
+	ret = btmtk_sdio_set_audio_pin_mux();
+	if (ret) {
+		BTMTK_ERR("btmtk_sdio_set_audio_pin_mux error(%d)", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_send_audio_slave(void)
+{
+	int ret = 0;
+
+	ret = btmtk_sdio_set_audio_slave();
+	if (ret) {
+		BTMTK_ERR("btmtk_sdio_set_audio_slave error(%d)", ret);
+		return ret;
+	}
+
+	if (is_mt7663(g_card)) {
+		ret = btmtk_sdio_set_audio_pin_mux();
+		if (ret) {
+			BTMTK_ERR("btmtk_sdio_set_audio_pin_mux error(%d)", ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_download_rom_patch(struct btmtk_sdio_card *card)
+{
+	const struct firmware *fw_firmware = NULL;
+	int firmwarelen, ret = 0;
+	u8 *fwbuf;
+	struct _PATCH_HEADER *patchHdr;
+	u16 u2HwVer = 0;
+	u16 u2SwVer = 0;
+	u32 u4PatchVer = 0;
+	u32 u4FwVersion = 0;
+	u32 u4ChipId = 0;
+	u32 u32ReadCRValue = 0;
+	int  patch_status = 0;
+	char strDateTime[17];
+	bool load_sysram3 = false;
+	int retry = 20;
+
+	ret = btmtk_sdio_set_own_back(DRIVER_OWN);
+	if (ret)
+		goto done;
+
+	u4FwVersion = btmtk_sdio_bt_memRegister_read(FW_VERSION);
+	BTMTK_INFO("Fw Version 0x%x", u4FwVersion);
+	u4ChipId = btmtk_sdio_bt_memRegister_read(CHIP_ID);
+	BTMTK_INFO("Chip Id 0x%x", u4ChipId);
+
+	if ((u4FwVersion & 0xff) == 0xff) {
+		BTMTK_ERR("failed ! wrong fw version : 0x%x", u4FwVersion);
+		ret = -ENODEV;
+		goto done;
+
+	} else {
+		u8 uFirmwareName[MAX_BIN_FILE_NAME_LEN] = {0};
+
+		/* Bin filename format : "mt$$$$_patch_e%.bin" */
+		/*     $$$$ : chip id */
+		/*     % : fw version + 1 (in HEX) */
+		snprintf(uFirmwareName, MAX_BIN_FILE_NAME_LEN, "mt%04x_patch_e%x_hdr.bin",
+				u4ChipId & 0xffff, (u4FwVersion & 0x0ff) + 1);
+		BTMTK_INFO("request_firmware(firmware name %s)", uFirmwareName);
+		ret = request_firmware(&fw_firmware, uFirmwareName,
+				&card->func->dev);
+
+		if ((ret < 0) || !fw_firmware) {
+			BTMTK_ERR("request_firmware(firmware name %s) failed, error code = %d",
+					uFirmwareName,
+					ret);
+			ret = -ENOENT;
+			goto done;
+		}
+	}
+	memset(fw_version_str, 0, FW_VERSION_BUF_SIZE);
+	if ((fw_firmware->data[8] >= '0') && (fw_firmware->data[8] <= '9'))
+		memcpy(fw_version_str, fw_firmware->data, FW_VERSION_SIZE - 1);
+	else
+		sprintf(fw_version_str, "%.4s-%.2s-%.2s.%.1s.%.2s.%.1s.%.1s.%.2s",
+			fw_firmware->data, fw_firmware->data + 4, fw_firmware->data + 6,
+			fw_firmware->data + 8, fw_firmware->data + 9,
+			fw_firmware->data + 11, fw_firmware->data + 12,
+			fw_firmware->data + 13);
+
+#if SUPPORT_MT7668
+	if (is_mt7668(g_card))
+		load_sysram3 =
+			(fw_firmware->size > (PATCH_INFO_SIZE + PATCH_LEN_ILM))
+				? true : false;
+#endif
+
+	do {
+		patch_status = btmtk_sdio_need_load_rom_patch();
+		BTMTK_DBG("patch_status %d", patch_status);
+
+		if (patch_status > PATCH_NEED_DOWNLOAD || patch_status < 0) {
+			BTMTK_ERR("patch_status error");
+			ret = -ENODEV;
+			goto done;
+		} else if (patch_status == PATCH_READY) {
+			BTMTK_INFO("patch is ready no need load patch again");
+			if (!load_sysram3)
+				goto patch_end;
+			else
+				goto sysram3;
+		} else if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+			msleep(100);
+			retry--;
+		} else if (patch_status == PATCH_NEED_DOWNLOAD) {
+			if (is_mt7663(g_card)) {
+				if (btmtk_sdio_send_wmt_cfg())
+					BTMTK_ERR("send wmt cfg failed!");
+			}
+			break;  /* Download ROM patch directly */
+		}
+	} while (retry > 0);
+
+	if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+		BTMTK_WARN("Hold by another fun more than 2 seconds");
+		ret = -ENODEV;
+		goto done;
+	}
+
+	fwbuf = (u8 *)fw_firmware->data;
+
+	/*Display rom patch info*/
+	patchHdr =  (struct _PATCH_HEADER *)fwbuf;
+	memcpy(strDateTime, patchHdr->ucDateTime, sizeof(patchHdr->ucDateTime));
+	strDateTime[16] = '\0';
+	u2HwVer = patchHdr->u2HwVer;
+	u2SwVer = patchHdr->u2SwVer;
+	u4PatchVer = patchHdr->u4PatchVer;
+
+	BTMTK_INFO("[btmtk] =============== Patch Info ==============");
+	BTMTK_INFO("[btmtk] Built Time = %s", strDateTime);
+	BTMTK_INFO("[btmtk] Hw Ver = 0x%x",
+			((u2HwVer & 0x00ff) << 8) | ((u2HwVer & 0xff00) >> 8));
+	BTMTK_INFO("[btmtk] Sw Ver = 0x%x",
+			((u2SwVer & 0x00ff) << 8) | ((u2SwVer & 0xff00) >> 8));
+	BTMTK_INFO("[btmtk] Patch Ver = 0x%04x",
+			((u4PatchVer & 0xff000000) >> 24) |
+			((u4PatchVer & 0x00ff0000) >> 16));
+
+	BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
+			patchHdr->ucPlatform[0],
+			patchHdr->ucPlatform[1],
+			patchHdr->ucPlatform[2],
+			patchHdr->ucPlatform[3]);
+	BTMTK_INFO("[btmtk] Patch start addr = %02x", patchHdr->u2PatchStartAddr);
+	BTMTK_INFO("[btmtk] =========================================");
+
+	firmwarelen = load_sysram3 ?
+			PATCH_LEN_ILM :	(fw_firmware->size - PATCH_INFO_SIZE);
+
+	BTMTK_INFO("loading ILM rom patch...");
+	ret = btmtk_sdio_download_partial_rom_patch(fwbuf, firmwarelen);
+	BTMTK_INFO("loading ILM rom patch... Done");
+
+	if (btmtk_sdio_need_load_rom_patch() == PATCH_READY) {
+		BTMTK_INFO("patchdownload is done by BT");
+	} else {
+		/* TODO: Need error handling here*/
+		BTMTK_WARN("patchdownload download by BT, not ready");
+	}
+
+	/* CHIP_RESET, ROM patch would be reactivated.
+	 * Currently, wmt reset is only for ILM rom patch, and there are also
+	 * some preparations need to be done in FW for loading sysram3 patch...
+	 */
+	ret = btmtk_sdio_send_wmt_reset();
+	if (ret)
+		goto done;
+
+sysram3:
+	if (load_sysram3) {
+		firmwarelen = fw_firmware->size - PATCH_INFO_SIZE
+			- PATCH_LEN_ILM - PATCH_INFO_SIZE;
+		fwbuf = (u8 *)fw_firmware->data + PATCH_INFO_SIZE
+			+ PATCH_LEN_ILM;
+		BTMTK_INFO("loading sysram3 rom patch...");
+		ret = btmtk_sdio_download_partial_rom_patch(fwbuf, firmwarelen);
+		BTMTK_INFO("loading sysram3 rom patch... Done");
+	}
+
+patch_end:
+	ret = btmtk_sdio_readl(0, &u32ReadCRValue);
+	BTMTK_INFO("read chipid =  %x", u32ReadCRValue);
+
+	/*Set interrupt output*/
+	ret = btmtk_sdio_writel(CHIER, FIRMWARE_INT|TX_FIFO_OVERFLOW |
+			FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
+			TX_UNDER_THOLD | TX_EMPTY | RX_DONE);
+	if (ret) {
+		BTMTK_ERR("Set interrupt output fail(%d)", ret);
+		ret = -EIO;
+		goto done;
+	}
+
+	/*enable interrupt output*/
+	ret = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET);
+	if (ret) {
+		BTMTK_ERR("enable interrupt output fail(%d)", ret);
+		ret = -EIO;
+		goto done;
+	}
+
+	btmtk_sdio_set_write_clear();
+
+done:
+	btmtk_sdio_set_own_back(FW_OWN);
+
+	if (fw_firmware)
+		release_firmware(fw_firmware);
+
+	if (!ret)
+		BTMTK_INFO("success");
+	else
+		BTMTK_INFO("fail");
+
+	return ret;
+}
+
+static int btmtk_sdio_download_partial_rom_patch(u8 *fwbuf, int firmwarelen)
+{
+	int ret = 0;
+	int RedundantSize = 0;
+	u32 bufferOffset = 0;
+
+	BTMTK_INFO("Downloading FW image (%d bytes)", firmwarelen);
+
+	fwbuf += PATCH_INFO_SIZE;
+	BTMTK_DBG("PATCH_HEADER size %d", PATCH_INFO_SIZE);
+
+	RedundantSize = firmwarelen;
+	BTMTK_DBG("firmwarelen %d", firmwarelen);
+
+	do {
+		bufferOffset = firmwarelen - RedundantSize;
+
+		if (RedundantSize == firmwarelen &&
+				RedundantSize >= PATCH_DOWNLOAD_SIZE)
+			ret = btmtk_send_rom_patch(fwbuf + bufferOffset,
+					PATCH_DOWNLOAD_SIZE,
+					SDIO_PATCH_DOWNLOAD_FIRST);
+		else if (RedundantSize == firmwarelen)
+			ret = btmtk_send_rom_patch(fwbuf + bufferOffset,
+					RedundantSize,
+					SDIO_PATCH_DOWNLOAD_FIRST);
+		else if (RedundantSize < PATCH_DOWNLOAD_SIZE) {
+			ret = btmtk_send_rom_patch(fwbuf + bufferOffset,
+					RedundantSize,
+					SDIO_PATCH_DOWNLOAD_END);
+			BTMTK_DBG("patch downoad last patch part");
+		} else
+			ret = btmtk_send_rom_patch(fwbuf + bufferOffset,
+					PATCH_DOWNLOAD_SIZE,
+					SDIO_PATCH_DOWNLOAD_CON);
+
+		RedundantSize -= PATCH_DOWNLOAD_SIZE;
+
+		if (ret) {
+			BTMTK_ERR("btmtk_send_rom_patch fail");
+			return ret;
+		}
+		BTMTK_DBG("RedundantSize %d", RedundantSize);
+		if (RedundantSize <= 0) {
+			BTMTK_DBG("patch downoad finish");
+			break;
+		}
+	} while (1);
+
+	return ret;
+}
+
+static void btmtk_sdio_close_coredump_file(void)
+{
+	BTMTK_DBG("vfs_fsync");
+
+	if (g_card->bt_cfg.save_fw_dump_in_kernel && fw_dump_file)
+		vfs_fsync(fw_dump_file, 0);
+
+	if (fw_dump_file) {
+		BTMTK_INFO("close file  %s", g_card->bt_cfg.fw_dump_file_name);
+		if (g_card->bt_cfg.save_fw_dump_in_kernel)
+			filp_close(fw_dump_file, NULL);
+		fw_dump_file = NULL;
+	} else {
+		BTMTK_WARN("fw_dump_file is NULL can't close file %s", g_card->bt_cfg.fw_dump_file_name);
+	}
+}
+
+static void btmtk_sdio_stop_wait_dump_complete_thread(void)
+{
+	if (IS_ERR(wait_dump_complete_tsk) || wait_dump_complete_tsk == NULL)
+		BTMTK_ERR("wait_dump_complete_tsk is error");
+	else {
+		kthread_stop(wait_dump_complete_tsk);
+		wait_dump_complete_tsk = NULL;
+	}
+}
+
+static int btmtk_sdio_card_to_host(struct btmtk_private *priv, const u8 *event, const int event_len,
+	int add_spec_header)
+/*event: check event which want to compare*/
+/*return value: -x fail, 0 success*/
+{
+	u16 buf_len = 0;
+	int ret = 0;
+	struct sk_buff *skb = NULL;
+	struct sk_buff *fops_skb = NULL;
+	struct sk_buff *skb_opcode = NULL;
+	struct sk_buff *tmp_opcode = NULL;
+	u32 type;
+	u32 fourbalignment_len = 0, tail_len = 0;
+	u32 dump_len = 0;
+	char *core_dump_end = NULL;
+	int i = 0;
+	u16 retry = 0;
+	u32 u32ReadCRValue = 0;
+	u8 is_fwdump = 0;
+	int fops_state = 0;
+	static u8 picus_blocking_warn;
+	static u8 fwdump_blocking_warn;
+
+	if (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE + 1)) {
+		buf_len = rx_length - (MTK_SDIO_PACKET_HEADER_SIZE + 1);
+	} else {
+		BTMTK_ERR("rx_length error(%d)", rx_length);
+		return -EINVAL;
+	}
+
+	/* Core dump packet format:
+	 * A0 00 00 00 80 AA BB CC 02 6F FC YY ZZ XX ... XX 00 00 00 00
+	 * A0 00 00 00: SDIO Header
+	 * 80 AA BB CC: STP Header
+	 * 02 6F FC: Core dump header
+	 * YY ZZ: Coredump length
+	 * 00 00 00 00: STP CRC and aligment to multiple of 4 by SDIO
+	 */
+	if (rx_length > (COREDUMP_PACKET_HEADER_LEN) &&
+		rxbuf[SDIO_HEADER_LEN] == 0x80 &&
+		rxbuf[SDIO_HEADER_LEN + STP_HEADER_LEN + 1] == 0x6F &&
+		rxbuf[SDIO_HEADER_LEN + STP_HEADER_LEN + 2] == 0xFC) {
+
+		dump_len = rxbuf[SDIO_HEADER_LEN + STP_HEADER_LEN + 3]
+			+ (rxbuf[SDIO_HEADER_LEN + STP_HEADER_LEN + 4] << 8);
+		BTMTK_DBG("get dump len %d", dump_len);
+
+		dump_data_counter++;
+		/* Total dump length to fw_dump_files */
+		dump_data_length += dump_len;
+		is_fwdump = 1;
+
+		if (dump_data_counter % 1000 == 0)
+			BTMTK_WARN("coredump on-going, total_packet = %d, total_length = %d",
+					dump_data_counter, dump_data_length);
+
+		if (dump_data_counter < PRINT_DUMP_COUNT) {
+			BTMTK_WARN("dump %d %s",
+				dump_data_counter,
+				&rxbuf[COREDUMP_PACKET_HEADER_LEN]);
+		/* release mode do reset dongle if print dump finish */
+		} else if (!g_card->bt_cfg.support_full_fw_dump &&
+			dump_data_counter == PRINT_DUMP_COUNT) {
+			/* create dump file fail and is user mode */
+			BTMTK_INFO("user mode, do reset after print dump done %d", dump_data_counter);
+			picus_blocking_warn = 0;
+			fwdump_blocking_warn = 0;
+			btmtk_sdio_close_coredump_file();
+			btmtk_sdio_stop_wait_dump_complete_thread();
+			goto exit;
+		}
+
+		if (dump_data_counter == 1) {
+			g_card->dongle_state = BT_SDIO_DONGLE_STATE_FW_DUMP;
+			btmtk_sdio_hci_snoop_print();
+			BTMTK_INFO("create btmtk_sdio_wait_dump_complete_thread");
+			if (!wait_dump_complete_tsk) {
+				wait_dump_complete_tsk = kthread_run(btmtk_sdio_wait_dump_complete_thread,
+					NULL, "btmtk_sdio_wait_dump_complete_thread");
+
+				msleep(100);
+				if (!wait_dump_complete_tsk)
+					BTMTK_ERR("wait_dump_complete_tsk is NULL");
+
+#if !CFG_SUPPORT_CHIP_RESET_KO
+				btmtk_sdio_notify_wlan_remove_start();
+#endif
+			}
+			btmtk_sdio_set_no_fw_own(g_priv, TRUE);
+
+			// if (g_card->bt_cfg.save_fw_dump_in_kernel) {
+			// 	BTMTK_WARN("open file %s",
+			// 		g_card->bt_cfg.fw_dump_file_name);
+			// 	fw_dump_file = filp_open(g_card->bt_cfg.fw_dump_file_name, O_RDWR | O_CREAT, 0644);
+
+			// 	if (!(IS_ERR(fw_dump_file))) {
+			// 		BTMTK_WARN("open file %s success",
+			// 			g_card->bt_cfg.fw_dump_file_name);
+			// 	} else {
+			// 		BTMTK_WARN("open file %s fail",
+			// 			g_card->bt_cfg.fw_dump_file_name);
+			// 		fw_dump_file = NULL;
+			// 	}
+
+			// 	if (fw_dump_file && fw_dump_file->f_op == NULL) {
+			// 		BTMTK_WARN("%s fw_dump_file->f_op is NULL, close",
+			// 			g_card->bt_cfg.fw_dump_file_name);
+			// 		filp_close(fw_dump_file, NULL);
+			// 		fw_dump_file = NULL;
+			// 	}
+
+			// 	if (fw_dump_file && fw_dump_file->f_op->write == NULL) {
+			// 		BTMTK_WARN("%s fw_dump_file->f_op->write is NULL, close",
+			// 			g_card->bt_cfg.fw_dump_file_name);
+			// 		filp_close(fw_dump_file, NULL);
+			// 		fw_dump_file = NULL;
+			// 	}
+			// }
+		}
+
+		if (g_card->bt_cfg.save_fw_dump_in_kernel && (dump_len > 0)
+			&& fw_dump_file && fw_dump_file->f_op && fw_dump_file->f_op->write)
+			fw_dump_file->f_op->write(fw_dump_file, &rxbuf[COREDUMP_PACKET_HEADER_LEN],
+				dump_len, &fw_dump_file->f_pos);
+
+		if (skb_queue_len(&g_card->fwlog_fops_queue) < FWLOG_ASSERT_QUEUE_COUNT) {
+			/* This is coredump data, save coredump data to picus_queue */
+			BTMTK_DBG("Receive coredump data, move data to fwlog queue for picus");
+			/* Save coredump data to picus_queue from 6F FC, minus ACL header */
+			btmtk_sdio_dispatch_fwlog(&rxbuf[SDIO_HEADER_LEN + STP_HEADER_LEN + 1],
+				dump_len + COREDUMP_HEADER_LEN - HCI_TYPE_LEN);
+			fwdump_blocking_warn = 0;
+		} else if (fwdump_blocking_warn == 0) {
+			fwdump_blocking_warn = 1;
+			BTMTK_WARN("btmtk_sdio FW dump queue size is full");
+		}
+
+		/* Modify header to ACL format, handle is 0xFFF0
+		 * Core dump header:
+		 * 80 AA BB CC DD 6F FC XX XX XX ......
+		 * 80 AA BB CC	-> STP header, droped, 4 bytes extra at tailed for CRC
+		 * DD		-> 02 (ACL TYPE)
+		 * 6F FC	-> FF F0
+		 */
+		rxbuf[SDIO_HEADER_LEN + 4] = HCI_ACLDATA_PKT;
+		rxbuf[SDIO_HEADER_LEN + 5] = 0xFF;
+		rxbuf[SDIO_HEADER_LEN + 6] = 0xF0;
+
+		if (dump_len >= strlen(FW_DUMP_END_EVENT)) {
+			core_dump_end = strstr(&rxbuf[SDIO_HEADER_LEN + 10],
+					FW_DUMP_END_EVENT);
+
+			if (core_dump_end) {
+				BTMTK_WARN("core_dump_end %s, total_packet = %d, total_length = %d",
+				    core_dump_end, dump_data_counter, dump_data_length);
+				BTMTK_WARN("rxbuf = %02x %02x %02x",
+					rxbuf[4], rxbuf[5], rxbuf[6]);
+				sdio_claim_host(g_card->func);
+				sdio_release_irq(g_card->func);
+				sdio_release_host(g_card->func);
+				dump_data_counter = 0;
+				dump_data_length = 0;
+				picus_blocking_warn = 0;
+				fwdump_blocking_warn = 0;
+				btmtk_sdio_close_coredump_file();
+				btmtk_sdio_stop_wait_dump_complete_thread();
+			}
+		}
+	} else if (rx_length > (SDIO_HEADER_LEN + 4) &&
+			((rxbuf[SDIO_HEADER_LEN] == 0x04 &&
+			  rxbuf[SDIO_HEADER_LEN + 1] == 0xFF &&
+			  rxbuf[SDIO_HEADER_LEN + 3] == 0x50) ||
+			(rxbuf[SDIO_HEADER_LEN] == 0x02 &&
+			 rxbuf[SDIO_HEADER_LEN + 1] == 0xFF &&
+			 rxbuf[SDIO_HEADER_LEN + 2] == 0x05))) {
+		 /*receive picus data to fwlog_queue*/
+		if (rxbuf[SDIO_HEADER_LEN] == 0x04) {
+			dump_len = rxbuf[SDIO_HEADER_LEN + 2] - 1;
+			buf_len = dump_len + 3;
+		} else {
+			dump_len = ((rxbuf[SDIO_HEADER_LEN + 4] & 0x0F) << 8) + rxbuf[SDIO_HEADER_LEN + 3];
+			buf_len = dump_len + 4;
+		}
+		BTMTK_DBG("This is debug log data, length = %d", dump_len);
+		if (rx_length < (buf_len + MTK_SDIO_PACKET_HEADER_SIZE + 1))
+			goto data_err;
+		btmtk_sdio_dispatch_fwlog(&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE + 1], buf_len);
+		btmtk_sdio_hci_snoop_save(FW_LOG_PKT, &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE + 1], buf_len);
+		goto exit;
+	} else if (rxbuf[SDIO_HEADER_LEN] == 0x04
+			&& rxbuf[SDIO_HEADER_LEN + 1] == 0x0E
+			&& rxbuf[SDIO_HEADER_LEN + 2] == 0x04
+			&& rxbuf[SDIO_HEADER_LEN + 3] == 0x01
+			&& rxbuf[SDIO_HEADER_LEN + 4] == 0x02
+			&& rxbuf[SDIO_HEADER_LEN + 5] == 0xFD) {
+		BTMTK_ERR("This is btclk event, status:%02x", rxbuf[SDIO_HEADER_LEN + 6]);
+		buf_len = rx_length - (MTK_SDIO_PACKET_HEADER_SIZE + 1);
+		goto exit;
+	} else if (rx_length >= (SDIO_HEADER_LEN + 13)
+			&& rxbuf[SDIO_HEADER_LEN] == 0x04
+			&& rxbuf[SDIO_HEADER_LEN + 1] == 0xFF
+			&& rxbuf[SDIO_HEADER_LEN + 3] == 0x41) {
+		/* receive BT clock data */
+		BTMTK_DBG("This is btclk data - %d", rx_length);
+		BTMTK_DBG("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+			rxbuf[SDIO_HEADER_LEN + 0], rxbuf[SDIO_HEADER_LEN + 1], rxbuf[SDIO_HEADER_LEN + 2],
+			rxbuf[SDIO_HEADER_LEN + 3], rxbuf[SDIO_HEADER_LEN + 4], rxbuf[SDIO_HEADER_LEN + 5],
+			rxbuf[SDIO_HEADER_LEN + 6], rxbuf[SDIO_HEADER_LEN + 7], rxbuf[SDIO_HEADER_LEN + 8],
+			rxbuf[SDIO_HEADER_LEN + 9], rxbuf[SDIO_HEADER_LEN + 10], rxbuf[SDIO_HEADER_LEN + 11],
+			rxbuf[SDIO_HEADER_LEN + 12], rxbuf[SDIO_HEADER_LEN + 13], rxbuf[SDIO_HEADER_LEN + 14],
+			rxbuf[SDIO_HEADER_LEN + 15], rxbuf[SDIO_HEADER_LEN + 16], rxbuf[SDIO_HEADER_LEN + 17]);
+
+		if (rxbuf[SDIO_HEADER_LEN + 12] == 0x0) {
+			u32 intra_clk = 0, clk = 0;
+
+			memcpy(&intra_clk, &rxbuf[SDIO_HEADER_LEN + 6], 2);
+			memcpy(&clk, &rxbuf[SDIO_HEADER_LEN + 8], 4);
+
+			LOCK_UNSLEEPABLE_LOCK(&stereo_spin_lock);
+			stereo_clk.fw_clk = (u64)(intra_clk + (clk & 0x0FFFFFFC) * 3125 / 10);
+			stereo_clk.sys_clk = sys_clk_tmp;
+			UNLOCK_UNSLEEPABLE_LOCK(&stereo_spin_lock);
+			BTMTK_DBG("btclk intra:%x, clk:%x, fw_clk:%llu, sysclk: %llu",
+				intra_clk, clk, stereo_clk.fw_clk, stereo_clk.sys_clk);
+		} else {
+			BTMTK_WARN("No ACL CONNECTION(%d), disable event and interrupt",
+				rxbuf[SDIO_HEADER_LEN + 12]);
+		}
+
+		buf_len = rx_length - (MTK_SDIO_PACKET_HEADER_SIZE + 1);
+		goto exit;
+	} else if (rxbuf[SDIO_HEADER_LEN] == HCI_EVENT_PKT &&
+			rxbuf[SDIO_HEADER_LEN + 1] == 0x0E &&
+			rxbuf[SDIO_HEADER_LEN + 4] == 0x03 &&
+			rxbuf[SDIO_HEADER_LEN + 5] == 0x0C &&
+			rxbuf[SDIO_HEADER_LEN + 6] == 0x00) {
+		BTMTK_INFO("get hci reset");
+		get_hci_reset = 1;
+	}
+
+	btmtk_print_buffer_conent(rxbuf, rx_length);
+
+	/* Read the length of data to be transferred , not include pkt type*/
+	buf_len = rx_length - (MTK_SDIO_PACKET_HEADER_SIZE + 1);
+
+	BTMTK_DBG("buf_len : %d", buf_len);
+	if (rx_length <= SDIO_HEADER_LEN) {
+		BTMTK_WARN("invalid packet length: %d", buf_len);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Allocate buffer */
+	/* rx_length = num_blocks * blksz + BTSDIO_DMA_ALIGN*/
+	skb = bt_skb_alloc(rx_length, GFP_ATOMIC);
+	if (skb == NULL) {
+		BTMTK_WARN("No free skb");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	BTMTK_DBG("rx_length %d,buf_len %d", rx_length, buf_len);
+
+	if (is_fwdump == 0) {
+		memcpy(skb->data, &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE + 1], buf_len);
+		type = rxbuf[MTK_SDIO_PACKET_HEADER_SIZE];
+	} else {
+		memcpy(skb->data, &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE + 5], buf_len - 4);
+		type = rxbuf[MTK_SDIO_PACKET_HEADER_SIZE + 4];
+		goto exit;
+	}
+
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		BTMTK_DBG("data[2] 0x%02x, data[3] 0x%02x"
+			, skb->data[2], skb->data[3]);
+		buf_len = skb->data[2] + skb->data[3] * 256 + 4;
+		BTMTK_DBG("acl buf_len %d", buf_len);
+		break;
+	case HCI_SCODATA_PKT:
+		buf_len = skb->data[2] + 3;
+		break;
+	case HCI_EVENT_PKT:
+		buf_len = skb->data[1] + 2;
+		break;
+	default:
+		BTSDIO_INFO_RAW(skb->data, buf_len, "CHISR(0x%08X) skb->data(type %d):", reg_CHISR, type);
+
+		for (retry = 0; retry < 5; retry++) {
+			ret = btmtk_sdio_readl(SWPCDBGR, &u32ReadCRValue);
+			BTMTK_INFO("ret %d, SWPCDBGR 0x%x, and not sleep!", ret, u32ReadCRValue);
+		}
+		btmtk_sdio_print_debug_sr();
+
+		/* trigger fw core dump */
+		FOPS_MUTEX_LOCK();
+		fops_state = btmtk_fops_get_state();
+		FOPS_MUTEX_UNLOCK();
+		if (fops_state == BTMTK_FOPS_STATE_OPENED)
+			btmtk_sdio_trigger_fw_assert();
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (buf_len > MTK_RXDATA_SIZE) {
+		BTMTK_ERR("buf_len %d is invalid, more than %d", buf_len, MTK_RXDATA_SIZE);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if ((buf_len >= sizeof(READ_ADDRESS_EVENT))
+		&& (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE)) {
+		if ((memcmp(skb->data, READ_ADDRESS_EVENT, sizeof(READ_ADDRESS_EVENT)) == 0) && (buf_len == 12)) {
+			for (i = 0; i < BD_ADDRESS_SIZE; i++)
+				g_card->bdaddr[i] = skb->data[6 + i];
+
+			BTMTK_DBG("GET TV BDADDR = %02X:%02X:%02X:%02X:%02X:%02X",
+			g_card->bdaddr[0], g_card->bdaddr[1], g_card->bdaddr[2],
+			g_card->bdaddr[3], g_card->bdaddr[4], g_card->bdaddr[5]);
+
+			/*
+			 * event_compare_status =
+			 * BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+			 */
+		} else
+			BTMTK_DBG("READ_ADDRESS_EVENT compare fail buf_len %d", buf_len);
+	}
+
+	if (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE) {
+		if (buf_len >= event_need_compare_len) {
+			if (memcmp(skb->data, event_need_compare, event_need_compare_len) == 0) {
+				event_compare_status = BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+				BTMTK_DBG("compare success");
+				/* Drop by driver, don't send to stack */
+				if(skb_queue_len(&g_card->discard_opcode_queue))
+				{
+					skb_queue_walk_safe(&g_card->discard_opcode_queue, skb_opcode, tmp_opcode) {
+						if((skb_opcode->data[0] == skb->data[3] && skb_opcode->data[1] == skb->data[4]) ||
+							(skb->data[0] == 0xE6 && skb_opcode->data[0] ==0xC9) ||
+							(skb->data[0] == 0xE8 && skb_opcode->data[0] ==0xCE))
+						{
+							BTMTK_INFO("%s: remove event from discard queue", __func__);
+							skb_unlink(skb_opcode, &g_card->discard_opcode_queue);
+							kfree_skb(skb_opcode);
+						}
+					}
+				}
+				goto exit;
+
+			} else {
+				BTMTK_DBG("%s compare fail", __func__);
+		BTSDIO_INFO_RAW(event_need_compare, event_need_compare_len,
+			"%s: event_need_compare:", __func__);
+		BTSDIO_INFO_RAW(skb->data, buf_len,
+			"%s: skb->data:", __func__);
+			}
+		}
+	}
+
+	if (is_fwdump == 0) {
+		btmtk_sdio_hci_snoop_save(type, skb->data, buf_len);
+		btmtk_sdio_dispatch_data_bluetooth_kpi(&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE], buf_len + 1, 0);
+	}
+
+	if (type == HCI_EVENT_PKT) {
+		if(skb_queue_len(&g_card->discard_opcode_queue))
+		{
+			BTMTK_INFO("%s: discard opcode queue len is %d", __func__,
+					skb_queue_len(&g_card->discard_opcode_queue));
+
+			skb_queue_walk_safe(&g_card->discard_opcode_queue, skb_opcode, tmp_opcode)
+			{
+				//first check command status event
+				if(skb->data[0] == 0x0F && skb_opcode->data[0] == skb->data[4] && skb_opcode->data[1] == skb->data[5])
+				{
+					BTMTK_INFO("%s: discard vendor cmd status", __func__);
+					goto exit;
+				}
+
+				//first check command complete event
+				if(skb->data[0] == 0x0E && skb_opcode->data[0] == skb->data[3] && skb_opcode->data[1] == skb->data[4])
+				{
+					BTMTK_INFO("%s: discard cmd event", __func__);
+					skb_unlink(skb_opcode, &g_card->discard_opcode_queue);
+					kfree_skb(skb_opcode);
+					goto exit;
+				}
+			}
+		}
+	}
+
+#if 0
+	/* to drop picus related event after save event, don't send picus event to host,
+	 * because host will trace this event as other host cmd's event,
+	 * it will cause command timeout
+	 */
+	if ((skb->data[3] == 0x5F || skb->data[3] == 0xBE) && skb->data[4] == 0xFC) {
+		BTSDIO_INFO_RAW(skb->data, buf_len, "%s: discard picus related event:", __func__);
+		goto exit;
+	}
+#endif
+
+	fops_skb = bt_skb_alloc(buf_len, GFP_ATOMIC);
+	if (fops_skb == NULL) {
+		BTMTK_WARN("No free fops_skb");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	bt_cb(fops_skb)->pkt_type = type;
+	memcpy(fops_skb->data, skb->data, buf_len);
+
+	fops_skb->len = buf_len;
+	LOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+	skb_queue_tail(&g_card->fops_queue, fops_skb);
+	if (skb_queue_empty(&g_card->fops_queue))
+		BTMTK_INFO("fops_queue is empty");
+	UNLOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+
+	wake_up_interruptible(&inq);
+
+exit:
+	if (skb) {
+		BTMTK_DBG("fail free skb");
+		kfree_skb(skb);
+	}
+
+	if (is_fwdump == 1) {
+		/*
+		 * This is a fw issue
+		 * Fw will send some bytes extra 0x00 at tail of coredump packet
+		 * It will cause alignment failed
+		 * Workaround is that use buf_len with bytes extra 0x00 added
+		 * bytes extra 0x00 = Total length - buf_len - header length
+		 * 9 = 4(SDIO header) + 4(STP header) + 1(type)
+		 */
+		tail_len = rx_length - buf_len - 9;
+		buf_len += (4 + tail_len);
+	}
+	buf_len += 1;
+	if (buf_len % 4)
+		fourbalignment_len = buf_len + 4 - (buf_len % 4);
+	else
+		fourbalignment_len = buf_len;
+
+	if (rx_length < fourbalignment_len)
+		goto data_err;
+
+	rx_length -= fourbalignment_len;
+
+	if (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE)) {
+		memcpy(&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE],
+		&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE + fourbalignment_len],
+		rx_length - MTK_SDIO_PACKET_HEADER_SIZE);
+	}
+
+	BTMTK_DBG("ret %d, rx_length, %d,fourbalignment_len %d <--",
+		ret, rx_length, fourbalignment_len);
+
+	return ret;
+
+data_err:
+	BTMTK_ERR("data error!!! discard rxbuf:");
+	BTSDIO_INFO_RAW(rxbuf, rx_length, "rxbuf");
+	rx_length = MTK_SDIO_PACKET_HEADER_SIZE;
+	return -EINVAL;
+}
+
+static int btmtk_sdio_process_int_status(struct btmtk_private *priv)
+{
+	int ret = 0;
+	u32 u32rxdatacount = 0;
+	u32 u32ReadCRValue = 0;
+
+	btmtk_sdio_timestamp(BTMTK_SDIO_RX_CHECKPOINT_RX_START);
+
+	ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+	BTMTK_DBG("CHISR 0x%08x", u32ReadCRValue);
+	if (u32ReadCRValue & FIRMWARE_INT_BIT15) {
+		btmtk_sdio_set_no_fw_own(g_priv, TRUE);
+		btmtk_sdio_writel(CHISR, FIRMWARE_INT_BIT15);
+		BTMTK_DBG("CHISR 0x%08x", u32ReadCRValue);
+	}
+
+	BTMTK_DBG("check TX_EMPTY CHISR 0x%08x", u32ReadCRValue);
+	if (TX_EMPTY&u32ReadCRValue) {
+		ret = btmtk_sdio_writel(CHISR, (TX_EMPTY | TX_COMPLETE_COUNT));
+		priv->btmtk_dev.tx_dnld_rdy = true;
+		BTMTK_DBG("set tx_dnld_rdy 1");
+	}
+
+	if (RX_DONE&u32ReadCRValue)
+		ret = btmtk_sdio_recv_rx_data();
+
+	if (ret == 0) {
+		btmtk_sdio_timestamp(BTMTK_SDIO_RX_CHECKPOINT_RX_DONE);
+		while (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE)) {
+			btmtk_sdio_card_to_host(priv, NULL, -1, 0);
+			u32rxdatacount++;
+			BTMTK_DBG("u32rxdatacount %d, rx_length %d",
+				u32rxdatacount, rx_length);
+		}
+	}
+
+#if LOWER_POWER_SINK
+	btmtk_sdio_lp_wake_unlock(g_card);
+#endif
+
+	btmtk_sdio_timestamp(BTMTK_SDIO_RX_CHECKPOINT_ENABLE_INTR);
+	ret = btmtk_sdio_enable_interrupt(1);
+
+	return ret;
+}
+
+static void btmtk_sdio_interrupt(struct sdio_func *func)
+{
+	struct btmtk_private *priv;
+	struct btmtk_sdio_card *card;
+
+	card = sdio_get_drvdata(func);
+
+	if (!card)
+		return;
+
+
+	if (!card->priv)
+		return;
+
+	btmtk_sdio_timestamp(BTMTK_SDIO_RX_CHECKPOINT_INTR);
+	priv = card->priv;
+	btmtk_sdio_enable_interrupt(0);
+
+	btmtk_interrupt(priv);
+#if LOWER_POWER_SINK
+	btmtk_sdio_lp_wake_lock(g_card);
+#endif
+}
+
+static int btmtk_sdio_register_dev(struct btmtk_sdio_card *card)
+{
+	struct sdio_func *func;
+	u8	u8ReadCRValue = 0;
+	u8 reg;
+	int ret = 0;
+
+	if (!card || !card->func) {
+		BTMTK_ERR("Error: card or function is NULL!");
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	func = card->func;
+
+	sdio_claim_host(func);
+
+	ret = sdio_enable_func(func);
+	sdio_release_host(g_card->func);
+	if (ret) {
+		BTMTK_ERR("sdio_enable_func() failed: ret=%d", ret);
+		ret = -EIO;
+		goto failed;
+	}
+
+	btmtk_sdio_readb(SDIO_CCCR_IENx, &u8ReadCRValue);
+	BTMTK_INFO("before claim irq read SDIO_CCCR_IENx %x, func num %d",
+		u8ReadCRValue, func->num);
+
+	sdio_claim_host(g_card->func);
+	ret = sdio_claim_irq(func, btmtk_sdio_interrupt);
+	sdio_release_host(g_card->func);
+	if (ret) {
+		BTMTK_ERR("sdio_claim_irq failed: ret=%d", ret);
+		ret = -EIO;
+		goto disable_func;
+	}
+	BTMTK_INFO("sdio_claim_irq success: ret=%d", ret);
+
+	btmtk_sdio_readb(SDIO_CCCR_IENx, &u8ReadCRValue);
+	BTMTK_INFO("after claim irq read SDIO_CCCR_IENx %x", u8ReadCRValue);
+
+	sdio_claim_host(g_card->func);
+	ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE);
+	sdio_release_host(g_card->func);
+	if (ret) {
+		BTMTK_ERR("cannot set SDIO block size");
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	ret = btmtk_sdio_readb(card->reg->io_port_0, &reg);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+	card->ioport = reg;
+
+	ret = btmtk_sdio_readb(card->reg->io_port_1, &reg);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+	card->ioport |= (reg << 8);
+
+	ret = btmtk_sdio_readb(card->reg->io_port_2, &reg);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport |= (reg << 16);
+
+	BTMTK_INFO("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
+
+	if (card->reg->int_read_to_clear) {
+		ret = btmtk_sdio_readb(card->reg->host_int_rsr, &reg);
+		if (ret < 0) {
+			ret = -EIO;
+			goto release_irq;
+		}
+		ret = btmtk_sdio_writeb(card->reg->host_int_rsr, reg | 0x3f);
+		if (ret < 0) {
+			ret = -EIO;
+			goto release_irq;
+		}
+
+		ret = btmtk_sdio_readb(card->reg->card_misc_cfg, &reg);
+		if (ret < 0) {
+			ret = -EIO;
+			goto release_irq;
+		}
+		ret = btmtk_sdio_writeb(card->reg->card_misc_cfg, reg | 0x10);
+		if (ret < 0) {
+			ret = -EIO;
+			goto release_irq;
+		}
+	}
+
+	sdio_set_drvdata(func, card);
+
+	return 0;
+
+release_irq:
+	sdio_release_irq(func);
+
+disable_func:
+	sdio_disable_func(func);
+
+failed:
+	BTMTK_INFO("fail");
+	return ret;
+}
+
+static int btmtk_sdio_unregister_dev(struct btmtk_sdio_card *card)
+{
+	if (card && card->func) {
+		sdio_claim_host(card->func);
+		sdio_release_irq(card->func);
+		sdio_disable_func(card->func);
+		sdio_release_host(card->func);
+		sdio_set_drvdata(card->func, NULL);
+	}
+
+	return 0;
+}
+
+static int btmtk_sdio_enable_host_int(struct btmtk_sdio_card *card)
+{
+	int ret;
+	u32 read_data = 0;
+
+	if (!card || !card->func)
+		return -EINVAL;
+
+	ret = btmtk_sdio_enable_host_int_mask(card, HIM_ENABLE);
+
+	btmtk_sdio_get_rx_unit(card);
+
+	if (0) {
+		typedef int (*fp_sdio_hook)(struct mmc_host *host,
+						unsigned int width);
+		fp_sdio_hook func_sdio_hook =
+			(fp_sdio_hook)btmtk_kallsyms_lookup_name("mmc_set_bus_width");
+		unsigned char data = 0;
+
+		sdio_claim_host(g_card->func);
+		data = sdio_f0_readb(card->func, SDIO_CCCR_IF, &ret);
+		if (ret)
+			BTMTK_INFO("sdio_f0_readb ret %d", ret);
+
+		BTMTK_INFO("sdio_f0_readb data 0x%X!", data);
+
+		data  &= ~SDIO_BUS_WIDTH_MASK;
+		data  |= SDIO_BUS_ASYNC_INT;
+		card->func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+		sdio_f0_writeb(card->func, data, SDIO_CCCR_IF, &ret);
+		if (ret)
+			BTMTK_INFO("sdio_f0_writeb ret %d", ret);
+
+		BTMTK_INFO("func_sdio_hook at 0x%p!", func_sdio_hook);
+		if (func_sdio_hook)
+			func_sdio_hook(card->func->card->host, MMC_BUS_WIDTH_1);
+
+		data = sdio_f0_readb(card->func, SDIO_CCCR_IF, &ret);
+		if (ret)
+			BTMTK_INFO("sdio_f0_readb 2 ret %d", ret);
+		sdio_release_host(g_card->func);
+
+		BTMTK_INFO("sdio_f0_readb2 data 0x%X", data);
+	}
+
+/* workaround for some platform no host clock sometimes */
+
+	btmtk_sdio_readl(CSDIOCSR, &read_data);
+	BTMTK_INFO("read CSDIOCSR is 0x%X", read_data);
+	read_data |= 0x4;
+	btmtk_sdio_writel(CSDIOCSR, read_data);
+	BTMTK_INFO("write CSDIOCSR is 0x%X", read_data);
+
+	return ret;
+}
+
+static int btmtk_sdio_disable_host_int(struct btmtk_sdio_card *card)
+{
+	int ret;
+
+	if (!card || !card->func)
+		return -EINVAL;
+
+	ret = btmtk_sdio_disable_host_int_mask(card, HIM_DISABLE);
+
+	return ret;
+}
+
+static int btmtk_sdio_download_fw(struct btmtk_sdio_card *card)
+{
+	int ret = 0;
+
+	BTMTK_INFO("begin");
+	if (!card || !card->func) {
+		BTMTK_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+
+	sdio_claim_host(card->func);
+
+	if (btmtk_sdio_download_rom_patch(card)) {
+		BTMTK_ERR("Failed to download firmware!");
+		ret = -EIO;
+	}
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmtk_sdio_push_data_to_metabuffer(
+						struct ring_buffer *metabuffer,
+						char *data,
+						int len,
+						u8 type,
+						bool use_type)
+{
+	int remainLen = 0;
+
+	if (metabuffer->write_p >= metabuffer->read_p)
+		remainLen = metabuffer->write_p - metabuffer->read_p;
+	else
+		remainLen = META_BUFFER_SIZE -
+			(metabuffer->read_p - metabuffer->write_p);
+
+	if ((remainLen + 1 + len) >= META_BUFFER_SIZE) {
+		BTMTK_DBG("copy copyLen %d > META_BUFFER_SIZE(%d), push back to queue",
+			(remainLen + 1 + len),
+			META_BUFFER_SIZE);
+		return -1;
+	}
+
+	if (use_type) {
+		metabuffer->buffer[metabuffer->write_p] = type;
+		metabuffer->write_p++;
+	}
+	if (metabuffer->write_p >= META_BUFFER_SIZE)
+		metabuffer->write_p = 0;
+
+	if (metabuffer->write_p + len <= META_BUFFER_SIZE)
+		memcpy(&metabuffer->buffer[metabuffer->write_p],
+			data,
+			len);
+	else {
+		memcpy(&metabuffer->buffer[metabuffer->write_p],
+			data,
+			META_BUFFER_SIZE - metabuffer->write_p);
+		memcpy(metabuffer->buffer,
+			&data[META_BUFFER_SIZE - metabuffer->write_p],
+			len - (META_BUFFER_SIZE - metabuffer->write_p));
+	}
+
+	metabuffer->write_p += len;
+	if (metabuffer->write_p >= META_BUFFER_SIZE)
+		metabuffer->write_p -= META_BUFFER_SIZE;
+
+	remainLen += (1 + len);
+	return 0;
+}
+
+static int btmtk_sdio_pull_data_from_metabuffer(
+						struct ring_buffer *metabuffer,
+						char __user *buf,
+						size_t count)
+{
+	int copyLen = 0;
+	unsigned long ret = 0;
+
+	if (metabuffer->write_p >= metabuffer->read_p)
+		copyLen = metabuffer->write_p - metabuffer->read_p;
+	else
+		copyLen = META_BUFFER_SIZE -
+			(metabuffer->read_p - metabuffer->write_p);
+
+	if (copyLen > count)
+		copyLen = count;
+
+	if (metabuffer->read_p + copyLen <= META_BUFFER_SIZE)
+		ret = copy_to_user(buf,
+				&metabuffer->buffer[metabuffer->read_p],
+				copyLen);
+	else {
+		ret = copy_to_user(buf,
+				&metabuffer->buffer[metabuffer->read_p],
+				META_BUFFER_SIZE - metabuffer->read_p);
+		if (!ret)
+			ret = copy_to_user(
+				&buf[META_BUFFER_SIZE - metabuffer->read_p],
+				metabuffer->buffer,
+				copyLen - (META_BUFFER_SIZE-metabuffer->read_p));
+	}
+
+	if (ret)
+		BTMTK_WARN("copy to user fail, ret %d", (int)ret);
+
+	metabuffer->read_p += (copyLen - ret);
+	if (metabuffer->read_p >= META_BUFFER_SIZE)
+		metabuffer->read_p -= META_BUFFER_SIZE;
+
+	return (copyLen - ret);
+}
+
+int btmtk_sdio_bt_trigger_core_dump(int trigger_dump)
+{
+	struct sk_buff *skb = NULL;
+	u8 coredump_cmd[] = {0x6F, 0xFC, 0x05,
+			0x01, 0x02, 0x01, 0x00, 0x08};
+
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL return");
+		return 0;
+	}
+
+	if (wait_dump_complete_tsk) {
+		BTMTK_WARN("wait_dump_complete_tsk is working, return");
+		return 0;
+	}
+
+#if !CFG_SUPPORT_CHIP_RESET_KO
+	if (wait_wlan_remove_tsk) {
+		BTMTK_WARN("wait_wlan_remove_tsk is working, return");
+		return 0;
+	}
+#endif
+
+	if (g_priv->btmtk_dev.reset_dongle) {
+		BTMTK_WARN("reset_dongle is true, return");
+		return 0;
+	}
+
+	if (!probe_ready) {
+		BTMTK_INFO("probe_ready %d, return -1",
+			probe_ready);
+		return -1;/*BT driver is not ready, ask wifi do coredump*/
+	}
+
+	BTMTK_INFO("trigger_dump %d", trigger_dump);
+#if !CFG_SUPPORT_CHIP_RESET_KO
+	if (trigger_dump) {
+		wlan_status = WLAN_STATUS_CALL_REMOVE_START;
+#endif
+		skb = bt_skb_alloc(sizeof(coredump_cmd), GFP_ATOMIC);
+		if (skb == NULL) {
+			BTMTK_ERR("skb alloc failed!");
+			return -1;
+		}
+		bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+		memcpy(&skb->data[0], &coredump_cmd[0], sizeof(coredump_cmd));
+		skb->len = sizeof(coredump_cmd);
+		skb_queue_tail(&g_card->tx_queue, skb);
+		wake_up_interruptible(&g_priv->main_thread.wait_q);
+#if !CFG_SUPPORT_CHIP_RESET_KO
+	} else {
+		if (g_card->bt_cfg.support_dongle_reset == 1) {
+			/* makesure wait thread is stopped */
+			btmtk_sdio_stop_wait_wlan_remove_tsk();
+			wait_wlan_remove_tsk =
+				kthread_run(btmtk_sdio_wait_wlan_remove_thread,
+					NULL,
+					"btmtk_sdio_wait_wlan_remove_thread");
+
+			msleep(100);
+			btmtk_sdio_notify_wlan_remove_start();
+		} else {
+			BTMTK_ERR("not support chip reset!");
+		}
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(btmtk_sdio_bt_trigger_core_dump);
+
+#if !CFG_SUPPORT_CHIP_RESET_KO
+void btmtk_sdio_notify_wlan_toggle_rst_end(void)
+{
+	typedef void (*pnotify_wlan_toggle_rst_end) (int reserved);
+	char *notify_wlan_toggle_rst_end_func_name =
+			"notify_wlan_toggle_rst_end";
+	/*void notify_wlan_toggle_rst_end(void)*/
+	pnotify_wlan_toggle_rst_end pnotify_wlan_toggle_rst_end_func =
+		(pnotify_wlan_toggle_rst_end) btmtk_kallsyms_lookup_name
+				(notify_wlan_toggle_rst_end_func_name);
+
+	BTMTK_INFO(L0_RESET_TAG);
+	if (pnotify_wlan_toggle_rst_end_func) {
+		BTMTK_INFO("do notify %s",
+			notify_wlan_toggle_rst_end_func_name);
+		pnotify_wlan_toggle_rst_end_func(1);
+	} else
+		BTMTK_ERR("do not get %s",
+			notify_wlan_toggle_rst_end_func_name);
+}
+
+int btmtk_sdio_notify_wlan_remove_end(void)
+{
+	BTMTK_INFO("begin");
+	wlan_remove_done = 1;
+	btmtk_sdio_stop_wait_wlan_remove_tsk();
+
+	BTMTK_INFO("done");
+	return 0;
+}
+EXPORT_SYMBOL(btmtk_sdio_notify_wlan_remove_end);
+
+int WF_rst_L0_notify_BT_step2(void)
+{
+	BTMTK_INFO(L0_RESET_TAG "begin");
+	btmtk_sdio_notify_wlan_remove_end();
+	BTMTK_INFO(L0_RESET_TAG "done");
+	return 0;
+}
+EXPORT_SYMBOL(WF_rst_L0_notify_BT_step2);
+
+int WF_rst_L0_notify_BT_step1(int force_toggle_reset)
+{
+	int ret = -1;
+
+	/* if BT is not ready for reset, return a non-positive value */
+	if (force_toggle_reset == true) {
+		if (is_mt7663(g_card) || is_mt7668(g_card)) {
+			BTMTK_INFO(L0_RESET_TAG "forcing reset dongle begin");
+			ret = btmtk_sdio_bt_trigger_core_dump(false);
+			if (ret >= 0)
+				BTMTK_INFO(L0_RESET_TAG "done");
+			else
+				BTMTK_INFO(L0_RESET_TAG "failed");
+		} else {
+			BTMTK_ERR(L0_RESET_TAG "is not MT766x");
+		}
+	} else {
+		if (is_mt7663(g_card) || is_mt7668(g_card)) {
+			BTMTK_INFO(L0_RESET_TAG "begin");
+			ret = btmtk_sdio_bt_trigger_core_dump(true);
+			if (ret >= 0)
+				BTMTK_INFO(L0_RESET_TAG "done");
+			else
+				BTMTK_INFO(L0_RESET_TAG "failed");
+		} else {
+			BTMTK_ERR(L0_RESET_TAG "is not MT766x");
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(WF_rst_L0_notify_BT_step1);
+#endif
+
+#ifdef MTK_KERNEL_DEBUG
+static int btmtk_sdio_L0_hang_thread(void *data)
+{
+	do {
+		BTMTK_INFO(L0_RESET_TAG "Whole Chip Reset was triggered");
+		msleep(3000);
+	} while (1);
+	return 0;
+}
+
+static int btmtk_sdio_L0_debug_probe(struct sdio_func *func,
+					const struct sdio_device_id *id)
+{
+	int ret = 0;
+	struct task_struct *task = NULL;
+	struct btmtk_sdio_device *data = (void *) id->driver_data;
+	u32 u32ReadCRValue = 0;
+	u8 fw_download_fail = 0;
+
+	BTMTK_INFO(L0_RESET_TAG "flow end");
+	probe_counter++;
+	BTMTK_INFO(L0_RESET_TAG "Mediatek Bluetooth driver Version=%s", VERSION);
+	BTMTK_INFO(L0_RESET_TAG "vendor=0x%x, device=0x%x, class=%d, fn=%d, support func_num %d",
+			id->vendor, id->device, id->class,
+			func->num, data->reg->func_num);
+
+	if (func->num != data->reg->func_num) {
+		BTMTK_ERR(L0_RESET_TAG "func num is not match");
+		return -ENODEV;
+		}
+
+	g_card->func = func;
+
+	if (id->driver_data) {
+		g_card->helper = data->helper;
+		g_card->reg = data->reg;
+		g_card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
+		g_card->support_pscan_win_report = data->support_pscan_win_report;
+		g_card->supports_fw_dump = data->supports_fw_dump;
+		g_card->chip_id = data->reg->chip_id;
+		g_card->suspend_count = 0;
+		BTMTK_INFO(L0_RESET_TAG "chip_id %x", data->reg->chip_id);
+	}
+
+	if (btmtk_sdio_register_dev(g_card) < 0) {
+		BTMTK_ERR(L0_RESET_TAG "Failed to register BT device!");
+		return -ENODEV;
+	}
+	BTMTK_INFO("btmtk_sdio_register_dev success");
+
+	/* Disable the interrupts on the card */
+	btmtk_sdio_enable_host_int(g_card);
+	BTMTK_DBG(L0_RESET_TAG "call btmtk_sdio_enable_host_int done");
+	if (btmtk_sdio_download_fw(g_card)) {
+		BTMTK_ERR(L0_RESET_TAG "Downloading firmware failed!");
+		fw_download_fail = 1;
+	}
+
+	task = kthread_run(btmtk_sdio_L0_hang_thread,
+					NULL, "btmtk_sdio_L0_hang_thread");
+	if (IS_ERR(task)) {
+		BTMTK_INFO(L0_RESET_TAG "create thread fail");
+		goto unreg_dev;
+	}
+
+	ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+	BTMTK_DBG(L0_RESET_TAG "chipid (0x%X)", g_card->chip_id);
+
+	BTMTK_INFO(L0_RESET_TAG "PASS!!");
+
+	if (fw_download_fail)
+		btmtk_sdio_start_reset_dongle_progress();
+
+	return 0;
+
+unreg_dev:
+	btmtk_sdio_unregister_dev(g_card);
+
+	BTMTK_ERR(L0_RESET_TAG "fail end");
+	return ret;
+}
+#else
+static int btmtk_sdio_L0_probe(struct sdio_func *func,
+					const struct sdio_device_id *id)
+{
+	int ret = 0;
+	/* Set flags/functions here to leave HW reset mark before probe. */
+
+	/* Now, ready to branch onto true sdio card probe. */
+	ret = btmtk_sdio_probe(func, id);
+
+	need_reset_stack = 1;
+	BTMTK_INFO("need_reset_stack %d probe_ret %d", need_reset_stack, ret);
+	wake_up_interruptible(&inq);
+	return ret;
+}
+#endif
+
+static int btmtk_sdio_L0_reset_host_config(struct mmc_host *host)
+{
+
+	if (host == NULL) {
+		BTMTK_ERR(L0_RESET_TAG "mmc host is NULL");
+		return -1;
+	}
+
+	if (host->rescan_entered != 0) {
+		host->rescan_entered = 0;
+		BTMTK_INFO(L0_RESET_TAG "set mmc_host rescan to 0");
+	}
+
+	BTMTK_DBG(L0_RESET_TAG "done");
+		return 0;
+}
+
+static int btmtk_sdio_L0_reset(struct mmc_card *card)
+{
+	int ret = -1;
+	struct mmc_host *host = NULL;
+
+	if ((card == NULL) || (card->host  == NULL)) {
+		BTMTK_ERR(L0_RESET_TAG "mmc structs are NULL");
+		return ret;
+	}
+
+	host = card->host;
+	ret = btmtk_sdio_L0_reset_host_config(host);
+	if (ret != 0) {
+		BTMTK_ERR(L0_RESET_TAG "set SDIO host failed");
+		return ret;
+	}
+
+	BTMTK_INFO(L0_RESET_TAG "mmc_remove_host");
+	mmc_remove_host(host);
+
+	/* Replace hooked SDIO driver probe to new API;
+	 * 1. It will be new kthread(state) after mmc_add_host;
+	 * 2. Extend flexibility to notify us that HW reset was triggered,
+	 * more flexiable on reviving in exchanging old/new kthread(state).
+	 */
+#ifdef MTK_KERNEL_DEBUG
+	/* For DBG purpose only, replace to customized probe.
+	 * Will only re-probe SDIO card function then hang for warning.
+	 */
+	btmtk_sdio_L0_hook_new_probe(btmtk_sdio_L0_debug_probe);
+#else
+	btmtk_sdio_L0_hook_new_probe(btmtk_sdio_L0_probe);
+#endif
+
+	BTMTK_INFO(L0_RESET_TAG "mmc_add_host");
+	ret = mmc_add_host(host);
+
+	BTMTK_INFO(L0_RESET_TAG "mmc_add_host return %d", ret);
+	return ret;
+}
+
+int btmtk_sdio_host_reset_dongle(void)
+{
+	int ret = -1;
+
+	BTMTK_INFO("begin");
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv = NULL, return");
+		return ret;
+	}
+	if ((!g_card) || (!g_card->func) || (!g_card->func->card)) {
+		BTMTK_ERR(L0_RESET_TAG "data corrupted");
+		goto rst_dongle_done;
+	}
+
+	wlan_remove_done = 0;
+
+	ret = btmtk_sdio_L0_reset(g_card->func->card);
+	BTMTK_INFO(L0_RESET_TAG "HW Reset status <%d>.", ret);
+
+rst_dongle_done:
+#if !CFG_SUPPORT_CHIP_RESET_KO
+	btmtk_sdio_notify_wlan_toggle_rst_end();
+	wlan_status = WLAN_STATUS_DEFAULT;
+#endif
+	g_priv->btmtk_dev.tx_dnld_rdy = 1;
+	g_priv->btmtk_dev.reset_dongle = 0;
+
+	btmtk_clean_queue();
+	g_priv->btmtk_dev.reset_progress = 0;
+	dump_data_counter = 0;
+	dump_data_length = 0;
+	return ret;
+}
+
+int btmtk_sdio_reset_dongle(void)
+{
+#if CFG_SUPPORT_CHIP_RESET_KO
+	send_reset_event(RESET_MODULE_TYPE_BT,
+		RFSM_EVENT_TRIGGER_RESET);
+	g_priv->btmtk_dev.reset_dongle = 0;
+	return 0;
+#else
+	return btmtk_sdio_host_reset_dongle();
+#endif
+}
+
+#if CFG_SUPPORT_CHIP_RESET_KO
+void btmtk_resetko_notify(unsigned int event, void *data)
+{
+	BTMTK_INFO("%s: event: %d", __func__, event);
+	if (event == MODULE_NOTIFY_PRE_RESET)
+		send_reset_event(RESET_MODULE_TYPE_BT,
+				RFSM_EVENT_L0_RESET_READY);
+}
+
+void btmtk_resetko_reset(void)
+{
+	BTMTK_INFO("%s: begin", __func__);
+	btmtk_sdio_host_reset_dongle();
+}
+#endif
+
+static irqreturn_t btmtk_sdio_woble_isr(int irq, void *dev)
+{
+	struct btmtk_sdio_card *data = (struct btmtk_sdio_card *)dev;
+
+	BTMTK_INFO("begin");
+	disable_irq_nosync(data->wobt_irq);
+	atomic_dec(&(data->irq_enable_count));
+	BTMTK_INFO("disable BT IRQ, call wake lock");
+	__pm_wakeup_event(data->eint_ws, WAIT_POWERKEY_TIMEOUT);
+
+	input_report_key(data->WoBLEInputDev, KEY_WAKEUP, 1);
+	input_sync(data->WoBLEInputDev);
+	input_report_key(data->WoBLEInputDev, KEY_WAKEUP, 0);
+	input_sync(data->WoBLEInputDev);
+	BTMTK_INFO("end");
+	return IRQ_HANDLED;
+}
+
+static int btmtk_sdio_RegisterBTIrq(struct btmtk_sdio_card *data)
+{
+	struct device_node *eint_node = NULL;
+	int interrupts[2];
+	int ret = 0;
+
+	eint_node = of_find_compatible_node(NULL, NULL, "mediatek,mt7668_bt_ctrl");
+	BTMTK_INFO("begin");
+	if (eint_node) {
+		BTMTK_INFO("Get mt76xx_bt_ctrl compatible node");
+		data->wobt_irq = irq_of_parse_and_map(eint_node, 0);
+		BTMTK_INFO("wobt_irq number:%d", data->wobt_irq);
+		if (data->wobt_irq) {
+			ret = of_property_read_u32_array(eint_node, "interrupts",
+						   interrupts, ARRAY_SIZE(interrupts));
+			if (ret) {
+				BTMTK_ERR("of_property_read_u32_array error");
+				return ret;
+			}
+			data->wobt_irqlevel = interrupts[1];
+			if (request_irq(data->wobt_irq, btmtk_sdio_woble_isr,
+					data->wobt_irqlevel, "mt7668_bt_ctrl-eint", data))
+				BTMTK_INFO("WOBTIRQ LINE NOT AVAILABLE!!");
+			else {
+				BTMTK_INFO("disable BT IRQ");
+				disable_irq_nosync(data->wobt_irq);
+			}
+
+		} else
+			BTMTK_INFO("can't find mt76xx_bt_ctrl irq");
+
+	} else {
+		data->wobt_irq = 0;
+		BTMTK_INFO("can't find mt76xx_bt_ctrl compatible node");
+	}
+
+
+	BTMTK_INFO("end");
+	return ret;
+}
+
+static int btmtk_sdio_woble_input_init(struct btmtk_sdio_card *data)
+{
+	int ret = 0;
+
+	data->WoBLEInputDev = input_allocate_device();
+	if (!data->WoBLEInputDev || IS_ERR(data->WoBLEInputDev)) {
+		BTMTK_ERR("input_allocate_device error");
+		return -ENOMEM;
+	}
+
+	data->WoBLEInputDev->name = "WOBLE_INPUT_DEVICE";
+	data->WoBLEInputDev->id.bustype = BUS_HOST;
+	data->WoBLEInputDev->id.vendor = 0x0002;
+	data->WoBLEInputDev->id.product = 0x0002;
+	data->WoBLEInputDev->id.version = 0x0002;
+
+	__set_bit(EV_KEY, data->WoBLEInputDev->evbit);
+	__set_bit(KEY_WAKEUP, data->WoBLEInputDev->keybit);
+
+	ret = input_register_device(data->WoBLEInputDev);
+	if (ret < 0) {
+		input_free_device(data->WoBLEInputDev);
+		data->WoBLEInputDev = NULL;
+		BTMTK_ERR("input_register_device %d", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void btmtk_sdio_woble_input_deinit(struct btmtk_sdio_card *data)
+{
+	if (data->WoBLEInputDev) {
+		input_unregister_device(data->WoBLEInputDev);
+		data->WoBLEInputDev = NULL;
+	}
+}
+
+static int btmtk_stereo_irq_handler(int irq, void *dev)
+{
+	/* Get sys clk */
+	sys_clk_tmp = (u64)btmtk_sdio_get_microseconds;
+	BTMTK_DBG("sys_clk %llu", sys_clk_tmp);
+	return 0;
+}
+
+static int btmtk_stereo_reg_irq(void)
+{
+	int ret = 0;
+	struct device_node *node;
+	int stereo_gpio;
+
+	BTMTK_INFO("start");
+	node = of_find_compatible_node(NULL, NULL, "mediatek,connectivity-combo");
+	if (node) {
+		stereo_gpio = of_get_named_gpio(node, "gpio_bt_stereo_pin", 0);
+		BTMTK_INFO("pmu_en %d", stereo_gpio);
+		if (gpio_is_valid(stereo_gpio))
+			gpio_direction_input(stereo_gpio);
+		else
+			BTMTK_ERR("invalid stereo gpio");
+
+		stereo_irq = irq_of_parse_and_map(node, 0);
+		ret = request_irq(stereo_irq, (irq_handler_t)btmtk_stereo_irq_handler,
+						IRQF_TRIGGER_RISING, "BTSTEREO_ISR_Handler", NULL);
+		if (ret) {
+			BTMTK_ERR("fail(%d)!!! irq_number=%d", ret, stereo_irq);
+			stereo_irq = -1;
+		}
+	} else {
+		BTMTK_INFO("of_find_compatible_node fail!!!");
+		ret = -1;
+		stereo_irq = -1;
+	}
+	return ret;
+}
+
+static void btmtk_stereo_unreg_irq(void)
+{
+	BTMTK_INFO("enter");
+	if (stereo_irq != -1)
+		free_irq(stereo_irq, NULL);
+	stereo_irq = -1;
+	BTMTK_INFO("exit");
+}
+
+static int btmtk_sdio_probe(struct sdio_func *func,
+					const struct sdio_device_id *id)
+{
+	int ret = 0;
+	struct btmtk_private *priv = NULL;
+	struct btmtk_sdio_device *data = (void *) id->driver_data;
+	u32 u32ReadCRValue = 0;
+	u8 fw_download_fail = 0;
+
+	probe_counter++;
+	BTMTK_INFO("Mediatek Bluetooth driver Version=%s", VERSION);
+	BTMTK_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d, support func_num %d",
+			id->vendor, id->device, id->class,
+			func->num, data->reg->func_num);
+#if CFG_SUPPORT_CHIP_RESET_KO
+	send_reset_event(RESET_MODULE_TYPE_BT, RFSM_EVENT_PROBE_START);
+#endif
+	if (func->num != data->reg->func_num) {
+		BTMTK_INFO("func num is not match");
+		ret = -ENODEV;
+		goto unreg_dev;
+	}
+
+	g_card->func = func;
+	g_card->bin_file_buffer = NULL;
+
+	if (id->driver_data) {
+		g_card->helper = data->helper;
+		g_card->reg = data->reg;
+		g_card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
+		g_card->support_pscan_win_report = data->support_pscan_win_report;
+		g_card->supports_fw_dump = data->supports_fw_dump;
+		g_card->chip_id = data->reg->chip_id;
+		g_card->suspend_count = 0;
+		BTMTK_INFO("chip_id is %x", data->reg->chip_id);
+		/*allocate memory for woble_setting_file*/
+		g_card->woble_setting_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+		if (!g_card->woble_setting_file_name) {
+			BTMTK_ERR("kzalloc woble_setting_file_name FAILED!");
+			ret = -ENOMEM;
+			goto unreg_dev;
+		}
+		need_retry_load_woble = 0;
+#if SUPPORT_MT7663
+		if (is_mt7668(g_card)) {
+			memcpy(g_card->woble_setting_file_name,
+					WOBLE_SETTING_FILE_NAME_7668,
+					sizeof(WOBLE_SETTING_FILE_NAME_7668));
+		}
+#endif
+
+#if SUPPORT_MT7668
+		if (is_mt7663(g_card)) {
+			memcpy(g_card->woble_setting_file_name,
+					WOBLE_SETTING_FILE_NAME_7663,
+					sizeof(WOBLE_SETTING_FILE_NAME_7663));
+		}
+#endif
+
+		/*allocate memory for bt_cfg_file_name*/
+		g_card->bt_cfg_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+		if (!g_card->bt_cfg_file_name) {
+			BTMTK_ERR("kzalloc bt_cfg_file_name FAILED!");
+			ret = -ENOMEM;
+			goto unreg_dev;
+		}
+
+		memcpy(g_card->bt_cfg_file_name, BT_CFG_NAME, sizeof(BT_CFG_NAME));
+	}
+
+	btmtk_sdio_initialize_cfg_items();
+	btmtk_sdio_load_setting_files(g_card->bt_cfg_file_name, &g_card->func->dev, g_card);
+
+	if (is_mt7663(g_card)) {
+		btmtk_load_bt_mac_file();
+		btmtk_sdio_loading_power_file();
+	}
+
+	/* check buffer mode */
+	btmtk_eeprom_bin_file(g_card);
+
+	/* Move from btmtk_fops_open() */
+	spin_lock_init(&(metabuffer.spin_lock.lock));
+	spin_lock_init(&(fwlog_metabuffer.spin_lock.lock));
+
+	spin_lock_init(&(stereo_spin_lock.lock));
+
+	BTMTK_DBG("spin_lock_init end");
+
+	priv = btmtk_add_card(g_card);
+	if (!priv) {
+		BTMTK_ERR("Initializing card failed!");
+		ret = -ENODEV;
+		goto unreg_dev;
+	}
+	BTMTK_DBG("btmtk_add_card success");
+	BTMTK_DBG("assign priv done");
+
+	BTMTK_DBG("func device %X, call btmtk_sdio_register_dev", g_card->func->device);
+	if (btmtk_sdio_register_dev(g_card) < 0) {
+		BTMTK_ERR("Failed to register BT device!");
+		ret = -ENODEV;
+		goto unreg_dev;
+	}
+
+	BTMTK_DBG("btmtk_sdio_register_dev success");
+
+	/* Disable the interrupts on the card */
+	btmtk_sdio_enable_host_int(g_card);
+	BTMTK_DBG("call btmtk_sdio_enable_host_int done");
+
+	if (btmtk_sdio_download_fw(g_card)) {
+		BTMTK_ERR("Downloading firmware failed!");
+		fw_download_fail = 1;
+	}
+
+	g_priv->hw_host_to_card = btmtk_sdio_host_to_card;
+	g_priv->hw_process_int_status = btmtk_sdio_process_int_status;
+	g_priv->hw_set_own_back =  btmtk_sdio_set_own_back;
+	g_priv->hw_sdio_reset_dongle = btmtk_sdio_reset_dongle;
+	g_priv->start_reset_dongle_progress = btmtk_sdio_start_reset_dongle_progress;
+	g_priv->hci_snoop_save = btmtk_sdio_hci_snoop_save;
+	btmtk_sdio_set_no_fw_own(g_priv, g_card->is_KeepFullPwr);
+
+	memset(&metabuffer.buffer, 0, META_BUFFER_SIZE);
+
+	fw_dump_file = NULL;
+
+	ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+	BTMTK_DBG("read CHLPCR (0x%08X), chipid is  (0x%X)",
+		u32ReadCRValue, g_card->chip_id);
+	if (is_support_unify_woble(g_card)) {
+		memset(g_card->bdaddr, 0, BD_ADDRESS_SIZE);
+		btmtk_sdio_load_setting_files(g_card->woble_setting_file_name,
+			&g_card->func->dev,
+			g_card);
+	}
+
+	if (g_card->bt_cfg.support_unify_woble && g_card->bt_cfg.support_woble_wakelock) {
+
+#if defined(CONFIG_MP_WAKEUP_SOURCE_SYSFS_STAT)
+		g_card->woble_ws = wakeup_source_register(NULL, "btevent_woble");
+#elif (defined(LINUX_OS) && (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE)) ||	\
+	(defined(ANDROID_OS) && (KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE))
+		g_card->woble_ws = wakeup_source_register("btevent_woble");
+#else
+		g_card->woble_ws = wakeup_source_register(NULL, "btevent_woble");
+#endif
+		if (!g_card->woble_ws) {
+			BTMTK_WARN("woble_ws register fail!");
+			goto unreg_dev;
+		}
+	}
+
+	if (g_card->bt_cfg.support_woble_by_eint) {
+#if defined(CONFIG_MP_WAKEUP_SOURCE_SYSFS_STAT)
+		g_card->eint_ws = wakeup_source_register(NULL, "btevent_eint");
+#elif (defined(LINUX_OS) && (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE)) ||	\
+	(defined(ANDROID_OS) && (KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE))
+		g_card->eint_ws = wakeup_source_register("btevent_eint");
+#else
+		g_card->eint_ws = wakeup_source_register(NULL, "btevent_eint");
+#endif
+		if (!g_card->eint_ws) {
+			wakeup_source_unregister(g_card->woble_ws);
+			BTMTK_WARN("eint_ws register fail!");
+			goto unreg_dev;
+		}
+
+		btmtk_sdio_RegisterBTIrq(g_card);
+		btmtk_sdio_woble_input_init(g_card);
+	}
+
+#if LOWER_POWER_SINK
+#if defined(CONFIG_MP_WAKEUP_SOURCE_SYSFS_STAT)
+	g_card->lp_ws = wakeup_source_register(NULL, "btevent_lp");
+#elif (defined(LINUX_OS) && (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE)) ||	\
+	(defined(ANDROID_OS) && (KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE))
+	g_card->lp_ws = wakeup_source_register("btevent_lp");
+#else
+	g_card->lp_ws = wakeup_source_register(NULL, "btevent_lp");
+#endif
+#endif
+	sema_init(&g_priv->wr_mtx, 1);
+	sema_init(&g_priv->rd_mtx, 1);
+
+	BTMTK_INFO("normal end");
+	probe_ready = true;
+	if (fw_download_fail)
+		btmtk_sdio_start_reset_dongle_progress();
+#if CFG_SUPPORT_CHIP_RESET_KO
+	send_reset_event(RESET_MODULE_TYPE_BT, RFSM_EVENT_PROBE_SUCCESS);
+#endif
+	return 0;
+
+unreg_dev:
+	btmtk_sdio_unregister_dev(g_card);
+#if CFG_SUPPORT_CHIP_RESET_KO
+	send_reset_event(RESET_MODULE_TYPE_BT, RFSM_EVENT_PROBE_FAIL);
+#endif
+	BTMTK_ERR("fail end");
+	return ret;
+}
+
+static void btmtk_sdio_remove(struct sdio_func *func)
+{
+	struct btmtk_sdio_card *card;
+
+	BTMTK_INFO("begin user_rmmod %d", user_rmmod);
+	probe_ready = false;
+	g_card->dongle_state = BT_SDIO_DONGLE_STATE_UNKNOWN;
+#if CFG_SUPPORT_CHIP_RESET_KO
+	send_reset_event(RESET_MODULE_TYPE_BT, RFSM_EVENT_REMOVE);
+#endif
+
+	btmtk_sdio_set_no_fw_own(g_priv, FALSE);
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			BTMTK_INFO(L0_RESET_TAG "begin reset_dongle <%d>",
+				card->priv->btmtk_dev.reset_dongle);
+			/* Send SHUTDOWN command & disable interrupt
+			 * if user removes the module.
+			 */
+			if (user_rmmod) {
+				BTMTK_INFO("begin user_rmmod %d in user mode", user_rmmod);
+				btmtk_sdio_enable_interrupt(0);
+				btmtk_sdio_disable_host_int(card);
+			}
+
+			if (card->bt_cfg.support_unify_woble && card->bt_cfg.support_woble_wakelock)
+				wakeup_source_unregister(card->woble_ws);
+
+			if (card->bt_cfg.support_woble_by_eint) {
+				wakeup_source_unregister(card->eint_ws);
+				btmtk_sdio_woble_input_deinit(g_card);
+			}
+
+#if LOWER_POWER_SINK
+			wakeup_source_unregister(card->lp_ws);
+#endif
+			btmtk_sdio_woble_free_setting();
+			btmtk_sdio_free_bt_cfg();
+			BTMTK_DBG("unregister dev");
+			card->priv->surprise_removed = true;
+#if CFG_SUPPORT_CHIP_RESET_KO
+			btmtk_remove_card(card->priv);
+#else
+			if (!card->priv->btmtk_dev.reset_dongle)
+				btmtk_remove_card(card->priv);
+#endif
+			btmtk_sdio_unregister_dev(card);
+			if (card->bin_file_buffer != NULL) {
+				kfree(card->bin_file_buffer);
+				card->bin_file_buffer = NULL;
+			}
+			need_reset_stack = 1;
+		}
+	}
+	skb_queue_purge(&g_card->discard_opcode_queue);
+	BTMTK_INFO("end");
+}
+
+/*
+ * cmd_type:
+ * #define HCI_COMMAND_PKT 0x01
+ * #define HCI_ACLDATA_PKT 0x02
+ * #define HCI_SCODATA_PKT 0x03
+ * #define HCI_EVENT_PKT 0x04
+ * #define HCI_VENDOR_PKT 0xff
+ */
+static int btmtk_sdio_send_hci_cmd(u8 cmd_type,
+				u8 *cmd, int cmd_len,
+				const u8 *event, const int event_len,
+				int total_timeout)
+		/* cmd: if cmd is null, don't compare event, just return 0 if send cmd success */
+		/* total_timeout: -1 */
+		/* add_spec_header:0 hci event, 1 use vend specic event header*/
+		/* return 0 if compare successfully and no need to compare */
+		/* return < 0 if error*/
+		/* return value: 0 or positive success, -x fail */
+{
+	int ret = -1;
+	unsigned long comp_event_timo = 0, start_time = 0;
+	struct sk_buff *skb = NULL;
+
+	if (cmd_len == 0) {
+		BTMTK_ERR("cmd_len (%d) error return", cmd_len);
+		return -EINVAL;
+	}
+
+
+	skb = bt_skb_alloc(cmd_len, GFP_ATOMIC);
+	if (skb == NULL) {
+		BTMTK_WARN("skb is null");
+		return -ENOMEM;
+	}
+	bt_cb(skb)->pkt_type = cmd_type;
+	memcpy(&skb->data[0], cmd, cmd_len);
+	skb->len = cmd_len;
+	if (event) {
+		event_compare_status = BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE;
+		memcpy(event_need_compare, event, event_len);
+		event_need_compare_len = event_len;
+	}
+	skb_queue_tail(&g_card->tx_queue, skb);
+	wake_up_interruptible(&g_priv->main_thread.wait_q);
+
+
+	if (event == NULL)
+		return 0;
+
+	if (event_len > EVENT_COMPARE_SIZE) {
+		BTMTK_ERR("event_len (%d) > EVENT_COMPARE_SIZE(%d), error", event_len, EVENT_COMPARE_SIZE);
+		return -1;
+	}
+
+	start_time = jiffies;
+	/* check HCI event */
+	comp_event_timo = jiffies + msecs_to_jiffies(total_timeout);
+	ret = -1;
+	BTMTK_DBG("event_need_compare_len %d, event_compare_status %d",
+		event_need_compare_len, event_compare_status);
+	do {
+		/* check if event_compare_success */
+		if (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS) {
+			BTMTK_DBG("compare success");
+			ret = 0;
+			break;
+		}
+		msleep(100);
+	} while (time_before(jiffies, comp_event_timo));
+	event_compare_status = BTMTK_SDIO_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE;
+	BTMTK_DBG("ret %d", ret);
+
+	if(ret)
+	{
+		BTMTK_INFO("trigger fw assert timeout");
+		btmtk_sdio_trigger_fw_assert();
+	}
+
+	return ret;
+}
+
+static int btmtk_sdio_trigger_fw_assert(void)
+{
+	int ret = 0;
+	/*
+	 * fw dump has 2 types
+	 * 1. Assert: trigger by hci cmd "5b fd 00" defined by bluedroid,
+	 * 2. Exception: trigger by wmt cmd "6F FC 05 01 02 01 00 08"
+	 */
+	u8 cmd[] = { 0x5b, 0xfd, 0x00 };
+
+	BTMTK_INFO("begin");
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd,
+		sizeof(cmd),
+		NULL, 0, WOBLE_COMP_EVENT_TIMO);
+	if (ret != 0)
+		BTMTK_INFO("ret = %d", ret);
+	return ret;
+}
+
+static int btmtk_sdio_send_get_vendor_cap(void)
+{
+	int ret = -1;
+	u8 get_vendor_cap_cmd[] = { 0x53, 0xFD, 0x00 };
+	u8 get_vendor_cap_event[] = { 0x0e, 0x12, 0x01, 0x53, 0xFD, 0x00};
+
+	BTMTK_DBG("begin");
+	BTSDIO_DEBUG_RAW(get_vendor_cap_cmd, (unsigned int)sizeof(get_vendor_cap_cmd),
+		"%s: send vendor_cap_cmd is:", __func__);
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+			get_vendor_cap_cmd, sizeof(get_vendor_cap_cmd),
+			get_vendor_cap_event, sizeof(get_vendor_cap_event),
+			WOBLE_COMP_EVENT_TIMO);
+
+	BTMTK_DBG("ret %d", ret);
+	return ret;
+}
+
+static int btmtk_sdio_send_read_BDADDR_cmd(void)
+{
+	u8 cmd[] = { 0x09, 0x10, 0x00 };
+	int ret = -1;
+	unsigned char zero[BD_ADDRESS_SIZE];
+
+	BTMTK_DBG("begin");
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card == NULL!");
+		return -1;
+	}
+
+	memset(zero, 0, sizeof(zero));
+	if (memcmp(g_card->bdaddr, zero, BD_ADDRESS_SIZE) != 0) {
+		BTMTK_DBG("already got bdaddr %02x%02x%02x%02x%02x%02x, return 0",
+		g_card->bdaddr[0], g_card->bdaddr[1], g_card->bdaddr[2],
+		g_card->bdaddr[3], g_card->bdaddr[4], g_card->bdaddr[5]);
+		return 0;
+	}
+	BTSDIO_DEBUG_RAW(cmd, (unsigned int)sizeof(cmd), "%s: send read bd address cmd is:", __func__);
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+		READ_ADDRESS_EVENT, sizeof(READ_ADDRESS_EVENT), WOBLE_COMP_EVENT_TIMO);
+	/*BD address will get in btmtk_sdio_host_to_card*/
+	BTMTK_DBG("ret = %d", ret);
+
+	return ret;
+}
+
+static int btmtk_sdio_set_Woble_APCF_filter_parameter(void)
+{
+	int ret = -1;
+	u8 cmd[] = { 0x57, 0xfd, 0x0a, 0x01, 0x00, 0x5a, 0x20, 0x00, 0x20, 0x00, 0x01, 0x80, 0x00 };
+	u8 event[] = { 0x0e, 0x07, 0x01, 0x57, 0xfd, 0x00, 0x01/*, 00, 63*/ };
+
+	BTMTK_DBG("begin");
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+		event, sizeof(event),
+		WOBLE_COMP_EVENT_TIMO);
+	if (ret < 0)
+		BTMTK_ERR("end ret %d", ret);
+	else
+		ret = 0;
+
+	BTMTK_INFO("end ret=%d", ret);
+	return ret;
+}
+
+
+/**
+ * Set APCF manufacturer data and filter parameter
+ */
+static int btmtk_sdio_set_Woble_APCF(void)
+{
+	int ret = -1;
+	unsigned int i = 0;
+	u8 manufactur_data[] = { 0x57, 0xfd, 0x27, 0x06, 0x00, 0x5a,
+		0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x4B, 0x54, 0x4D,
+		0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	u8 event_complete[] = { 0x0e, 0x07, 0x01, 0x57, 0xfd};
+
+	BTMTK_DBG("begin");
+	if (!g_card) {
+		BTMTK_INFO("g_card is NULL, return -1");
+		return -1;
+	}
+
+	BTMTK_DBG("g_card->woble_setting_apcf[0].length %d",
+		g_card->woble_setting_apcf[0].length);
+
+	/* start to send apcf cmd from woble setting  file */
+	if (g_card->woble_setting_apcf[0].length) {
+		for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
+			if (!g_card->woble_setting_apcf[i].length)
+				continue;
+
+			BTMTK_INFO("g_card->woble_setting_apcf_fill_mac[%d].content[0] = 0x%02x",
+				i, g_card->woble_setting_apcf_fill_mac[i].content[0]);
+			BTMTK_INFO("g_card->woble_setting_apcf_fill_mac_location[%d].length = %d",
+				i, g_card->woble_setting_apcf_fill_mac_location[i].length);
+
+			if ((g_card->woble_setting_apcf_fill_mac[i].content[0] == 1) &&
+				g_card->woble_setting_apcf_fill_mac_location[i].length) {
+				/* need add BD addr to apcf cmd */
+				memcpy(g_card->woble_setting_apcf[i].content +
+					(*g_card->woble_setting_apcf_fill_mac_location[i].content),
+					g_card->bdaddr, BD_ADDRESS_SIZE);
+				BTMTK_INFO("apcf %d ,add mac to location %d",
+					i, (*g_card->woble_setting_apcf_fill_mac_location[i].content));
+			}
+
+			BTMTK_INFO("send APCF %d", i);
+			BTSDIO_INFO_RAW(g_card->woble_setting_apcf[i].content, g_card->woble_setting_apcf[i].length,
+				"woble_setting_apcf");
+
+			btmtk_sdio_discard_opcode(&g_card->discard_opcode_queue, g_card->woble_setting_apcf[i].content);
+
+			ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, g_card->woble_setting_apcf[i].content,
+				g_card->woble_setting_apcf[i].length,
+				event_complete, sizeof(event_complete), WOBLE_COMP_EVENT_TIMO);
+
+			if (ret < 0) {
+				BTMTK_ERR("apcf %d error ret %d", i, ret);
+				return ret;
+			}
+
+		}
+	} else { /* use default */
+		BTMTK_INFO("use default manufactur data");
+		memcpy(manufactur_data + 9, g_card->bdaddr, BD_ADDRESS_SIZE);
+		BTSDIO_DEBUG_RAW(manufactur_data, (unsigned int)sizeof(manufactur_data),
+						"send manufactur_data ");
+
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, manufactur_data,
+				sizeof(manufactur_data),
+				event_complete, sizeof(event_complete), WOBLE_COMP_EVENT_TIMO);
+		if (ret < 0) {
+			BTMTK_ERR("manufactur_data error ret %d", ret);
+			return ret;
+		}
+
+		ret = btmtk_sdio_set_Woble_APCF_filter_parameter();
+	}
+
+	BTMTK_INFO("end ret=%d", ret);
+	return ret;
+}
+
+
+
+static int btmtk_sdio_send_woble_settings(struct fw_cfg_struct *settings_cmd,
+	struct fw_cfg_struct *settings_event, char *message)
+{
+	int ret = -1;
+	int i = 0;
+
+	BTMTK_INFO("%s length %d", message, settings_cmd->length);
+	if (g_card->woble_setting_radio_on[0].length) {
+		for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
+			if (settings_cmd[i].length) {
+				BTMTK_INFO("send %s %d", message, i);
+				BTSDIO_INFO_RAW(settings_cmd[i].content,
+					settings_cmd[i].length, "Raw");
+
+				btmtk_sdio_discard_opcode(&g_card->discard_opcode_queue, settings_cmd[i].content);
+
+				ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+						settings_cmd[i].content,
+						settings_cmd[i].length,
+						settings_event[i].content,
+						settings_event[i].length,
+						WOBLE_COMP_EVENT_TIMO);
+
+				if (ret) {
+					BTMTK_ERR("%s %d return error", message, i);
+					return ret;
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+static int btmtk_sdio_send_unify_woble_suspend_default_cmd(void)
+{
+	int ret = 0;	/* if successful, 0 */
+	/* Turn off WOBLE, FW go into low power mode only */
+	u8 cmd[] = { 0xC9, 0xFC, 0x14, 0x01, 0x20, 0x02, 0x00, 0x00,
+		0x02, 0x01, 0x00, 0x05, 0x10, 0x01, 0x00, 0x40, 0x06,
+		0x02, 0x40, 0x5A, 0x02, 0x41, 0x0F };
+	/*u8 status[] = { 0x0F, 0x04, 0x00, 0x01, 0xC9, 0xFC };*/
+	u8 event[] = { 0xE6, 0x02, 0x08, 0x00 };
+
+	BTMTK_DBG("begin");
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+				event, sizeof(event), WOBLE_COMP_EVENT_TIMO);
+	if (ret)
+		BTMTK_ERR("comp_event return error(%d)", ret);
+
+	return ret;
+}
+
+/**
+ * Set APCF manufacturer data and filter parameter
+ *
+ * WoBLE test command(TCI_TRIGGER_GPIO, 0xFD77) define:
+ * b3 GPIO pin (1 or 9)
+ * b4 active mode (0: low active, 1: high active)
+ * b5 duration (slots)
+ *
+ */
+static int btmtk_sdio_set_Woble_radio_off(u8 is_suspend)
+{
+	int ret = -1;
+	u8 cmd[] = { 0x77, 0xFD, 0x03, 0x01, 0x00, 0xA0 };
+	char *radio_off = NULL;
+	int length = 0;
+
+	if (is_suspend) {
+		BTMTK_DBG("g_card->woble_setting_radio_off[0].length %d",
+				g_card->woble_setting_radio_off[0].length);
+		BTMTK_DBG("g_card->woble_setting_radio_off_comp_event[0].length %d",
+				g_card->woble_setting_radio_off_comp_event[0].length);
+
+		if (g_card->woble_setting_radio_off[0].length) {
+			if (g_card->woble_setting_radio_off_comp_event[0].length && is_support_unify_woble(g_card)) {
+				length = g_card->woble_setting_radio_off[0].length +
+						g_card->woble_setting_wakeup_type[0].length;
+				radio_off = kzalloc(length, GFP_KERNEL);
+					if (!radio_off) {
+						BTMTK_ERR("%s: alloc memory fail (radio_off)",
+							__func__);
+						return -ENOMEM;
+					}
+
+				memcpy(radio_off,
+					g_card->woble_setting_radio_off[0].content,
+					g_card->woble_setting_radio_off[0].length);
+				if (g_card->woble_setting_wakeup_type[0].length) {
+					memcpy(radio_off + g_card->woble_setting_radio_off[0].length,
+							g_card->woble_setting_wakeup_type[0].content,
+							g_card->woble_setting_wakeup_type[0].length);
+							radio_off[2] += g_card->woble_setting_wakeup_type[0].length;
+						}
+
+				btmtk_sdio_discard_opcode(&g_card->discard_opcode_queue, radio_off);
+
+				ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+						radio_off,
+						length,
+						g_card->woble_setting_radio_off_comp_event[0].content,
+						g_card->woble_setting_radio_off_comp_event[0].length,
+						WOBLE_COMP_EVENT_TIMO);
+
+				if (ret) {
+					BTMTK_ERR("radio off error");
+					kfree(radio_off);
+					radio_off = NULL;
+					return ret;
+				}
+			} else
+				BTMTK_INFO("woble_setting_radio_off length is %d",
+					g_card->woble_setting_radio_off[0].length);
+		} else {/* use default */
+			BTMTK_INFO("use default radio off cmd");
+			ret = btmtk_sdio_send_unify_woble_suspend_default_cmd();
+		}
+	} else {
+		BTMTK_INFO("begin");
+
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+					NULL, 0, WOBLE_COMP_EVENT_TIMO);
+		if (ret)
+			BTMTK_ERR("comp_event return error(%d)", ret);
+	}
+	kfree(radio_off);
+	radio_off = NULL;
+	BTMTK_INFO("end ret=%d", ret);
+	return ret;
+}
+
+static int btmtk_sdio_handle_entering_WoBLE_state(u8 is_suspend)
+{
+	int ret = 0;
+	u8 radio_off_cmd[] = { 0xC9, 0xFC, 0x05, 0x01, 0x20, 0x02, 0x00, 0x00 };
+	u8 radio_off_evt[] = { 0xE6, 0x02, 0x08, 0x00 };
+	int fops_state = 0;
+
+	BTMTK_DBG("begin");
+
+#if LOWER_POWER_SINK
+	/* Can't enter woble mode */
+	BTMTK_INFO("not support woble mode for lp sink");
+	return 0;
+#endif
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+
+	if (!is_support_unify_woble(g_card)) {
+		if (fops_state == BTMTK_FOPS_STATE_OPENED) {
+			BTMTK_ERR("not support, send radio off");
+
+			BTSDIO_DEBUG_RAW(radio_off_cmd, (unsigned int)sizeof(radio_off_cmd),
+				"%s: send radio_off_cmd is:", __func__);
+			ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+					radio_off_cmd, sizeof(radio_off_cmd),
+					radio_off_evt, sizeof(radio_off_evt),
+					WOBLE_COMP_EVENT_TIMO);
+
+			BTMTK_DBG("ret %d", ret);
+		} else
+			BTMTK_WARN("when not support woble, in bt off state, do nothing!");
+	} else {
+		if (g_card->dongle_state != BT_SDIO_DONGLE_STATE_POWER_ON) {
+			if (!g_card->bt_cfg.support_woble_for_bt_disable) {
+				BTMTK_INFO("BT is off, not support WoBLE");
+				goto Finish;
+			}
+
+			if (btmtk_sdio_bt_set_power(1)) {
+				BTMTK_ERR("power on failed");
+				ret = -EIO;
+				goto Finish;
+			}
+			g_card->dongle_state = BT_SDIO_DONGLE_STATE_POWER_ON_FOR_WOBLE;
+		} else {
+			g_card->dongle_state = BT_SDIO_DONGLE_STATE_WOBLE;
+		}
+
+		if (is_suspend) {
+			ret = btmtk_sdio_send_get_vendor_cap();
+			if (ret < 0) {
+				BTMTK_ERR("btmtk_sdio_send_get_vendor_cap fail ret = %d", ret);
+				goto Finish;
+			}
+
+			ret = btmtk_sdio_send_read_BDADDR_cmd();
+			if (ret < 0) {
+				BTMTK_ERR("btmtk_sdio_send_read_BDADDR_cmd fail ret = %d", ret);
+				goto Finish;
+			}
+
+			ret = btmtk_sdio_set_Woble_APCF();
+			if (ret < 0) {
+				BTMTK_ERR("btmtk_sdio_set_Woble_APCF fail %d", ret);
+				goto Finish;
+			}
+		}
+		ret = btmtk_sdio_set_Woble_radio_off(is_suspend);
+		if (ret < 0) {
+			BTMTK_ERR("btmtk_sdio_set_Woble_radio_off return fail %d", ret);
+			goto Finish;
+		}
+	}
+Finish:
+	if (ret)
+		btmtk_sdio_woble_wake_lock(g_card);
+
+	BTMTK_INFO("end");
+	return ret;
+}
+
+#if ((CFG_ENABLE_DEBUG_WRITE == TRUE) || (LOWER_POWER_SINK != TRUE))
+static int btmtk_sdio_send_leave_woble_suspend_cmd(void)
+{
+	int ret = 0;	/* if successful, 0 */
+	u8 cmd[] = { 0xC9, 0xFC, 0x05, 0x01, 0x21, 0x02, 0x00, 0x00 };
+	u8 event[] = { 0xe6, 0x02, 0x08, 0x01 };
+
+	BTSDIO_DEBUG_RAW(cmd, (unsigned int)sizeof(cmd), "cmd ");
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+				event, sizeof(event), WOBLE_COMP_EVENT_TIMO);
+
+	if (ret < 0) {
+		BTMTK_ERR("failed(%d)", ret);
+	} else {
+		BTMTK_INFO("OK");
+		ret = 0;
+	}
+	return ret;
+}
+#endif
+
+static int btmtk_sdio_del_Woble_APCF_inde(void)
+{
+	int ret = -1;
+	u8 cmd[] = { 0x57, 0xfd, 0x03, 0x01, 0x01, 0x5a };
+	u8 event[] = { 0x0e, 0x07, 0x01, 0x57, 0xfd, 0x00, 0x01, /* 00, 63 */ };
+
+	BTMTK_DBG("begin");
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
+		event, sizeof(event), WOBLE_COMP_EVENT_TIMO);
+
+	if (ret < 0)
+		BTMTK_ERR("Got error %d", ret);
+
+	BTMTK_INFO("end ret = %d", ret);
+	return ret;
+}
+
+static void btmtk_sdio_check_wobx_debug_log(void)
+{
+	/* 0xFF, 0xFF, 0xFF, 0xFF is log level */
+	u8 cmd[] = { 0xCE, 0xFC, 0x04, 0xFF, 0xFF, 0xFF, 0xFF };
+	u8 event[] = { 0xE8 };
+	u8 *p = NULL, *pend = NULL;
+	int ret = -1;
+	u8 recv_len = 0;
+
+	BTMTK_INFO("%s: begin", __func__);
+	if (g_card == NULL) {
+		BTMTK_ERR("%s: Incorrect g_card", __func__);
+		return;
+	}
+
+	btmtk_sdio_discard_opcode(&g_card->discard_opcode_queue, cmd);
+
+	ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd), event, sizeof(event), WOBLE_COMP_EVENT_TIMO);
+	if (ret != 0) {
+		BTMTK_ERR("%s: failed(%d)", __func__, ret);
+		return;
+	}
+
+	recv_len = SDIO_HEADER_LEN + HCI_TYPE_LEN + HCI_EVENT_CODE_LEN;
+	recv_len += rxbuf[recv_len];
+	BTSDIO_INFO_RAW(rxbuf, recv_len, "%s: ", __func__);
+
+	/* parse WoBX debug log */
+	p = &rxbuf[SDIO_HEADER_LEN + HCI_TYPE_LEN + HCI_EVENT_CODE_LEN];
+	pend = p + rxbuf[SDIO_HEADER_LEN + HCI_TYPE_LEN + HCI_EVENT_CODE_LEN];
+	while (p < pend) {
+		u8 attr_len = *(p + 1);
+		u8 attr_type = *(p + 2);
+
+		BTMTK_INFO("attr_len = 0x%x, attr_type = 0x%x", attr_len, attr_type);
+		switch (attr_type) {
+		case WOBX_TRIGGER_INFO_ADDR_TYPE:
+			break;
+		case WOBX_TRIGGER_INFO_ADV_DATA_TYPE:
+			BTMTK_INFO("key code = %d", *(p + 8));
+			if(*(p + 8) == 0x02)
+			{
+				//send netflix key to input
+			}
+			break;
+		case WOBX_TRIGGER_INFO_TRACE_LOG_TYPE:
+			break;
+		case WOBX_TRIGGER_INFO_SCAN_LOG_TYPE:
+			break;
+		case WOBX_TRIGGER_INFO_TRIGGER_CNT_TYPE:
+			BTMTK_INFO("wakeup times(via BT) = %02X%02X%02X%02X",
+				*(p + 6), *(p + 5), *(p + 4), *(p + 3));
+			break;
+		default:
+			BTMTK_ERR("%s: unexpected attribute type(0x%x)", __func__, attr_type);
+			return;
+		}
+		p += 1 + attr_len;	// 1: len
+	}
+}
+
+static int btmtk_sdio_handle_leaving_WoBLE_state(void)
+{
+#if !LOWER_POWER_SINK
+	int ret = 0;
+	u8 radio_on_cmd[] = { 0xC9, 0xFC, 0x05, 0x01, 0x21, 0x02, 0x00, 0x00 };
+	u8 radio_on_evt[] = { 0xE6, 0x02, 0x08, 0x01 };
+	int fops_state = 0;
+#endif
+	BTMTK_DBG("begin");
+
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card is NULL return");
+		goto exit;
+	}
+
+#if LOWER_POWER_SINK
+	/* Can't enter woble mode */
+	BTMTK_INFO("not support woble mode for lp sink");
+	return 0;
+#endif
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+
+	if (!is_support_unify_woble(g_card)) {
+		if (fops_state == BTMTK_FOPS_STATE_OPENED) {
+			BTMTK_ERR("not support, send radio on");
+			BTSDIO_DEBUG_RAW(radio_on_cmd, (unsigned int)sizeof(radio_on_cmd),
+				"%s: send radio_on_cmd is:", __func__);
+			ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+					radio_on_cmd, sizeof(radio_on_cmd),
+					radio_on_evt, sizeof(radio_on_evt),
+					WOBLE_COMP_EVENT_TIMO);
+			BTMTK_DBG("ret %d", ret);
+			goto exit;
+		} else {
+			BTMTK_WARN("when not support woble, in bt off state, do nothing!");
+			goto exit;
+		}
+	}
+
+	if ((g_card->dongle_state != BT_SDIO_DONGLE_STATE_POWER_ON_FOR_WOBLE)
+		&& (g_card->dongle_state != BT_SDIO_DONGLE_STATE_WOBLE)) {
+		BTMTK_ERR("Not in woble mode");
+		goto exit;
+	}
+
+	if (g_card->woble_setting_radio_on[0].length &&
+		g_card->woble_setting_radio_on_comp_event[0].length &&
+		g_card->woble_setting_apcf_resume[0].length) {
+			/* start to send radio on cmd from woble setting file */
+		ret = btmtk_sdio_send_woble_settings(g_card->woble_setting_radio_on,
+			g_card->woble_setting_radio_on_comp_event, "radio on");
+		if (ret) {
+			BTMTK_ERR("woble radio on error");
+			goto finish;
+		}
+
+		ret = btmtk_sdio_send_woble_settings(g_card->woble_setting_apcf_resume,
+			g_card->woble_setting_apcf_resume_event, "apcf resume");
+		if (ret) {
+			BTMTK_ERR("apcf resume error");
+			goto finish;
+		}
+
+	} else { /* use default */
+		ret = btmtk_sdio_send_leave_woble_suspend_cmd();
+		if (ret) {
+			BTMTK_ERR("radio on error");
+			goto finish;
+		}
+
+		ret = btmtk_sdio_del_Woble_APCF_inde();
+		if (ret) {
+			BTMTK_ERR("del apcf index error");
+			goto finish;
+		}
+	}
+
+finish:
+	btmtk_sdio_check_wobx_debug_log();
+
+	if (g_card->dongle_state == BT_SDIO_DONGLE_STATE_POWER_ON_FOR_WOBLE) {
+		if (btmtk_sdio_bt_set_power(0)) {
+			BTMTK_ERR("power off failed");
+			return -EIO;
+		}
+	} else {
+		g_card->dongle_state = BT_SDIO_DONGLE_STATE_POWER_ON;
+	}
+
+exit:
+	BTMTK_INFO("end");
+	return ret;
+}
+
+static int btmtk_sdio_send_apcf_reserved(void)
+{
+	int ret = -1;
+	/* 76x8 APCF Cmd formate: [Header(0xFC5C), Len, Groups of APCF (Max. reserved 10)]
+	 * 76x3 APCF Cmd Formate: [Header(0xFC85), Len, Groups of APCF (Max. reserved 2)]
+	 */
+	u8 reserve_apcf_cmd_7668[] = { 0x5C, 0xFC, 0x01, 0x0A };
+	u8 reserve_apcf_event_7668[] = { 0x0e, 0x06, 0x01, 0x5C, 0xFC, 0x00 };
+
+	/* change apcf cmd and event according to mt7663 fw requirement in mp1.4*/
+	u8 reserve_apcf_cmd_7663[] = { 0x85, 0xFC, 0x01, 0x02 };
+	u8 reserve_apcf_event_7663[] = { 0x0e, 0x06, 0x01, 0x85, 0xFC, 0x00 };
+
+	if (g_card->func->device == 0x7668)
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+			reserve_apcf_cmd_7668, sizeof(reserve_apcf_cmd_7668),
+			reserve_apcf_event_7668, sizeof(reserve_apcf_event_7668),
+			WOBLE_COMP_EVENT_TIMO);
+	else if (g_card->func->device == 0x7663)
+		ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT,
+			reserve_apcf_cmd_7663, sizeof(reserve_apcf_cmd_7663),
+			reserve_apcf_event_7663, sizeof(reserve_apcf_event_7663),
+			WOBLE_COMP_EVENT_TIMO);
+	else
+		BTMTK_WARN("not support for 0x%x", g_card->func->device);
+
+	BTMTK_INFO("ret %d", ret);
+	return ret;
+}
+
+static int btmtk_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	int fops_state = 0;
+	int ret = 0;
+	mmc_pm_flag_t pm_flags;
+	struct irq_desc *desc;
+
+	BTMTK_INFO("begin");
+
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card is NULL return");
+		return 0;
+	}
+
+	if (g_card->suspend_count++) {
+		BTMTK_WARN("Has suspended. suspend_count: %d, end", g_card->suspend_count);
+		return 0;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if ((fops_state == BTMTK_FOPS_STATE_OPENING) || (fops_state == BTMTK_FOPS_STATE_CLOSING)) {
+		BTMTK_WARN("fops state is %d, suspend abort", fops_state);
+		return -ENOSYS;
+	}
+
+	btmtk_sdio_handle_entering_WoBLE_state(1);
+
+	if (g_card->bt_cfg.support_unify_woble && g_card->bt_cfg.support_woble_by_eint) {
+		if (g_card->wobt_irq != 0 && atomic_read(&(g_card->irq_enable_count)) == 0) {
+			/* clear irq data before enable woble irq to avoid DUT wake up
+			 *  automatically by edge trigger, sync from Jira CONN-50629
+			 */
+			desc = irq_to_desc(g_card->wobt_irq);
+			if (desc)
+				desc->irq_data.chip->irq_ack(&desc->irq_data);
+			else
+				BTMTK_ERR("%s:can't get desc\n", __func__);
+
+			BTMTK_INFO("enable BT IRQ:%d", g_card->wobt_irq);
+			irq_set_irq_wake(g_card->wobt_irq, 1);
+			enable_irq(g_card->wobt_irq);
+			atomic_inc(&(g_card->irq_enable_count));
+		} else
+			BTMTK_INFO("irq_enable count:%d", atomic_read(&(g_card->irq_enable_count)));
+	}
+
+	if (func) {
+		pm_flags = sdio_get_host_pm_caps(func);
+		if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+			BTMTK_ERR("%s cannot remain alive while suspended(0x%x)",
+				sdio_func_id(func), pm_flags);
+		}
+
+		pm_flags = MMC_PM_KEEP_POWER;
+		ret = sdio_set_host_pm_flags(func, pm_flags);
+		if (ret) {
+			BTMTK_ERR("set flag 0x%x err %d", pm_flags, (int)ret);
+			return -ENOSYS;
+		}
+	} else {
+		BTMTK_ERR("sdio_func is not specified");
+		return 0;
+	}
+
+	return 0;
+}
+
+static int btmtk_sdio_resume(struct device *dev)
+{
+	u8 ret = 0;
+	int fops_state = 0;
+
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card is NULL return");
+		return 0;
+	}
+
+	g_card->suspend_count--;
+	if (g_card->suspend_count) {
+		BTMTK_INFO("data->suspend_count %d, return 0", g_card->suspend_count);
+		return 0;
+	}
+
+	if (g_card->bt_cfg.support_unify_woble && g_card->bt_cfg.support_woble_by_eint) {
+		if (g_card->wobt_irq != 0 && atomic_read(&(g_card->irq_enable_count)) == 1) {
+			BTMTK_INFO("disable BT IRQ:%d", g_card->wobt_irq);
+			atomic_dec(&(g_card->irq_enable_count));
+			disable_irq_nosync(g_card->wobt_irq);
+		} else
+			BTMTK_INFO("irq_enable count:%d", atomic_read(&(g_card->irq_enable_count)));
+	}
+
+	ret = btmtk_sdio_handle_leaving_WoBLE_state();
+	if (ret)
+		BTMTK_ERR("btmtk_sdio_handle_leaving_WoBLE_state return fail  %d", ret);
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if (g_card->bt_cfg.reset_stack_after_woble
+		&& need_reset_stack == 0
+		&& fops_state == BTMTK_FOPS_STATE_OPENED)
+		need_reset_stack = 1;
+
+	BTMTK_INFO("end");
+	return 0;
+}
+
+static void btmtk_sdio_shutdown(struct device *dev)
+{
+	int fops_state = 0;
+	struct irq_desc *desc;
+
+	BTMTK_INFO("begin");
+
+	if (g_card == NULL) {
+		BTMTK_ERR("g_card is NULL return");
+		return;
+	}
+
+	if (g_card->suspend_count++) {
+		BTMTK_WARN("Has shutdown. suspend_count: %d, end", g_card->suspend_count);
+		return;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if ((fops_state == BTMTK_FOPS_STATE_OPENING) || (fops_state == BTMTK_FOPS_STATE_CLOSING)) {
+		BTMTK_WARN("fops state is %d, shutdown abort", fops_state);
+		return;
+	}
+
+	btmtk_sdio_handle_entering_WoBLE_state(1);
+
+	if (g_card->bt_cfg.support_unify_woble && g_card->bt_cfg.support_woble_by_eint) {
+		if (g_card->wobt_irq != 0 && atomic_read(&(g_card->irq_enable_count)) == 0) {
+			/* clear irq data before enable woble irq to avoid DUT wake up
+			 *  automatically by edge trigger, sync from Jira CONN-50629
+			 */
+			desc = irq_to_desc(g_card->wobt_irq);
+			if (desc)
+				desc->irq_data.chip->irq_ack(&desc->irq_data);
+			else
+				BTMTK_ERR("%s:can't get desc\n", __func__);
+
+			BTMTK_INFO("enable BT IRQ:%d", g_card->wobt_irq);
+			irq_set_irq_wake(g_card->wobt_irq, 1);
+			enable_irq(g_card->wobt_irq);
+			atomic_inc(&(g_card->irq_enable_count));
+		} else
+			BTMTK_INFO("irq_enable count:%d", atomic_read(&(g_card->irq_enable_count)));
+	}
+
+}
+static const struct dev_pm_ops btmtk_sdio_pm_ops = {
+	.suspend = btmtk_sdio_suspend,
+	.resume = btmtk_sdio_resume,
+};
+
+static struct sdio_driver bt_mtk_sdio = {
+	.name = "btmtk_sdio",
+	.id_table = btmtk_sdio_ids,
+	.probe = btmtk_sdio_probe,
+	.remove = btmtk_sdio_remove,
+	.drv = {
+		.owner = THIS_MODULE,
+		.pm = &btmtk_sdio_pm_ops,
+		.shutdown = btmtk_sdio_shutdown,
+	}
+};
+
+static void btmtk_sdio_L0_hook_new_probe(sdio_card_probe pFn_Probe)
+{
+	if (pFn_Probe == NULL) {
+		BTMTK_ERR(L0_RESET_TAG "new probe is NULL");
+		return;
+	}
+	bt_mtk_sdio.probe = pFn_Probe;
+}
+
+static int btmtk_clean_queue(void)
+{
+	BTMTK_INFO("enter");
+	LOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+	skb_queue_purge(&g_card->tx_queue);
+	skb_queue_purge(&g_card->fops_queue);
+	UNLOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+	BTMTK_INFO("end");
+	return 0;
+}
+
+static int btmtk_fops_open(struct inode *inode, struct file *file)
+{
+	int fops_state = 0;
+	int ret = 0;
+
+	BTMTK_INFO("begin");
+
+	BTMTK_INFO("Mediatek Bluetooth SDIO driver ver %s", VERSION);
+	BTMTK_INFO("major %d minor %d (pid %d), probe counter: %d",
+		imajor(inode), iminor(inode), current->pid, probe_counter);
+
+	if (!probe_ready) {
+		BTMTK_ERR("probe_ready is %d return", probe_ready);
+		return -EFAULT;
+	}
+
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		return -ENOENT;
+	}
+
+	if (g_priv->adapter == NULL) {
+		BTMTK_ERR("g_priv->adapter is NULL");
+		return -ENOENT;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if (fops_state == BTMTK_FOPS_STATE_CLOSING) {
+		BTMTK_ERR("mode is %d", fops_state);
+		return -EAGAIN;
+	}
+
+	if (fops_state == BTMTK_FOPS_STATE_OPENING || fops_state == BTMTK_FOPS_STATE_OPENED) {
+		BTMTK_ERR("mode is %d", fops_state);
+		return -ENOENT;
+	}
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_OPENING);
+	FOPS_MUTEX_UNLOCK();
+
+	metabuffer.read_p = 0;
+	metabuffer.write_p = 0;
+
+	btmtk_sdio_hci_snoop_init();
+
+	if (btmtk_sdio_send_init_cmds(g_card)) {
+		BTMTK_ERR("send init failed");
+		ret = -EAGAIN;
+		goto failed;
+	}
+
+	if (is_support_unify_woble(g_card)) {
+		if (btmtk_sdio_send_apcf_reserved()) {
+			BTMTK_ERR("send apcf failed");
+			ret = -EAGAIN;
+			goto failed;
+		}
+	}
+
+	btmtk_clean_queue();
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_OPENED);
+	FOPS_MUTEX_UNLOCK();
+
+	need_reset_stack = 0;
+	need_reopen = 0;
+	stereo_irq = -1;
+	BTMTK_INFO("end");
+
+	return ret;
+
+failed:
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_UNKNOWN);
+	FOPS_MUTEX_UNLOCK();
+
+	BTMTK_INFO("failed");
+	return ret;
+}
+
+static int btmtk_fops_close(struct inode *inode, struct file *file)
+{
+	int fops_state = 0;
+
+	BTMTK_INFO("begin");
+
+	BTMTK_INFO("Mediatek Bluetooth SDIO driver ver %s", VERSION);
+	BTMTK_INFO("major %d minor %d (pid %d), probe counter: %d",
+		imajor(inode), iminor(inode), current->pid, probe_counter);
+
+	if (!probe_ready) {
+		BTMTK_ERR("probe_ready is %d return", probe_ready);
+		return -EFAULT;
+	}
+
+	if (g_priv == NULL) {
+		pr_info("%s g_priv is NULL\n", __func__);
+		return -ENOENT;
+	}
+
+	if (g_priv->adapter == NULL) {
+		pr_info("%s g_priv->adapter is NULL\n", __func__);
+		return -ENOENT;
+	}
+
+	if (g_card->dongle_state != BT_SDIO_DONGLE_STATE_POWER_ON) {
+		BTMTK_ERR("dongle_state is %d", g_card->dongle_state);
+		goto exit;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if (fops_state != BTMTK_FOPS_STATE_OPENED) {
+		BTMTK_ERR("mode is %d", fops_state);
+		goto exit;
+	}
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_CLOSING);
+	FOPS_MUTEX_UNLOCK();
+
+	if (g_card->bt_cfg.support_auto_picus == true)
+		btmtk_sdio_picus_operation(false);
+
+	btmtk_sdio_send_hci_reset(true);
+	btmtk_sdio_send_deinit_cmds();
+
+exit:
+	btmtk_stereo_unreg_irq();
+	btmtk_clean_queue();
+	need_reopen = 0;
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_CLOSED);
+	FOPS_MUTEX_UNLOCK();
+
+	if (g_priv && g_priv->adapter)
+		BTMTK_INFO("end");
+	else
+		BTMTK_INFO("end g_priv or adapter is null");
+	return 0;
+}
+
+ssize_t btmtk_fops_write(struct file *filp, const char __user *buf,
+			size_t count, loff_t *f_pos)
+{
+	int retval = 0;
+	struct sk_buff *skb = NULL;
+	static u8 waiting_for_hci_without_packet_type; /* INITIALISED_STATIC: do not initialise statics to 0 */
+	static u8 hci_packet_type = 0xff;
+	u32 copy_size = 0;
+	unsigned long c_result = 0;
+	u8 pkt_type = 0xff;
+	u32 pkt_len = 0;
+	unsigned char *pkt_data = NULL;
+	int fops_state = 0;
+#if SUPPORT_CR_WR
+	u32 crAddr = 0, crValue = 0, crMask = 0;
+#endif
+
+	if (!probe_ready) {
+		BTMTK_ERR("probe_ready is %d return", probe_ready);
+		return -EFAULT;
+	}
+
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		return -EFAULT;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if (fops_state != BTMTK_FOPS_STATE_OPENED) {
+		BTMTK_ERR("fops_state is %d", fops_state);
+		return -EFAULT;
+	}
+
+	if (g_card->dongle_state != BT_SDIO_DONGLE_STATE_POWER_ON) {
+		BTMTK_ERR("dongle_state is %d return", g_card->dongle_state);
+		return 0;
+	}
+
+	if (need_reopen) {
+		BTMTK_ERR("need_reopen (%d)!", need_reopen);
+		return -EFAULT;
+	}
+
+	if (g_card->suspend_count) {
+		BTMTK_ERR("suspend_count is %d", g_card->suspend_count);
+		return -EAGAIN;
+	}
+
+	down(&g_priv->wr_mtx);
+
+	if (count > 0 && count < MTK_TXDATA_SIZE) {
+		memset(userbuf, 0, MTK_TXDATA_SIZE);
+		c_result = copy_from_user(userbuf, buf, count);
+	} else {
+		BTMTK_ERR("target packet length:%zu is not allowed", count);
+		retval = -EFAULT;
+		goto OUT;
+	}
+
+	if (c_result != 0) {
+		BTMTK_ERR("copy_from_user failed!");
+		retval = -EFAULT;
+		goto OUT;
+	}
+
+#if SUPPORT_CR_WR
+	if (userbuf[0] == 0x7 && waiting_for_hci_without_packet_type == 0) {
+		/* write CR */
+		if (count < 15) {
+			BTMTK_ERR("count=%zd less than 15, error", count);
+			retval = -EFAULT;
+			goto OUT;
+		}
+
+		crAddr = (userbuf[3] & 0xff) + ((userbuf[4] & 0xff) << 8)
+			+ ((userbuf[5] & 0xff) << 16) + ((userbuf[6] & 0xff) << 24);
+		crValue = (userbuf[7] & 0xff) + ((userbuf[8] & 0xff) << 8)
+			+ ((userbuf[9] & 0xff) << 16) + ((userbuf[10] & 0xff) << 24);
+		crMask = (userbuf[11] & 0xff) + ((userbuf[12] & 0xff) << 8)
+			+ ((userbuf[13] & 0xff) << 16) + ((userbuf[14] & 0xff) << 24);
+
+		BTMTK_INFO("crAddr=0x%08x crValue=0x%08x crMask=0x%08x", crAddr, crValue, crMask);
+		crValue &= crMask;
+
+		BTMTK_INFO("write crAddr=0x%08x crValue=0x%08x",
+			crAddr, crValue);
+		btmtk_sdio_writel(crAddr, crValue);
+		retval = count;
+		goto OUT;
+	} else if (userbuf[0] == 0x8 && waiting_for_hci_without_packet_type == 0) {
+		/* read CR */
+		if (count < 16) {
+			BTMTK_ERR("count=%zd less than 15, error", count);
+			retval = -EFAULT;
+			goto OUT;
+		}
+
+		crAddr = (userbuf[3] & 0xff) + ((userbuf[4] & 0xff) << 8) +
+			((userbuf[5] & 0xff) << 16) + ((userbuf[6] & 0xff) << 24);
+		crMask = (userbuf[11] & 0xff) + ((userbuf[12] & 0xff)<<8) +
+			((userbuf[13] & 0xff) << 16) + ((userbuf[14] & 0xff) << 24);
+
+		btmtk_sdio_readl(crAddr, &crValue);
+		BTMTK_INFO("read crAddr=0x%08x crValue=0x%08x crMask=0x%08x",
+			crAddr, crValue, crMask);
+		retval = count;
+		goto OUT;
+	}
+#endif
+	if (count == 1) {
+		if (waiting_for_hci_without_packet_type == 1) {
+			BTMTK_WARN("Waiting for hci_without_packet_type, but receive data count is 1!");
+			BTMTK_WARN("Treat this packet as packet_type(0x%02X)", userbuf[0]);
+		}
+		memcpy(&hci_packet_type, &userbuf[0], 1);
+		waiting_for_hci_without_packet_type = 1;
+		retval = 1;
+		goto OUT;
+	}
+
+	if (waiting_for_hci_without_packet_type) {
+		copy_size = count + 1;
+		pkt_type = hci_packet_type;
+		pkt_data = &userbuf[0];
+	} else {
+		copy_size = count;
+		pkt_type = userbuf[0];
+		pkt_data = &userbuf[1];
+	}
+
+	/* Check input length which must follow the BT spec.*/
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		/* HCI command : Type(8b) OpCode(16b) length(8b)
+		 * Header length = 1 + 2 + 1
+		 */
+		pkt_len = pkt_data[2] + MTK_HCI_CMD_HEADER_LEN;
+		break;
+	case HCI_ACLDATA_PKT:
+		/* ACL data : Type(8b) handle+flag(16b) length(16b)
+		 * Header length = 1 + 2 + 2
+		 */
+		pkt_len = (pkt_data[2] | (pkt_data[3] << 8)) + MTK_HCI_ACL_HEADER_LEN;
+		break;
+	case HCI_SCODATA_PKT:
+		/* SCO data : Type(8b) handle+flag(16b) length(8b)
+		 * Header length = 1 + 2 + 1
+		 */
+		pkt_len = pkt_data[2] + MTK_HCI_SCO_HEADER_LEN;
+		break;
+	default:
+		BTMTK_ERR("type is 0x%x", pkt_type);
+		retval = -EFAULT;
+		goto OUT;
+	}
+
+	/* check frame length is valid */
+	if (pkt_len != copy_size) {
+		BTMTK_ERR("input len(%d) error (expect %d)", copy_size, pkt_type);
+		retval = -EFAULT;
+		goto OUT;
+	}
+
+
+	skb = bt_skb_alloc(copy_size - 1, GFP_ATOMIC);
+	if (skb == NULL) {
+		BTMTK_ERR(" No meory for skb");
+		retval = -EFAULT;
+		goto OUT;
+	}
+	bt_cb(skb)->pkt_type = pkt_type;
+	memcpy(&skb->data[0], pkt_data, copy_size - 1);
+
+	skb->len = copy_size - 1;
+
+	if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+		u8 fw_assert_cmd[] = { 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
+		u8 reset_cmd[] = { 0x03, 0x0C, 0x00 };
+		u8 read_ver_cmd[] = { 0x01, 0x10, 0x00 };
+
+		if (skb->len == sizeof(fw_assert_cmd) &&
+			!memcmp(&skb->data[0], fw_assert_cmd, sizeof(fw_assert_cmd)))
+			BTMTK_INFO("Donge FW Assert Triggered by upper layer");
+		else if (skb->len == sizeof(reset_cmd) &&
+			!memcmp(&skb->data[0], reset_cmd, sizeof(reset_cmd)))
+			BTMTK_INFO("got command: 0x03 0C 00 (HCI_RESET)");
+		else if (skb->len == sizeof(read_ver_cmd) &&
+			!memcmp(&skb->data[0], read_ver_cmd, sizeof(read_ver_cmd)))
+			BTMTK_INFO("got command: 0x01 10 00 (READ_LOCAL_VERSION)");
+	}
+
+	skb_queue_tail(&g_card->tx_queue, skb);
+	wake_up_interruptible(&g_priv->main_thread.wait_q);
+
+	retval = copy_size;
+
+	if (waiting_for_hci_without_packet_type) {
+		hci_packet_type = 0xff;
+		waiting_for_hci_without_packet_type = 0;
+		if (retval > 0)
+			retval -= 1;
+	}
+
+OUT:
+	BTMTK_DBG("end");
+	up(&g_priv->wr_mtx);
+	return retval;
+}
+
+ssize_t btmtk_fops_read(struct file *filp, char __user *buf,
+			size_t count, loff_t *f_pos)
+{
+	struct sk_buff *skb = NULL;
+	int copyLen = 0;
+	u8 hwerr_event[] = { 0x04, 0x10, 0x01, 0xff };
+	static int send_hw_err_event_count;
+	int fops_state = 0;
+
+	if (!probe_ready) {
+		BTMTK_WARN_LIMITTED("probe_ready is %d return", probe_ready);
+		return -EFAULT;
+	}
+
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		return -EFAULT;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if ((fops_state != BTMTK_FOPS_STATE_OPENED) && (need_reset_stack == 0)) {
+		BTMTK_ERR("fops_state is %d", fops_state);
+		return -EFAULT;
+	}
+
+	if (g_card->suspend_count) {
+		BTMTK_ERR("suspend_count is %d", g_card->suspend_count);
+		return -EAGAIN;
+	}
+
+	down(&g_priv->rd_mtx);
+
+	if (need_reset_stack == 1) {
+		BTMTK_WARN("Reset BT stack, go if send_hw_err_event_count %d", send_hw_err_event_count);
+		if (send_hw_err_event_count < sizeof(hwerr_event)) {
+			if (count < (sizeof(hwerr_event) - send_hw_err_event_count)) {
+				copyLen = count;
+				BTMTK_INFO("call wake_up_interruptible");
+				wake_up_interruptible(&inq);
+			} else
+				copyLen = (sizeof(hwerr_event) - send_hw_err_event_count);
+			BTMTK_WARN("in if copyLen = %d", copyLen);
+			if (copy_to_user(buf, hwerr_event + send_hw_err_event_count, copyLen)) {
+				BTMTK_ERR("send_hw_err_event_count %d copy to user fail, count = %d, go out",
+					send_hw_err_event_count, copyLen);
+				copyLen = -EFAULT;
+				goto OUT;
+			}
+			send_hw_err_event_count += copyLen;
+			BTMTK_WARN("in if send_hw_err_event_count = %d", send_hw_err_event_count);
+			if (send_hw_err_event_count >= sizeof(hwerr_event)) {
+				send_hw_err_event_count  = 0;
+				BTMTK_WARN("set need_reset_stack=0");
+				need_reset_stack = 0;
+				need_reopen = 1;
+				kill_fasync(&fasync, SIGIO, POLL_IN);
+			}
+			BTMTK_WARN("set call up");
+			goto OUT;
+		} else {
+			BTMTK_ERR("xx set copyLen = -EFAULT");
+			copyLen = -EFAULT;
+			goto OUT;
+		}
+	}
+
+	if (get_hci_reset == 1) {
+		BTMTK_INFO("get reset complete and set audio!");
+		btmtk_sdio_send_audio_slave();
+		if (g_card->pa_setting != -1)
+			btmtk_set_pa((uint8_t)g_card->pa_setting);
+		if (g_card->duplex_setting != -1)
+			btmtk_set_duplex((uint8_t)g_card->duplex_setting);
+
+		if(g_card->func->device == 0x7663)
+			btmtk_set_mac_power();
+
+		if (g_card->efuse_mode == EFUSE_BIN_FILE_MODE)
+			btmtk_set_eeprom2ctrler((uint8_t *)g_card->bin_file_buffer,
+					g_card->bin_file_size,
+					g_card->func->device);
+		if (g_card->bt_cfg.support_auto_picus == true)
+			btmtk_sdio_picus_operation(true);
+
+		get_hci_reset = 0;
+	}
+
+	LOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+	if (skb_queue_empty(&g_card->fops_queue)) {
+		/* if (filp->f_flags & O_NONBLOCK) { */
+		if (metabuffer.write_p == metabuffer.read_p) {
+			UNLOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+			up(&g_priv->rd_mtx);
+			return 0;
+		}
+	}
+
+	if (need_reset_stack == 1) {
+		kill_fasync(&fasync, SIGIO, POLL_IN);
+		need_reset_stack = 0;
+		BTMTK_ERR("Call kill_fasync and set reset_stack 0");
+		UNLOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+		up(&g_priv->rd_mtx);
+		return -ENODEV;
+	}
+
+	do {
+		skb = skb_dequeue(&g_card->fops_queue);
+		if (skb == NULL) {
+			BTMTK_DBG("skb=NULL break");
+			break;
+		}
+
+		btmtk_print_buffer_conent(skb->data, skb->len);
+
+		if (btmtk_sdio_push_data_to_metabuffer(&metabuffer, skb->data,
+				skb->len, bt_cb(skb)->pkt_type, true) < 0) {
+			skb_queue_head(&g_card->fops_queue, skb);
+			break;
+		}
+		kfree_skb(skb);
+	} while (!skb_queue_empty(&g_card->fops_queue));
+	UNLOCK_UNSLEEPABLE_LOCK(&(metabuffer.spin_lock));
+
+	up(&g_priv->rd_mtx);
+	return btmtk_sdio_pull_data_from_metabuffer(&metabuffer, buf, count);
+
+OUT:
+	up(&g_priv->rd_mtx);
+	return copyLen;
+}
+
+static int btmtk_fops_fasync(int fd, struct file *file, int on)
+{
+	BTMTK_INFO("fd = 0x%X, flag = 0x%X", fd, on);
+	return fasync_helper(fd, file, on, &fasync);
+}
+
+unsigned int btmtk_fops_poll(struct file *filp, poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	if (!probe_ready) {
+		BTMTK_WARN_LIMITTED("probe_ready is %d return", probe_ready);
+		return 0;
+	}
+
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		return 0;
+	}
+
+	poll_wait(filp, &inq, wait);
+
+	if (metabuffer.write_p != metabuffer.read_p || need_reset_stack)
+		mask |= (POLLIN | POLLRDNORM);
+
+	if (!skb_queue_empty(&g_card->fops_queue)) {
+		if (skb_queue_len(&g_card->fops_queue))
+			mask |= (POLLIN | POLLRDNORM);
+		/* BTMTK_INFO("poll done"); */
+	}
+
+	mask |= (POLLOUT | POLLWRNORM);
+
+	/* BTMTK_INFO("poll mask 0x%x",mask); */
+	return mask;
+}
+
+static long btmtk_fops_unlocked_ioctl(struct file *filp,
+				unsigned int cmd, unsigned long arg)
+{
+	u32 ret = 0;
+	struct bt_stereo_para stereo_para;
+	struct sk_buff *skb = NULL;
+	u8 set_btclk[] = {0x01, 0x02, 0xFD, 0x0B,
+		0x00, 0x00,			/* Handle */
+		0x00,				/* Method */
+						/* bit0~3 = 0x0:CVSD remove, 0x1:GPIO, 0x2:In-band with transport*/
+						/* bit4~7 = 0x0:Shared memory, 0x1:auto event */
+		0x00, 0x00, 0x00, 0x00,		/* Period = value * 0.625ms */
+		0x09,				/* GPIO num - 0x01:BGF_INT_B, 0x09:GPIO0 */
+		0x01,				/* trigger mode - 0x00:Low, 0x01:High */
+		0x00, 0x00};			/* active slots = value * 0.625ms */
+
+
+	switch (cmd) {
+	case BTMTK_IOCTL_STEREO_GET_CLK:
+		BTMTK_DBG("BTMTK_IOCTL_STEREO_GET_CLK cmd");
+
+		LOCK_UNSLEEPABLE_LOCK(&stereo_spin_lock);
+		if (copy_to_user((struct bt_stereo_clk __user *)arg, &stereo_clk, sizeof(struct bt_stereo_clk)))
+			ret = -ENOMEM;
+		UNLOCK_UNSLEEPABLE_LOCK(&stereo_spin_lock);
+		break;
+	case BTMTK_IOCTL_STEREO_SET_PARA:
+		BTMTK_DBG("BTMTK_IOCTL_STEREO_SET_PARA cmd");
+		if (copy_from_user(&stereo_para, (struct bt_stereo_para __user *)arg,
+						sizeof(struct bt_stereo_para)))
+			return -ENOMEM;
+
+		if (stereo_para.period != 0)
+			btmtk_stereo_reg_irq();
+
+		/* Send and check HCI cmd */
+		memcpy(&set_btclk[4], &stereo_para.handle, sizeof(stereo_para.handle));
+		memcpy(&set_btclk[6], &stereo_para.method, sizeof(stereo_para.method));
+		memcpy(&set_btclk[7], &stereo_para.period, sizeof(stereo_para.period));
+		memcpy(&set_btclk[13], &stereo_para.active_slots, sizeof(stereo_para.active_slots));
+#if SUPPORT_MT7668
+		if (is_mt7668(g_card))
+			set_btclk[11] = 0x09;
+#endif
+#if SUPPORT_MT7663
+		if (is_mt7663(g_card))
+			set_btclk[11] = 0x0B;
+#endif
+
+		skb = bt_skb_alloc(sizeof(set_btclk) - 1, GFP_ATOMIC);
+		if (skb == NULL) {
+			btmtk_stereo_unreg_irq();
+			BTMTK_ERR("bt_skb_alloc fail return");
+			return -ENOMEM;
+		}
+		bt_cb(skb)->pkt_type = set_btclk[0];
+		memcpy(&skb->data[0], &set_btclk[1], sizeof(set_btclk) - 1);
+
+		skb->len = sizeof(set_btclk) - 1;
+		skb_queue_tail(&g_card->tx_queue, skb);
+		wake_up_interruptible(&g_priv->main_thread.wait_q);
+
+		if (stereo_para.period == 0)
+			btmtk_stereo_unreg_irq();
+		break;
+	default:
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static int btmtk_fops_openfwlog(struct inode *inode,
+					struct file *file)
+{
+	if (g_priv == NULL) {
+		BTMTK_ERR("ERROR, g_priv is NULL!");
+		return -ENODEV;
+	}
+
+	sema_init(&g_priv->wr_fwlog_mtx, 1);
+	BTMTK_INFO("OK");
+	return 0;
+}
+
+static int btmtk_fops_closefwlog(struct inode *inode,
+					struct file *file)
+{
+	if (g_priv == NULL) {
+		BTMTK_ERR("ERROR, g_priv is NULL!");
+		return -ENODEV;
+	}
+
+	BTMTK_INFO("OK");
+	return 0;
+}
+
+static ssize_t btmtk_fops_readfwlog(struct file *filp,
+			char __user *buf,
+			size_t count,
+			loff_t *f_pos)
+{
+	struct sk_buff *skb = NULL;
+	int copyLen = 0;
+
+	if (g_priv == NULL || g_priv->adapter == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		return -EFAULT;
+	}
+
+	if (g_card->suspend_count) {
+		BTMTK_ERR("suspend_count is %d", g_card->suspend_count);
+		return -EAGAIN;
+	}
+
+	/* picus read a queue, it may occur performace issue */
+	LOCK_UNSLEEPABLE_LOCK(&(fwlog_metabuffer.spin_lock));
+	if (skb_queue_len(&g_card->fwlog_fops_queue))
+		skb = skb_dequeue(&g_card->fwlog_fops_queue);
+	UNLOCK_UNSLEEPABLE_LOCK(&(fwlog_metabuffer.spin_lock));
+
+	if (skb == NULL)
+		return 0;
+
+	if (skb->len <= count) {
+		if (copy_to_user(buf, skb->data, skb->len)) {
+			BTMTK_ERR("copy_to_user failed!");
+			/* copy_to_user failed, add skb to fwlog_fops_queue */
+			skb_queue_head(&g_card->fwlog_fops_queue, skb);
+			copyLen = -EFAULT;
+			goto OUT;
+		}
+		copyLen = skb->len;
+	} else {
+		BTMTK_ERR("socket buffer length error(count: %d, skb.len: %d)", (int)count, skb->len);
+	}
+	kfree_skb(skb);
+OUT:
+	return copyLen;
+}
+
+static ssize_t btmtk_fops_writefwlog(
+			struct file *filp, const char __user *buf,
+			size_t count, loff_t *f_pos)
+{
+#if (CFG_ENABLE_DEBUG_WRITE == 0)
+	return -ENODEV;
+#else
+	struct sk_buff *skb = NULL;
+	int i = 0, len = 0, val = 0, ret = -1;
+	/*+1 is for i_fwlog_buf[count] = 0, end string byte*/
+	int i_fwlog_buf_size = HCI_MAX_COMMAND_BUF_SIZE + 1;
+	u8 *i_fwlog_buf = NULL;
+	u8 *o_fwlog_buf = NULL;
+	int fops_state = 0;
+	u32 crAddr = 0, crValue = 0;
+	struct irq_desc *desc;
+
+	if (g_priv == NULL || g_priv->adapter == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		goto exit;
+	}
+
+	down(&g_priv->wr_fwlog_mtx);
+
+	FOPS_MUTEX_LOCK();
+	fops_state = btmtk_fops_get_state();
+	FOPS_MUTEX_UNLOCK();
+	if (fops_state != BTMTK_FOPS_STATE_OPENED) {
+		BTMTK_ERR("fops_state is %d", fops_state);
+		count = -EFAULT;
+		goto exit;
+	}
+
+	if (g_card->suspend_count) {
+		BTMTK_ERR("suspend_count is %d", g_card->suspend_count);
+		count = -EAGAIN;
+		goto exit;
+	}
+
+	i_fwlog_buf = kmalloc(i_fwlog_buf_size, GFP_KERNEL);
+	o_fwlog_buf = kmalloc(HCI_MAX_COMMAND_SIZE + 16, GFP_KERNEL);
+
+	if (i_fwlog_buf == NULL || o_fwlog_buf == NULL) {
+		BTMTK_ERR("buf alloc fail");
+		count = -ENOMEM;
+		goto exit;
+	}
+
+	memset(i_fwlog_buf, 0, i_fwlog_buf_size);
+	memset(o_fwlog_buf, 0, HCI_MAX_COMMAND_SIZE + 16);
+
+	if (count > HCI_MAX_COMMAND_BUF_SIZE) {
+		BTMTK_ERR("your command is larger than maximum length, count = %zd", count);
+		count = -EFAULT;
+		goto exit;
+	}
+
+	if (copy_from_user(i_fwlog_buf, buf, count) != 0) {
+		BTMTK_ERR("copy_from_user failed!");
+		count = -EFAULT;
+		goto exit;
+	}
+
+	i_fwlog_buf[count] = 0;
+
+	if (strstr(i_fwlog_buf, FW_OWN_OFF)) {
+		BTMTK_WARN("set FW_OWN_OFF");
+		btmtk_sdio_set_no_fw_own(g_priv, true);
+		len = count;
+		wake_up_interruptible(&g_priv->main_thread.wait_q);
+		goto exit;
+	}
+
+	if (strstr(i_fwlog_buf, FW_OWN_ON)) {
+		BTMTK_WARN("set FW_OWN_ON");
+		btmtk_sdio_set_no_fw_own(g_priv, false);
+		len = count;
+		wake_up_interruptible(&g_priv->main_thread.wait_q);
+		goto exit;
+	}
+
+	if (strstr(i_fwlog_buf, WOBLE_ON)) {
+		BTMTK_INFO("set WOBLE_ON");
+		btmtk_sdio_handle_entering_WoBLE_state(0);
+		len = count;
+		if (g_card->bt_cfg.support_woble_by_eint) {
+			if (g_card->wobt_irq != 0 &&
+				atomic_read(&(g_card->irq_enable_count)) == 0) {
+				/* clear irq data before enable woble irq to avoid DUT wake up
+				 *  automatically by edge trigger, sync from Jira CONN-50629
+				 */
+				desc = irq_to_desc(g_card->wobt_irq);
+				if (desc)
+					desc->irq_data.chip->irq_ack(&desc->irq_data);
+				else
+					BTMTK_ERR("%s:can't get desc\n", __func__);
+
+				BTMTK_INFO("enable BT IRQ:%d", g_card->wobt_irq);
+				irq_set_irq_wake(g_card->wobt_irq, 1);
+				enable_irq(g_card->wobt_irq);
+				atomic_inc(&(g_card->irq_enable_count));
+			} else
+				BTMTK_INFO("irq_enable count:%d",
+					atomic_read(&(g_card->irq_enable_count)));
+		}
+		goto exit;
+	}
+
+	if (strstr(i_fwlog_buf, WOBLE_OFF)) {
+		BTMTK_INFO("set WOBLE_OFF");
+		if (g_card->bt_cfg.support_woble_by_eint) {
+			if (g_card->wobt_irq != 0 &&
+				atomic_read(&(g_card->irq_enable_count)) == 1) {
+				BTMTK_INFO("disable BT IRQ:%d", g_card->wobt_irq);
+				atomic_dec(&(g_card->irq_enable_count));
+				disable_irq_nosync(g_card->wobt_irq);
+			} else
+				BTMTK_INFO(":irq_enable count:%d",
+					atomic_read(&(g_card->irq_enable_count)));
+		}
+		btmtk_sdio_send_leave_woble_suspend_cmd();
+		len = count;
+		goto exit;
+	}
+
+	if (strstr(i_fwlog_buf, RX_CHECK_ON)) {
+		/* echo rx check on 0=100000 */
+		int type, i;
+
+		if (strstr(i_fwlog_buf, "=")) {
+			type = i_fwlog_buf[strlen(RX_CHECK_ON) + 1] - '0';
+			if (type < 0 || type >= BTMTK_SDIO_RX_CHECKPOINT_NUM) {
+				BTMTK_WARN("set RX_CHECK_ON failed. type=%d", type);
+				goto exit;
+			}
+			timestamp_threshold[type] = 0;
+			for (i = strlen("rx check on 0="); (i_fwlog_buf[i] >= '0' && i_fwlog_buf[i] <= '9'); i++) {
+				timestamp_threshold[type] = timestamp_threshold[type] * 10;
+				timestamp_threshold[type] += i_fwlog_buf[i] - '0';
+			}
+		} else {
+			type = 0;
+			timestamp_threshold[BTMTK_SDIO_RX_CHECKPOINT_INTR] = 50000;
+		}
+		BTMTK_WARN("set RX_CHECK_ON %d=%d", type, timestamp_threshold[type]);
+		goto exit;
+	}
+
+	if (strstr(i_fwlog_buf, RX_CHECK_OFF)) {
+		BTMTK_WARN("set RX_CHECK_OFF");
+		memset(timestamp_threshold, 0, sizeof(timestamp_threshold));
+		goto exit;
+	}
+
+	if (strstr(i_fwlog_buf, RELOAD_SETTING)) {
+		BTMTK_INFO("set RELOAD_SETTING");
+		btmtk_eeprom_bin_file(g_card);
+		len = count;
+		goto exit;
+	}
+
+	/* For log_lvl, EX: echo log_lvl=4 > /dev/stpbtfwlog */
+	if (strcmp(i_fwlog_buf, "log_lvl=") > 0) {
+		val = *(i_fwlog_buf + strlen("log_lvl=")) - 48;
+
+		if (val > BTMTK_LOG_LEVEL_MAX || val <= 0) {
+			BTMTK_ERR("Got incorrect value for log level(%d)", val);
+			count = -EINVAL;
+			goto exit;
+		}
+		btmtk_log_lvl = val;
+		BTMTK_INFO("btmtk_log_lvl = %d", btmtk_log_lvl);
+		goto exit;
+	}
+
+	/* For btmtk_bluetooth_kpi, EX: echo bperf=1 > /dev/stpbtfwlog */
+	if (strcmp(i_fwlog_buf, "bperf=") >= 0) {
+		u8 val = *(i_fwlog_buf + strlen("bperf=")) - 48;
+
+		btmtk_bluetooth_kpi = val;
+		BTMTK_INFO("set bluetooth KPI feature(bperf) to %d", btmtk_bluetooth_kpi);
+		goto exit;
+	}
+
+	/* hci input command format : echo 01 be fc 01 05 > /dev/stpbtfwlog */
+	/* We take the data from index three to end. */
+	for (i = 0; i < count; i++) {
+		char *pos = i_fwlog_buf + i;
+		char temp_str[3] = {'\0'};
+		long res = 0;
+
+		if (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n') {
+			continue;
+		} else if (*pos == '0' && (*(pos + 1) == 'x' || *(pos + 1) == 'X')) {
+			i++;
+			continue;
+		} else if (!(*pos >= '0' && *pos <= '9') && !(*pos >= 'A' && *pos <= 'F')
+			&& !(*pos >= 'a' && *pos <= 'f')) {
+			BTMTK_ERR("There is an invalid input(%c)", *pos);
+			count = -EINVAL;
+			goto exit;
+		} else if (len + 1 >= HCI_MAX_COMMAND_SIZE) {
+			BTMTK_ERR("%s: input data exceed maximum command length (%d)", __func__, len);
+			count = -EINVAL;
+			goto exit;
+		}
+		temp_str[0] = *pos;
+		temp_str[1] = *(pos + 1);
+		i++;
+		ret = kstrtol(temp_str, 16, &res);
+		if (ret == 0) {
+			o_fwlog_buf[len++] = (u8)res;
+			if (len >= (HCI_MAX_COMMAND_SIZE - 1) && ((i + 1) < len)) {
+				BTMTK_WARN("buf is full, input format may error");
+				goto exit;
+			}
+		} else
+			BTMTK_ERR("Convert %s failed(%d)", temp_str, ret);
+	}
+
+	/*
+	 * Receive command from stpbtfwlog, then Sent hci command
+	 * to controller
+	 */
+	BTMTK_DBG("hci buff is %02x%02x%02x%02x%02x, length %d",
+		o_fwlog_buf[0], o_fwlog_buf[1],
+		o_fwlog_buf[2], o_fwlog_buf[3], o_fwlog_buf[4], len);
+
+	/* check HCI command length */
+	if (len > HCI_MAX_COMMAND_SIZE) {
+		BTMTK_ERR("your command is larger than maximum length, length = %d", len);
+		goto exit;
+	}
+
+	if (len <= 1) {
+		BTMTK_ERR("length = %d, command format may error", len);
+		goto exit;
+	}
+
+	BTMTK_DBG("hci buff is %02x%02x%02x%02x%02x",
+		o_fwlog_buf[0], o_fwlog_buf[1],
+		o_fwlog_buf[2], o_fwlog_buf[3], o_fwlog_buf[4]);
+
+	switch (o_fwlog_buf[0]) {
+	case MTK_HCI_READ_CR_PKT:
+		if (len == MTK_HCI_READ_CR_PKT_LENGTH) {
+			crAddr = (o_fwlog_buf[1] << 24) + (o_fwlog_buf[2] << 16) +
+			(o_fwlog_buf[3] << 8) + (o_fwlog_buf[4]);
+			btmtk_sdio_readl(crAddr, &crValue);
+			BTMTK_INFO("read crAddr=0x%08x crValue=0x%08x", crAddr, crValue);
+		} else
+			BTMTK_INFO("read length=%d is incorrect, should be %d",
+				len, MTK_HCI_READ_CR_PKT_LENGTH);
+		break;
+
+	case MTK_HCI_WRITE_CR_PKT:
+		if (len == MTK_HCI_WRITE_CR_PKT_LENGTH) {
+			crAddr = (o_fwlog_buf[1] << 24) + (o_fwlog_buf[2] << 16) +
+			(o_fwlog_buf[3] << 8) + (o_fwlog_buf[4]);
+			crValue = (o_fwlog_buf[5] << 24) + (o_fwlog_buf[6] << 16) +
+			(o_fwlog_buf[7] << 8) + (o_fwlog_buf[8]);
+			BTMTK_INFO("write crAddr=0x%08x crValue=0x%08x",
+				crAddr, crValue);
+			btmtk_sdio_writel(crAddr, crValue);
+		} else
+			BTMTK_INFO("write length=%d is incorrect, should be %d",
+				len, MTK_HCI_WRITE_CR_PKT_LENGTH);
+		break;
+
+	default:
+		/*
+		 * Receive command from stpbtfwlog, then Sent hci command
+		 * to Stack
+		 */
+		skb = bt_skb_alloc(len - 1, GFP_ATOMIC);
+		if (skb == NULL) {
+			BTMTK_WARN("skb is null");
+			count = -ENOMEM;
+			goto exit;
+		}
+
+		// 0xFC26 is get link & profile information command.
+		if (*(uint16_t *)(o_fwlog_buf + 1) != 0xFC26) {
+			btmtk_sdio_discard_opcode(&g_card->discard_opcode_queue, o_fwlog_buf + 1);
+			BTMTK_INFO("opcode is %02x,%02x", o_fwlog_buf[1], o_fwlog_buf[2]);
+		}
+
+		bt_cb(skb)->pkt_type = o_fwlog_buf[0];
+		memcpy(&skb->data[0], &o_fwlog_buf[1], len - 1);
+		skb->len = len - 1;
+		skb_queue_tail(&g_card->tx_queue, skb);
+		wake_up_interruptible(&g_priv->main_thread.wait_q);
+		break;
+	}
+
+	BTMTK_INFO("write end");
+exit:
+	BTMTK_INFO("exit, length = %d", len);
+	kfree(i_fwlog_buf);
+	kfree(o_fwlog_buf);
+	if (g_priv)
+		up(&g_priv->wr_fwlog_mtx);
+	return count;
+#endif
+}
+
+static unsigned int btmtk_fops_pollfwlog(
+			struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	if (!probe_ready) {
+		BTMTK_WARN_LIMITTED("probe_ready is %d return", probe_ready);
+		return 0;
+	}
+
+	if (g_priv == NULL) {
+		BTMTK_ERR("g_priv is NULL");
+		return 0;
+	}
+
+	poll_wait(file, &fw_log_inq, wait);
+
+	if (!skb_queue_empty(&g_card->fwlog_fops_queue)) {
+		if (skb_queue_len(&g_card->fwlog_fops_queue))
+			mask |= (POLLIN | POLLRDNORM);
+		/* BTMTK_INFO("poll done"); */
+	}
+
+	mask |= (POLLOUT | POLLWRNORM);
+
+	/* BTMTK_INFO("poll mask 0x%x",mask); */
+	return mask;
+}
+
+static long btmtk_fops_unlocked_ioctlfwlog(
+			struct file *filp, unsigned int cmd,
+			unsigned long arg)
+{
+	int retval = 0;
+
+	BTMTK_INFO("->");
+	if (g_priv == NULL) {
+		BTMTK_ERR("ERROR, g_priv is NULL!");
+		return -ENODEV;
+	}
+
+	return retval;
+}
+
+/*============================================================================*/
+/* Interface Functions : Proc */
+/*============================================================================*/
+#define __________________________________________Interface_Function_for_Proc
+static int btmtk_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "patch version:%s  driver version:%s", fw_version_str, VERSION);
+	return 0;
+}
+
+static int btmtk_proc_open(struct inode *inode, struct  file *file)
+{
+	return single_open(file, btmtk_proc_show, NULL);
+}
+
+void btmtk_proc_create_new_entry(void)
+{
+	struct proc_dir_entry *proc_show_entry;
+
+	BTMTK_INFO("proc initialized");
+
+	g_proc_dir = proc_mkdir("stpbt", 0);
+	if (g_proc_dir == 0) {
+		BTMTK_ERR("Unable to creat dir");
+		return;
+	}
+	proc_show_entry =  proc_create("bt_fw_version", 0640, g_proc_dir, &BT_proc_fops);
+}
+
+static int BTMTK_major;
+static int BT_majorfwlog;
+static struct cdev BTMTK_cdev;
+static struct cdev BT_cdevfwlog;
+static int BTMTK_devs = 1;
+
+static wait_queue_head_t inq;
+const struct file_operations BTMTK_fops = {
+	.open = btmtk_fops_open,
+	.release = btmtk_fops_close,
+	.read = btmtk_fops_read,
+	.write = btmtk_fops_write,
+	.poll = btmtk_fops_poll,
+	.unlocked_ioctl = btmtk_fops_unlocked_ioctl,
+	.fasync = btmtk_fops_fasync
+};
+
+const struct file_operations BT_fopsfwlog = {
+	.open = btmtk_fops_openfwlog,
+	.release = btmtk_fops_closefwlog,
+	.read = btmtk_fops_readfwlog,
+	.write = btmtk_fops_writefwlog,
+	.poll = btmtk_fops_pollfwlog,
+	.unlocked_ioctl = btmtk_fops_unlocked_ioctlfwlog
+
+};
+
+static int BTMTK_init(void)
+{
+	dev_t devID = MKDEV(BTMTK_major, 0);
+	dev_t devIDfwlog = MKDEV(BT_majorfwlog, 1);
+	int ret = 0;
+	int cdevErr = 0;
+	int major = 0;
+	int majorfwlog = 0;
+
+	BTMTK_INFO("begin");
+
+	g_card = NULL;
+	txbuf = NULL;
+	rxbuf = NULL;
+	userbuf = NULL;
+	rx_length = 0;
+	fw_dump_file = NULL;
+	g_priv = NULL;
+	probe_counter = 0;
+
+	btmtk_proc_create_new_entry();
+
+	ret = alloc_chrdev_region(&devID, 0, 1, "BT_chrdev");
+	if (ret) {
+		BTMTK_ERR("fail to allocate chrdev");
+		return ret;
+	}
+
+	ret = alloc_chrdev_region(&devIDfwlog, 0, 1, "BT_chrdevfwlog");
+	if (ret) {
+		BTMTK_ERR("fail to allocate chrdev");
+		return ret;
+	}
+
+	BTMTK_major = major = MAJOR(devID);
+	BTMTK_INFO("major number:%d", BTMTK_major);
+	BT_majorfwlog = majorfwlog = MAJOR(devIDfwlog);
+	BTMTK_INFO("BT_majorfwlog number: %d", BT_majorfwlog);
+
+	cdev_init(&BTMTK_cdev, &BTMTK_fops);
+	BTMTK_cdev.owner = THIS_MODULE;
+
+	cdev_init(&BT_cdevfwlog, &BT_fopsfwlog);
+	BT_cdevfwlog.owner = THIS_MODULE;
+
+	cdevErr = cdev_add(&BTMTK_cdev, devID, BTMTK_devs);
+	if (cdevErr)
+		goto error;
+
+	cdevErr = cdev_add(&BT_cdevfwlog, devIDfwlog, 1);
+	if (cdevErr)
+		goto error;
+
+	BTMTK_INFO("%s driver(major %d) installed.",
+			"BT_chrdev", BTMTK_major);
+	BTMTK_INFO("%s driver(major %d) installed.",
+			"BT_chrdevfwlog", BT_majorfwlog);
+
+	pBTClass = class_create(THIS_MODULE, "BT_chrdev");
+	if (IS_ERR(pBTClass)) {
+		BTMTK_ERR("class create fail, error code(%ld)",
+			PTR_ERR(pBTClass));
+		goto error;
+	}
+
+	pBTDev = device_create(pBTClass, NULL, devID, NULL, "%s", BT_NODE);
+	if (IS_ERR(pBTDev)) {
+		BTMTK_ERR("device create fail, error code(%ld)",
+			PTR_ERR(pBTDev));
+		goto err2;
+	}
+
+	pBTDevfwlog = device_create(pBTClass, NULL,
+				devIDfwlog, NULL, "%s", "stpbtfwlog");
+	if (IS_ERR(pBTDevfwlog)) {
+		BTMTK_ERR("device(stpbtfwlog) create fail, error code(%ld)",
+			PTR_ERR(pBTDevfwlog));
+		goto err2;
+	}
+
+	BTMTK_INFO("BT_major %d, BT_majorfwlog %d", BTMTK_major, BT_majorfwlog);
+	BTMTK_INFO("devID %d, devIDfwlog %d", devID, devIDfwlog);
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_INIT);
+	FOPS_MUTEX_UNLOCK();
+
+	/* init wait queue */
+	g_devIDfwlog = devIDfwlog;
+	init_waitqueue_head(&(fw_log_inq));
+	init_waitqueue_head(&(inq));
+
+	return 0;
+
+ err2:
+	if (pBTClass) {
+		class_destroy(pBTClass);
+		pBTClass = NULL;
+	}
+
+ error:
+	if (cdevErr == 0)
+		cdev_del(&BTMTK_cdev);
+
+	if (ret == 0)
+		unregister_chrdev_region(devID, BTMTK_devs);
+
+	return -1;
+}
+
+static void BTMTK_exit(void)
+{
+	dev_t dev = MKDEV(BTMTK_major, 0);
+	dev_t devIDfwlog = g_devIDfwlog;
+
+	BTMTK_INFO("begin");
+
+	if (g_proc_dir != NULL) {
+		remove_proc_entry("bt_fw_version", g_proc_dir);
+		remove_proc_entry("stpbt", NULL);
+		g_proc_dir = NULL;
+		BTMTK_INFO("proc device node and folder removed!!");
+	}
+
+	if (pBTDevfwlog) {
+		BTMTK_INFO("6");
+		device_destroy(pBTClass, devIDfwlog);
+		pBTDevfwlog = NULL;
+	}
+
+	if (pBTDev) {
+		device_destroy(pBTClass, dev);
+		pBTDev = NULL;
+	}
+
+	if (pBTClass) {
+		class_destroy(pBTClass);
+		pBTClass = NULL;
+	}
+	cdev_del(&BTMTK_cdev);
+	unregister_chrdev_region(dev, 1);
+
+	cdev_del(&BT_cdevfwlog);
+	unregister_chrdev_region(devIDfwlog, 1);
+	BTMTK_INFO("%s driver removed.", BT_DRIVER_NAME);
+}
+
+static int btmtk_sdio_allocate_memory(void)
+{
+	txbuf = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
+	memset(txbuf, 0, MTK_TXDATA_SIZE);
+
+	rxbuf = kmalloc(MTK_RXDATA_SIZE, GFP_ATOMIC);
+	memset(rxbuf, 0, MTK_RXDATA_SIZE);
+
+	userbuf = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
+	memset(userbuf, 0, MTK_TXDATA_SIZE);
+
+	userbuf_fwlog = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
+	memset(userbuf_fwlog, 0, MTK_TXDATA_SIZE);
+
+	g_card = kzalloc(sizeof(*g_card), GFP_KERNEL);
+	memset(g_card, 0, sizeof(*g_card));
+
+	g_priv = kzalloc(sizeof(*g_priv), GFP_KERNEL);
+	memset(g_priv, 0, sizeof(*g_priv));
+
+	g_priv->adapter = kzalloc(sizeof(*g_priv->adapter), GFP_KERNEL);
+	memset(g_priv->adapter, 0, sizeof(*g_priv->adapter));
+
+	g_card->priv = g_priv;
+
+	skb_queue_head_init(&g_card->tx_queue);
+	skb_queue_head_init(&g_card->fops_queue);
+	skb_queue_head_init(&g_card->fwlog_fops_queue);
+	skb_queue_head_init(&g_card->discard_opcode_queue);// discard opcode
+
+	return 0;
+}
+
+static int btmtk_sdio_free_memory(void)
+{
+	skb_queue_purge(&g_card->tx_queue);
+	skb_queue_purge(&g_card->fops_queue);
+	skb_queue_purge(&g_card->fwlog_fops_queue);
+	skb_queue_purge(&g_card->discard_opcode_queue);
+
+	kfree(txbuf);
+	txbuf = NULL;
+
+	kfree(rxbuf);
+	rxbuf = NULL;
+
+	kfree(userbuf);
+	userbuf = NULL;
+
+	kfree(userbuf_fwlog);
+	userbuf_fwlog = NULL;
+
+	kfree(g_card->priv->adapter);
+	g_card->priv->adapter = NULL;
+
+	kfree(g_card->priv);
+	g_card->priv = NULL;
+	g_priv = NULL;
+
+	kfree(g_card);
+	g_card = NULL;
+
+	return 0;
+}
+
+static int __init btmtk_sdio_init_module(void)
+{
+	int ret = 0;
+
+#if CFG_SUPPORT_CHIP_RESET_KO
+	/* Register reset function to reset ko */
+	resetko_register_module(RESET_MODULE_TYPE_BT,
+				"bt",
+				TRIGGER_RESET_TYPE_GPIO_API,
+				btmtk_resetko_reset,
+				btmtk_resetko_notify);
+#endif
+
+	ret = BTMTK_init();
+	if (ret) {
+		BTMTK_ERR("BTMTK_init failed!");
+		return ret;
+	}
+
+	if (btmtk_sdio_allocate_memory() < 0) {
+		BTMTK_ERR("allocate memory failed!");
+		return -ENOMEM;
+	}
+
+	if (sdio_register_driver(&bt_mtk_sdio) != 0) {
+		BTMTK_ERR("SDIO Driver Registration Failed");
+		return -ENODEV;
+	}
+
+	BTMTK_INFO("SDIO Driver Registration Success");
+
+	/* Clear the flag in case user removes the card. */
+	user_rmmod = 0;
+
+	return 0;
+}
+
+static void __exit btmtk_sdio_exit_module(void)
+{
+	/* Set the flag as user is removing this module. */
+	user_rmmod = 1;
+#if CFG_SUPPORT_CHIP_RESET_KO
+	/* notify reset ko module BT rmmod */
+	resetko_unregister_module(RESET_MODULE_TYPE_BT);
+#endif
+	BTMTK_exit();
+	sdio_unregister_driver(&bt_mtk_sdio);
+	btmtk_sdio_free_memory();
+}
+
+module_init(btmtk_sdio_init_module);
+module_exit(btmtk_sdio_exit_module);
diff --git a/btmtk_sdio.h b/btmtk_sdio.h
new file mode 100644
index 0000000..3d33401
--- /dev/null
+++ b/btmtk_sdio.h
@@ -0,0 +1,526 @@
+/*
+ *  Copyright (c) 2016 MediaTek Inc.
+ *
+ *  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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef _BTMTK_SDIO_H_
+#define _BTMTK_SDIO_H_
+#include "btmtk_config.h"
+#include <linux/pm_wakeup.h>
+
+#define VERSION "v0.0.1.13_2024070201"
+
+
+#if CFG_SUPPORT_CHIP_RESET_KO
+#include "reset.h"
+#include "reset_fsm.h"
+#include "reset_fsm_def.h"
+
+void btmtk_resetko_reset(void);
+void btmtk_resetko_notify(unsigned int event, void *data);
+extern enum ReturnStatus resetko_register_module(enum ModuleType module,
+				char *name,
+				enum TriggerResetApiType resetApiType,
+				void *resetFunc,
+				void *notifyFunc);
+extern enum ReturnStatus resetko_unregister_module(enum ModuleType module);
+#endif
+
+#define SDIO_HEADER_LEN				4
+#define STP_HEADER_LEN				4
+#define COREDUMP_HEADER_LEN			5
+#define HCI_TYPE_LEN				1
+#define HCI_EVENT_CODE_LEN			1
+#define COREDUMP_PACKET_HEADER_LEN		13
+
+#define BD_ADDRESS_SIZE 6
+
+#define SET_POWER_NUM 3
+
+#define DUMP_HCI_LOG_FILE_NAME          "/sys/hcilog"
+/* 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                 256
+
+#define SDIO_PATCH_DOWNLOAD_FIRST    1/*first packet*/
+#define SDIO_PATCH_DOWNLOAD_CON        2/*continue*/
+#define SDIO_PATCH_DOWNLOAD_END        3/*end*/
+
+/* Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK        2
+
+/* This is for firmware specific length */
+#define FW_EXTRA_LEN                    36
+
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+					(HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
+
+#define ALLOC_BUF_SIZE  (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+				MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+				+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
+				* SDIO_BLOCK_SIZE)
+
+/* The number of times to try when polling for status */
+#define MAX_POLL_TRIES                  100
+
+/* Max retry number of CMD53 write */
+#define MAX_WRITE_IOMEM_RETRY           2
+
+/* register bitmasks */
+#define HOST_POWER_UP                           BIT(1)
+#define HOST_CMD53_FIN                          BIT(2)
+
+#define HIM_DISABLE                             0xff
+#define HIM_ENABLE                              (BIT(0) | BIT(1))
+
+#define UP_LD_HOST_INT_STATUS                   BIT(0)
+#define DN_LD_HOST_INT_STATUS                   BIT(1)
+
+#define DN_LD_CARD_RDY                          BIT(0)
+#define CARD_IO_READY                           BIT(3)
+
+#define FIRMWARE_READY                          0xfedc
+
+enum {
+	BTMTK_FOPS_STATE_UNKNOWN,	/* deinit in stpbt destroy */
+	BTMTK_FOPS_STATE_INIT,		/* init in stpbt created */
+	BTMTK_FOPS_STATE_OPENING,	/* during opening */
+	BTMTK_FOPS_STATE_OPENED,	/* opened */
+	BTMTK_FOPS_STATE_CLOSING,	/* during closing */
+	BTMTK_FOPS_STATE_CLOSED,	/* closed */
+	BTMTK_FOPS_STATE_MAX
+};
+
+struct btmtk_sdio_card_reg {
+	u8 cfg;
+	u8 host_int_mask;
+	u8 host_intstatus;
+	u8 card_status;
+	u8 sq_read_base_addr_a0;
+	u8 sq_read_base_addr_a1;
+	u8 card_revision;
+	u8 card_fw_status0;
+	u8 card_fw_status1;
+	u8 card_rx_len;
+	u8 card_rx_unit;
+	u8 io_port_0;
+	u8 io_port_1;
+	u8 io_port_2;
+	bool int_read_to_clear;
+	u8 host_int_rsr;
+	u8 card_misc_cfg;
+	u8 fw_dump_ctrl;
+	u8 fw_dump_start;
+	u8 fw_dump_end;
+	u8 func_num;
+	u32 chip_id;
+};
+#if SUPPORT_MT7668
+#define WOBLE_SETTING_FILE_NAME_7668 "woble_setting_7668.bin"
+#endif
+
+#if SUPPORT_MT7663
+#define WOBLE_SETTING_FILE_NAME_7663 "woble_setting_7663.bin"
+#endif
+
+#define BT_MACADDR_FILE    "mac_bt"
+
+/*BT Power Config File*/
+#define POWER_SETTING_FILE "BtPwrLimit_MT76x3.dat"
+#define BT_POWER_MODE      "PowerMode"
+#define BT_POWER_LEVEL     "BTLevel"
+#define BLE_POWER_LEVEL    "BLELevel"
+#define SUB_POWER_LEVEL    "SubLevel"
+
+/* Backward compatibility */
+#define WOBLE_SETTING_FILE_NAME "woble_setting.bin"
+#define BT_CFG_NAME "bt.cfg"
+#define BT_CFG_NAME_PREFIX "bt"
+#define BT_CFG_NAME_SUFFIX "cfg"
+#define BT_UNIFY_WOBLE "SUPPORT_UNIFY_WOBLE"
+#define BT_UNIFY_WOBLE_TYPE "UNIFY_WOBLE_TYPE"
+#define BT_LEGACY_WOBLE "SUPPORT_LEGACY_WOBLE"
+#define BT_WOBLE_BY_EINT "SUPPORT_WOBLE_BY_EINT"
+#define BT_DONGLE_RESET_PIN "BT_DONGLE_RESET_GPIO_PIN"
+#define BT_SAVE_FW_DUMP_IN_KERNEL "SAVE_FW_DUMP_IN_KERNEL"
+#define BT_SYS_LOG_FILE "SYS_LOG_FILE_NAME"
+#define BT_FW_DUMP_FILE "FW_DUMP_FILE_NAME"
+#define BT_RESET_DONGLE "SUPPORT_DONGLE_RESET"
+#define BT_FULL_FW_DUMP "SUPPORT_FULL_FW_DUMP"
+#define BT_WOBLE_WAKELOCK "SUPPORT_WOBLE_WAKELOCK"
+#define BT_WOBLE_FOR_BT_DISABLE "SUPPORT_WOBLE_FOR_BT_DISABLE"
+#define BT_RESET_STACK_AFTER_WOBLE "RESET_STACK_AFTER_WOBLE"
+#define BT_AUTO_PICUS "SUPPORT_AUTO_PICUS"
+#define BT_AUTO_PICUS_FILTER "PICUS_FILTER_CMD"
+#define BT_WMT_CMD "WMT_CMD"
+#define BT_VENDOR_CMD "VENDOR_CMD"
+
+#define WMT_CMD_COUNT 255
+#define VENDOR_CMD_COUNT 255
+
+#define WOBLE_SETTING_COUNT 10
+
+#define WOBLE_FAIL -10
+
+enum bt_sdio_dongle_state {
+	BT_SDIO_DONGLE_STATE_UNKNOWN,
+	BT_SDIO_DONGLE_STATE_POWER_ON,
+	BT_SDIO_DONGLE_STATE_POWER_ON_FOR_WOBLE,
+	BT_SDIO_DONGLE_STATE_POWER_OFF,
+	BT_SDIO_DONGLE_STATE_WOBLE,
+	BT_SDIO_DONGLE_STATE_FW_DUMP,
+	BT_SDIO_DONGLE_STATE_ERROR
+};
+
+enum fw_cfg_index_len {
+	FW_CFG_INX_LEN_NONE = 0,
+	FW_CFG_INX_LEN_2 = 2,
+	FW_CFG_INX_LEN_3 = 3,
+};
+
+struct fw_cfg_struct {
+	char	*content;	/* APCF conecnt or radio off content */
+	int	length;		/* APCF conecnt or radio off content of length */
+};
+
+struct power_cfg_struct
+{
+	unsigned int powerCfg;
+	unsigned int PowerMode;
+	unsigned int BTLevel;
+	unsigned int BLELevel;
+	unsigned int SubLevel;
+};
+
+struct bt_cfg_struct {
+	bool	support_unify_woble;	/* support unify woble or not */
+	bool	support_legacy_woble;		/* support legacy woble or not */
+	bool	support_woble_by_eint;		/* support woble by eint or not */
+	bool	save_fw_dump_in_kernel;		/* save fw dump in kernel or not */
+	bool	support_dongle_reset;		/* support chip reset or not */
+	bool	support_full_fw_dump;		/* dump full fw coredump or not */
+	bool	support_woble_wakelock;		/* support when woble error, do wakelock or not */
+	bool	support_woble_for_bt_disable;		/* when bt disable, support enter susend or not */
+	bool	reset_stack_after_woble;	/* support reset stack to re-connect IOT after resume */
+	bool	support_auto_picus;			/* support enable PICUS automatically */
+	struct	fw_cfg_struct picus_filter;	/* support on PICUS filter command customization */
+	int	dongle_reset_gpio_pin;		/* BT_DONGLE_RESET_GPIO_PIN number */
+	unsigned int unify_woble_type;		/* 0: legacy. 1: waveform. 2: IR */
+	char	*sys_log_file_name;
+	char	*fw_dump_file_name;
+	struct	fw_cfg_struct wmt_cmd[WMT_CMD_COUNT];
+	struct	fw_cfg_struct vendor_cmd[VENDOR_CMD_COUNT];
+};
+
+struct btmtk_sdio_card {
+	struct sdio_func *func;
+	u32 ioport;
+	const char *helper;
+	const struct btmtk_sdio_card_reg *reg;
+	bool support_pscan_win_report;
+	bool supports_fw_dump;
+	u16 sd_blksz_fw_dl;
+	u8 rx_unit;
+	bool is_KeepFullPwr;
+	struct btmtk_private *priv;
+
+	unsigned char		*woble_setting_file_name;
+
+	unsigned int		chip_id;
+	struct fw_cfg_struct		woble_setting_apcf[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_apcf_fill_mac[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_apcf_fill_mac_location[WOBLE_SETTING_COUNT];
+
+	struct fw_cfg_struct		woble_setting_radio_off[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_wakeup_type[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_radio_off_status_event[WOBLE_SETTING_COUNT];
+	/* complete event */
+	struct fw_cfg_struct		woble_setting_radio_off_comp_event[WOBLE_SETTING_COUNT];
+
+	struct fw_cfg_struct		woble_setting_radio_on[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_radio_on_status_event[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_radio_on_comp_event[WOBLE_SETTING_COUNT];
+
+	int		suspend_count;
+	/* set apcf after resume(radio on) */
+	struct fw_cfg_struct		woble_setting_apcf_resume[WOBLE_SETTING_COUNT];
+	struct fw_cfg_struct		woble_setting_apcf_resume_event[WOBLE_SETTING_COUNT];
+	unsigned char					bdaddr[BD_ADDRESS_SIZE];
+	unsigned int					woble_need_trigger_coredump;
+	unsigned char		*bt_cfg_file_name;
+	unsigned char		*setting_file;
+	struct bt_cfg_struct		bt_cfg;
+	struct		wakeup_source	*woble_ws;
+	struct		wakeup_source	*eint_ws;
+#if LOWER_POWER_SINK
+	struct		wakeup_source	*lp_ws;
+#endif
+	/* WoBLE */
+	unsigned int wobt_irq;
+	int wobt_irqlevel;
+	atomic_t irq_enable_count;
+	struct input_dev *WoBLEInputDev;
+
+	int pa_setting;
+	int duplex_setting;
+	u8 *bin_file_buffer;
+	size_t bin_file_size;
+	u8 efuse_mode;
+
+	bool   						mac_bt_flag;
+	unsigned char				mac_btAddr[BD_ADDRESS_SIZE];
+	struct power_cfg_struct 	power_cfg;
+
+	struct sk_buff_head discard_opcode_queue;
+
+	struct	sk_buff_head tx_queue;
+	struct	sk_buff_head fops_queue;
+	struct	sk_buff_head fwlog_fops_queue;
+
+	enum	bt_sdio_dongle_state dongle_state;
+};
+
+struct btmtk_sdio_device {
+	const char *helper;
+	const struct btmtk_sdio_card_reg *reg;
+	const bool support_pscan_win_report;
+	u16 sd_blksz_fw_dl;
+	bool supports_fw_dump;
+};
+#pragma pack(1)
+struct _PATCH_HEADER {
+	u8 ucDateTime[16];
+	u8 ucPlatform[4];
+	u16 u2HwVer;
+	u16 u2SwVer;
+	u32 u4PatchVer;
+	u16 u2PatchStartAddr;/*Patch ram start address*/
+};
+#pragma pack()
+
+struct bt_stereo_clk {
+	u64 sys_clk;
+	u64 fw_clk;
+};
+
+struct bt_stereo_para {
+	u16 handle;
+	u8 method;
+	u32 period;
+	u16 active_slots;
+};
+
+#define HW_VERSION 0x80000000
+#define FW_VERSION 0x80000004
+#define CHIP_ID 0x80000008
+
+/*common register address*/
+#define CCIR 0x0000
+#define CHLPCR 0x0004
+#define CSDIOCSR 0x0008
+#define CHCR   0x000C
+#define CHISR  0x0010
+#define CHIER  0x0014
+#define CTDR   0x0018
+#define CRDR   0x001C
+#define CTFSR 0x0020
+#define CRPLR 0x0024
+#define SWPCDBGR   0x0154
+/*CHLPCR*/
+#define C_FW_INT_EN_SET            0x00000001
+#define C_FW_INT_EN_CLEAR        0x00000002
+/*CHISR*/
+#define RX_PKT_LEN             0xFFFF0000
+#define FIRMWARE_INT             0x0000FE00
+#define FIRMWARE_INT_BIT15       0x00008000/*FW inform driver don't change to fw own for dore dump*/
+#define TX_FIFO_OVERFLOW         0x00000100
+#define FW_INT_IND_INDICATOR        0x00000080
+#define TX_COMPLETE_COUNT         0x00000070
+#define TX_UNDER_THOLD             0x00000008
+#define TX_EMPTY             0x00000004
+#define RX_DONE                 0x00000002
+#define FW_OWN_BACK_INT             0x00000001
+
+
+#define MTKSTP_HEADER_SIZE 0x0004
+
+#define MTK_SDIO_PACKET_HEADER_SIZE 4
+#define MTKDATA_HEADER_SIZE 10
+#define MTKWMT_HEADER_SIZE 4
+
+#define PATCH_DOWNLOAD_SIZE 1970
+
+#define DRIVER_OWN 0
+#define FW_OWN 1
+
+#define MTK_WMT_HEADER_LEN 4
+
+#define DEFAULE_PATCH_FRAG_SIZE    1000
+
+#define PATCH_IS_DOWNLOAD_BY_OTHER 0
+#define PATCH_READY 1
+#define PATCH_NEED_DOWNLOAD 2
+
+#define BTMTK_SDIO_RETRY_COUNT 500
+
+#define BTMTK_LOAD_WOBLE_RETRY_COUNT 1
+
+/**
+ * stpbtfwlog device node
+ */
+#define HCI_MAX_COMMAND_SIZE		255
+/* Write a char to buffer.
+ * ex : echo 01 be > /dev/stpbtfwlog
+ * "01 " need three bytes.
+ */
+#define HCI_MAX_COMMAND_BUF_SIZE	(HCI_MAX_COMMAND_SIZE * 3)
+
+/*
+ * data event:
+ * return
+ * 0:
+ * patch download is not complete/get patch semaphore fail
+ * 1:
+ * patch download is complete by others
+ * 2
+ * patch download is not coplete
+ * 3:(for debug)
+ * release patch semaphore success
+ */
+
+/* Platform specific DMA alignment */
+#define BTSDIO_DMA_ALIGN                8
+
+/* Macros for Data Alignment : size */
+#define ALIGN_SZ(p, a)  \
+		(((p) + ((a) - 1)) & ~((a) - 1))
+
+/* Macros for Data Alignment : address */
+#define ALIGN_ADDR(p, a)        \
+		((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
+		~(((unsigned long)(a)) - 1))
+struct sk_buff *btmtk_create_send_data(struct sk_buff *skb);
+int btmtk_print_buffer_conent(u8 *buf, u32 Datalen);
+u32 lock_unsleepable_lock(struct _OSAL_UNSLEEPABLE_LOCK_ *pUSL);
+u32 unlock_unsleepable_lock(struct _OSAL_UNSLEEPABLE_LOCK_ *pUSL);
+
+extern unsigned char probe_counter;
+extern unsigned char *txbuf;
+extern u8 probe_ready;
+
+enum {
+	BTMTK_WOBLE_STATE_UNKNOWN,
+	BTMTK_WOBLE_STATE_SUSPEND,
+	BTMTK_WOBLE_STATE_RESUME,
+	BTMTK_WOBLE_STATE_DUMPING,
+	BTMTK_WOBLE_STATE_DUMPEND,
+	BTMTK_WOBLE_STATE_NEEDRESET_STACK,
+};
+
+enum {
+	BTMTK_SDIO_EVENT_COMPARE_STATE_UNKNOWN,
+	BTMTK_SDIO_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE,
+	BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE,
+	BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS,
+};
+
+
+/**
+ * Maximum rom patch file name length
+ */
+#define MAX_BIN_FILE_NAME_LEN	32
+
+
+#define COMPARE_FAIL				-1
+#define COMPARE_SUCCESS				1
+#define COMP_EVENT_TIMO				2000
+#define WOBLE_COMP_EVENT_TIMO		5000
+#define WLAN_STATUS_IS_NOT_LOAD		-1
+#define WLAN_STATUS_DEFAULT		0
+#define WLAN_STATUS_CALL_REMOVE_START	1 /* WIFI driver is inserted */
+
+/**
+ * BTMTK ioctl
+ */
+#define BTMTK_IOCTL_MAGIC 'k'
+
+#define BTMTK_IOCTL_STEREO_GET_CLK _IOR(BTMTK_IOCTL_MAGIC, 1, void *)
+#define BTMTK_IOCTL_STEREO_SET_PARA _IOW(BTMTK_IOCTL_MAGIC, 2, void *)
+
+/**
+ * Inline functions
+ */
+static inline int is_support_unify_woble(struct btmtk_sdio_card *data)
+{
+	if (data->bt_cfg.support_unify_woble) {
+		if (((data->chip_id & 0xffff) == 0x7668) ||
+				((data->chip_id & 0xffff) == 0x7663))
+			return 1;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+}
+
+static inline int is_mt7668(struct btmtk_sdio_card *data)
+{
+#if SUPPORT_MT7668
+	return ((data->chip_id & 0xffff) == 0x7668);
+#else
+	return 0;
+#endif
+}
+
+static inline int is_mt7663(struct btmtk_sdio_card *data)
+{
+#if SUPPORT_MT7663
+	return ((data->chip_id & 0xffff) == 0x7663);
+#else
+	return 0;
+#endif
+}
+
+#define FW_OWN_OFF "fw own off"
+#define FW_OWN_ON  "fw own on"
+
+#define WOBLE_OFF "woble off"
+#define WOBLE_ON  "woble on"
+
+#define RX_CHECK_OFF "rx check off"
+#define RX_CHECK_ON "rx check on"
+
+#define RELOAD_SETTING "reload_setting"
+
+enum BTMTK_SDIO_RX_CHECKPOINT {
+	BTMTK_SDIO_RX_CHECKPOINT_INTR,
+	BTMTK_SDIO_RX_CHECKPOINT_RX_START,
+	BTMTK_SDIO_RX_CHECKPOINT_RX_DONE,
+	BTMTK_SDIO_RX_CHECKPOINT_ENABLE_INTR,
+
+	BTMTK_SDIO_RX_CHECKPOINT_NUM
+};
+
+#define BTMTK_SDIO_TIMESTAMP_NUM 50
+
+int btmtk_sdio_reset_dongle(void);
+
+/* WOBX attribute type */
+#define WOBX_TRIGGER_INFO_ADDR_TYPE         1
+#define WOBX_TRIGGER_INFO_ADV_DATA_TYPE     2
+#define WOBX_TRIGGER_INFO_TRACE_LOG_TYPE    3
+#define WOBX_TRIGGER_INFO_SCAN_LOG_TYPE     4
+#define WOBX_TRIGGER_INFO_TRIGGER_CNT_TYPE  5
+
+#endif
+
diff --git a/init.btmtksdio.rc b/init.btmtksdio.rc
new file mode 100644
index 0000000..4564258
--- /dev/null
+++ b/init.btmtksdio.rc
@@ -0,0 +1,11 @@
+service insmod_sh_btmtksdio /vendor/bin/init.insmod.sh /vendor_dlkm/etc/init.insmod.btmtksdio.cfg
+    class main
+    user root
+    group root system
+    disabled
+    oneshot
+
+# load btmtksdio
+on boot
+  write /sys/module/firmware_class/parameters/path /vendor/firmware,/mnt/product/factory/configs
+  exec_start insmod_sh_btmtksdio
diff --git a/init.insmod.btmtksdio.cfg b/init.insmod.btmtksdio.cfg
new file mode 100644
index 0000000..9dc59f5
--- /dev/null
+++ b/init.insmod.btmtksdio.cfg
@@ -0,0 +1,8 @@
+###############################################
+#           init.insmod.btmtksdio.cfg         #
+# This file contains btmtksdio kernel modules #
+# to load at init time by init.insmod.sh      #
+# script                                      #
+###############################################
+
+modprobe|mtreset_mt7663.ko btmtksdio.ko