diff --git a/drivers/scsi/bht/linux_os/linux_api.c b/drivers/scsi/bht/linux_os/linux_api.c
new file mode 100644
index 000000000000..be7b44b641a3
--- /dev/null
+++ b/drivers/scsi/bht/linux_os/linux_api.c
@@ -0,0 +1,2207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 BHT Inc.
+ *
+ * File Name: linux_api.c
+ *
+ * Abstract: Linux api definition
+ *
+ * Version: 1.00
+ *
+ * Author: Peter
+ *
+ * Environment:	Linux
+ *
+ * History:
+ *
+ * 5/20/2015		Creation	Peter.Guo
+ */
+
+#include <linux/version.h>
+#include "../include/basic.h"
+#include "../include/debug.h"
+#include "../include/util.h"
+#include "../include/funcapi.h"
+#include "../include/hostapi.h"
+#include "../include/hostvenapi.h"
+#include <linux/moduleparam.h>
+#include <linux/kthread.h>
+#include <linux/random.h>
+#include <scsi/scsi_device.h>
+#include "../linux_os/linux_scsi.h"
+#if BHT_LINUX_ENABLE_RTD3
+#include <linux/pm_runtime.h>
+#endif
+
+/* ----------linux Module param management---------- */
+
+/* This Flag is used to control emmc */
+static ulong m_emmc_mode;
+module_param(m_emmc_mode, ulong, 0444);
+
+/* Use to select dma mode, adma2 adma3 */
+static ulong m_dma_mode;
+module_param(m_dma_mode, ulong, 0444);
+
+/* use to enable or disable infinite transfer */
+static ulong m_infinite_ctrl = 0x8000000f;
+module_param(m_infinite_ctrl, ulong, 0444);
+
+/* use for tag queue capability */
+static ulong m_tag_cap;
+module_param(m_tag_cap, ulong, 0444);
+
+/* use for sdmode disable */
+static ulong m_sdmode_dis;
+module_param(m_sdmode_dis, ulong, 0444);
+
+/* use to disable mmc support */
+static ulong m_mmc_dis;
+module_param(m_mmc_dis, ulong, 0444);
+
+/* uhs2 range/half/low select */
+static ulong m_uhs2_ctrl = 0x97400012;
+module_param(m_uhs2_ctrl, ulong, 0444);
+
+/* output tuning control */
+static ulong m_output_tuning = 0x98030036;
+module_param(m_output_tuning, ulong, 0444);
+
+/* auto stop infinite control */
+static ulong m_auto_stopinf;
+module_param(m_auto_stopinf, ulong, 0444);
+
+/* auto poweroff card function */
+static ulong m_auto_poweroff;
+module_param(m_auto_poweroff, ulong, 0444);
+
+/* uhs2 auto go dormant/hbr function */
+static ulong m_auto_dmt;
+module_param(m_auto_dmt, ulong, 0444);
+
+/* Enable need set to 0x8004_0100 */
+static ulong m_psd_mode = 0x80000000;
+module_param(m_psd_mode, ulong, 0444);
+
+/* Enable need set to 0x8004_0100 */
+static ulong m_sw_sel_inject_sdr50 = 0x67f;
+module_param(m_sw_sel_inject_sdr50, ulong, 0444);
+
+static unsigned int pcr_setting_count;
+char *pcr_settings[MAX_PCR_SETTING_SIZE] = { 0 };
+
+module_param_array(pcr_settings, charp, &pcr_setting_count, 0444);
+MODULE_PARM_DESC(pcr_settings,
+		 "pcr_settings is a reg=value like string list split by comma");
+
+static unsigned int dmdn_setting_count;
+char *dmdn_settings[MAX_FREQ_SUPP] = { 0 };
+
+module_param_array(dmdn_settings, charp, &dmdn_setting_count, 0444);
+MODULE_PARM_DESC(dmdn_settings,
+		 "dmdn_settings is a reg=value like string list split by comma");
+
+uint m_sd_3v3_clk_driver_strength = 2;
+module_param(m_sd_3v3_clk_driver_strength, uint, 0444);
+
+uint m_sd_3v3_cmddata_driver_strength = 2;
+module_param(m_sd_3v3_cmddata_driver_strength, uint, 0444);
+
+uint m_sd_1v8_clk_driver_strength = 2;
+module_param(m_sd_1v8_clk_driver_strength, uint, 0444);
+
+uint m_sd_1v8_cmddata_driver_strength = 1;
+module_param(m_sd_1v8_cmddata_driver_strength, uint, 0444);
+
+uint m_cnfg_drv;
+module_param(m_cnfg_drv, uint, 0444);
+
+uint m_cnfg_trm_code_tx = 8;
+module_param(m_cnfg_trm_code_tx, uint, 0444);
+
+uint m_cnfg_trm_code_rx = 7;
+module_param(m_cnfg_trm_code_rx, uint, 0444);
+
+uint m_cnfg_rint_code = 9;
+module_param(m_cnfg_rint_code, uint, 0444);
+
+uint m_ram_ema = 0x42492000;
+module_param(m_ram_ema, uint, 0444);
+
+uint m_vdd1_vdd2_source;
+module_param(m_vdd1_vdd2_source, uint, 0444);
+
+uint m_vdd18_debounce_time = 1;
+module_param(m_vdd18_debounce_time, uint, 0444);
+
+uint m_ssc_enable;
+module_param(m_ssc_enable, uint, 0444);
+
+static ulong m_driver_item = 0x8080000a;
+module_param(m_driver_item, ulong, 0444);
+
+/* sd7_sd_mode_switch_control, 300: camera, 100: pc */
+static ulong m_sd7_sdmode_switch_control = 0x00000300;
+module_param(m_sd7_sdmode_switch_control, ulong, 0444);
+
+/* mmc_mode_dis */
+static ulong m_mmc_mode_dis = 0x80000000;
+module_param(m_mmc_mode_dis, ulong, 0444);
+
+/* pcie_wake_setting */
+static ulong m_pcie_wake_setting;
+module_param(m_pcie_wake_setting, ulong, 0444);
+
+/* sd_card_mode_dis */
+static ulong m_sd_card_mode_dis = 0x80000021;
+module_param(m_sd_card_mode_dis, ulong, 0444);
+
+/* vdd_power_source_item */
+static ulong m_vdd_power_source_item = 0x00090a0c;
+module_param(m_vdd_power_source_item, ulong, 0444);
+
+/* hsmux_vcme_enable */
+static ulong m_hsmux_vcme_enable;
+module_param(m_hsmux_vcme_enable, ulong, 0444);
+
+/* test_max_access_mode */
+static ulong m_test_max_access_mode = 0x80000005;
+module_param(m_test_max_access_mode, ulong, 0444);
+
+/* host_drive_strength */
+static ulong m_host_drive_strength = 0x80001125;
+module_param(m_host_drive_strength, ulong, 0444);
+
+/* auto_detect_refclk_counter_range_ctl */
+static ulong m_auto_detect_refclk_counter_range_ctl = 0x80640000;
+module_param(m_auto_detect_refclk_counter_range_ctl, ulong, 0444);
+
+/* refclk_stable_detection_counter1 */
+static ulong m_refclk_stable_detection_counter1 = 0x00000003;
+module_param(m_refclk_stable_detection_counter1, ulong, 0444);
+
+static ulong m_refclk_stable_detection_counter2 = 0x001e044c;
+module_param(m_refclk_stable_detection_counter2, ulong, 0444);
+
+static ulong m_refclk_stable_detection_counter3 = 0x04b024b0;
+module_param(m_refclk_stable_detection_counter3, ulong, 0444);
+
+static ulong m_l1_enter_exit_logic_ctl;
+module_param(m_l1_enter_exit_logic_ctl, ulong, 0444);
+
+/* pcie_phy_amplitude_adjust */
+static ulong m_pcie_phy_amplitude_adjust = 0x0000006a;
+module_param(m_pcie_phy_amplitude_adjust, ulong, 0444);
+
+static ulong m_output_tuning_item = 0xc01837bb;
+module_param(m_output_tuning_item, ulong, 0444);
+
+static ulong m_test_driver_strength_sel;
+module_param(m_test_driver_strength_sel, ulong, 0444);
+
+static ulong m_test_max_power_limit = 0x80000003;
+module_param(m_test_max_power_limit, ulong, 0444);
+
+static ulong m_test_uhs2_setting2 = 0x80000006;
+module_param(m_test_uhs2_setting2, ulong, 0444);
+
+static ulong m_test_sdma_boundary = 0x00000020;
+module_param(m_test_sdma_boundary, ulong, 0444);
+
+static ulong m_test_dma_mode_setting = 0x80000071;
+module_param(m_test_dma_mode_setting, ulong, 0444);
+
+static ulong m_test_ocb_ctrl;
+module_param(m_test_ocb_ctrl, ulong, 0444);
+
+static ulong m_bios_l1_substate = 0x8000000f;
+module_param(m_bios_l1_substate, ulong, 0444);
+
+static ulong m_test_main_ldo_setting;
+module_param(m_test_main_ldo_setting, ulong, 0444);
+
+static ulong m_auto_sleep_control;
+module_param(m_auto_sleep_control, ulong, 0444);
+
+static ulong m_power_wait_time = 0x0024000a;
+module_param(m_power_wait_time, ulong, 0444);
+
+static ulong m_test_write_data_timeout = 0x80001770;
+module_param(m_test_write_data_timeout, ulong, 0444);
+
+static ulong m_test_read_data_timeout = 0x80001770;
+module_param(m_test_read_data_timeout, ulong, 0444);
+
+static ulong m_test_non_data_timeout = 0x800003e8;
+module_param(m_test_non_data_timeout, ulong, 0444);
+
+static ulong m_test_r1b_data_timeout = 0x80001194;
+module_param(m_test_r1b_data_timeout, ulong, 0444);
+
+static ulong m_test_card_init_timeout = 0x800005dc;
+module_param(m_test_card_init_timeout, ulong, 0444);
+
+extern cfg_item_t g_cfg[SUPPORT_CHIP_COUNT][2];
+
+struct kmem_cache *srb_ext_cachep;
+
+/*
+ *  SD Spec Standard Memory register access
+ */
+
+u32 sdhci_readl(sd_host_t *host, u16 offset)
+{
+	u32 regval = 0;
+
+	regval = readl(host->pci_dev.membase + offset);
+	DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACER, NOT_TO_RAM,
+		"%s(0x%08X): 0x%08X\n", __func__, offset, regval);
+
+	return regval;
+}
+
+void sdhci_writel(sd_host_t *host, u16 offset, u32 value)
+{
+	DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACEW, NOT_TO_RAM,
+		"%s(0x%08X): 0x%08X\n", __func__, offset, value);
+	writel(value, host->pci_dev.membase + offset);
+}
+
+void sdhci_or32(sd_host_t *host, u16 offset, u32 value)
+{
+	u32 regval = 0;
+
+	regval = sdhci_readl(host, offset);
+	regval |= value;
+	sdhci_writel(host, offset, regval);
+}
+
+void sdhci_and32(sd_host_t *host, u16 offset, u32 value)
+{
+	u32 regval = 0;
+
+	regval = sdhci_readl(host, offset);
+	regval &= (value);
+	sdhci_writel(host, offset, regval);
+
+}
+
+void sdhci_writew(sd_host_t *host, u16 offset, u16 value)
+{
+	writew(value, host->pci_dev.membase + offset);
+	DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACEW, NOT_TO_RAM,
+		"%s(0x%04X): 0x%08X\n", __func__, offset, value);
+
+}
+
+u16 sdhci_readw(sd_host_t *host, u16 offset)
+{
+	u16 regval = 0;
+
+	regval = readw(host->pci_dev.membase + offset);
+	DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACER, NOT_TO_RAM,
+		"%s(0x%04X): 0x%08X\n", __func__, offset, regval);
+
+	return regval;
+}
+
+void sdhci_or16(sd_host_t *host, u16 offset, u16 value)
+{
+	u16 regval = 0;
+
+	regval = sdhci_readw(host, offset);
+	regval |= value;
+	sdhci_writew(host, offset, regval);
+}
+
+void sdhci_and16(sd_host_t *host, u16 offset, u16 value)
+{
+	u16 regval = 0;
+
+	regval = sdhci_readw(host, offset);
+	regval &= value;
+	sdhci_writew(host, offset, regval);
+}
+
+/*
+ *  Vendor Memory register access
+ */
+
+/* 16bit access */
+u16 ven_readw(sd_host_t *host, u16 offset)
+{
+	u16 regval = 0;
+
+	regval = readw(host->pci_dev.membase2 + offset);
+
+	DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACER, NOT_TO_RAM,
+		"[Memory Base 2]  Readw(0x%08X): 0x%08X\n", offset, regval);
+	return regval;
+}
+
+void ven_writew(sd_host_t *host, u16 offset, u16 value)
+{
+
+	writew(value, host->pci_dev.membase2 + offset);
+	DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACEW, NOT_TO_RAM,
+		"[Memory Base 2] Writew(0x%08X): 0x%08X\n", offset, value);
+
+}
+
+void ven_or16(sd_host_t *host, u16 offset, u16 value)
+{
+	u16 regval = 0;
+
+	regval = ven_readw(host, offset);
+	regval |= value;
+	ven_writew(host, offset, regval);
+}
+
+void ven_and16(sd_host_t *host, u16 offset, u16 value)
+{
+	u16 regval = 0;
+
+	regval = ven_readw(host, offset);
+	regval &= (value);
+	ven_writew(host, offset, regval);
+}
+
+/* 32bit access */
+u32 ven_readl(sd_host_t *host, u16 offset)
+{
+	u32 regval = 0;
+
+	regval = readl(host->pci_dev.membase2 + offset);
+
+	DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACER, NOT_TO_RAM,
+		"[Memory Base 2]  Readl(0x%08X): 0x%08X\n", offset, regval);
+	return regval;
+}
+
+void ven_writel(sd_host_t *host, u16 offset, u32 value)
+{
+
+	writel(value, host->pci_dev.membase2 + offset);
+
+	DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACEW, NOT_TO_RAM,
+		"[Memory Base 2] Writel(0x%08X): 0x%08X\n", offset, value);
+
+}
+
+void ven_or32(sd_host_t *host, u16 offset, u32 value)
+{
+	u32 regval = 0;
+
+	regval = ven_readl(host, offset);
+	regval |= value;
+	ven_writel(host, offset, regval);
+
+}
+
+void ven_and32(sd_host_t *host, u16 offset, u32 value)
+{
+	u32 regval = 0;
+
+	regval = ven_readl(host, offset);
+	regval &= (value);
+	ven_writel(host, offset, regval);
+
+}
+
+/*
+ *  PCI config register accessing
+ */
+void pci_port_writel(sd_host_t *host, u32 port, u32 data)
+{
+
+	pci_write_config_dword(host->pci_dev.pci_dev, port, data);
+
+	DbgInfo(MODULE_SD_HOST, FEATURE_PCIREG_TRACEW, NOT_TO_RAM,
+		"%s(0x%08X): 0x%08X\n", __func__, port, data);
+
+}
+
+u32 pci_port_readl(sd_host_t *host, u32 port)
+{
+	u32 regval = 0;
+
+	pci_read_config_dword(host->pci_dev.pci_dev, port, &regval);
+
+	DbgInfo(MODULE_SD_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM,
+		"%s(0x%08X): 0x%08X\n", __func__, port, regval);
+
+	return regval;
+}
+
+/*
+ *  PCI config register accessing by configuration IO method
+ */
+void pci_cfgio_writel(sd_host_t *host, u16 offset, u32 value)
+{
+	pci_write_config_dword(host->pci_dev.pci_dev, offset, value);
+}
+
+u32 pci_cfgio_readl(sd_host_t *host, u16 offset)
+{
+	u32 regval = 0;
+
+	pci_read_config_dword(host->pci_dev.pci_dev, offset, &regval);
+	DbgInfo(MODULE_SD_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM,
+		"%s(0x%08X): 0x%08X\n", __func__, offset, regval);
+
+	return regval;
+}
+
+/*
+ *
+ * Function Name: timer_auto_cb
+ *
+ * Abstract:
+ *
+ *			for timer callback
+ *
+ * Input:
+ *
+ *			PVOID	pctx [in]: Pointer to  pdx
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			null
+ *
+ * Notes:
+ *
+ *           Caller: OS
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+void timer_auto_cb2(struct timer_list *t)
+{
+	os_struct *o = from_timer(o, t, timer);
+	bht_dev_ext_t *pdx = container_of(o, bht_dev_ext_t, os);
+
+	func_timer_callback(pdx);
+}
+#else
+void timer_auto_cb2(PVOID pdx)
+{
+	func_timer_callback(pdx);
+}
+#endif
+
+/*
+ *
+ * Function Name: timer_subid_cb
+ *
+ * Abstract:
+ *
+ *			  for timer callback
+ *
+ * Input:
+ *
+ *			PVOID	pctx [in]: Pointer to  pdx
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			null
+ *
+ * Notes:
+ *
+ *           Caller: OS
+ */
+
+void timer_subid_cb(PVOID ctx, PVOID pdx)
+{
+	/* todo */
+}
+
+/*
+ * Function Name: os_start_timer
+ * Abstract: This Function is used to start a timer
+ *
+ * Input:
+ *			os_struct *os,
+ *			e_timer_t t timer id
+ *			u32 time_ms
+ *
+ *
+ *
+ * IRQL: any
+ *
+ *        so giving the routine another name requires you to modify the build tools.
+ */
+
+void os_start_timer(void *p, os_struct *os, e_timer_t t, u32 time_ms)
+{
+	if (t >= MAX_TIMER_NUM)
+		return;
+
+	if ((u32) t != 0) {
+		DbgErr("Timer %d not support now\n", t);
+		return;
+	}
+
+	mod_timer(&os->timer, jiffies + msecs_to_jiffies(time_ms));
+}
+
+/*
+ * Function Name: os_cancel_timer
+ * Abstract: This Function is used to start a timer
+ *
+ * Input:
+ *			os_struct *os,
+ *			e_timer_t t timer id
+ *			u32 time_ms
+ *
+ *
+ * IRQL:any
+ *
+ *        so giving the routine another name requires you to modify the build tools.
+ */
+void os_cancel_timer(void *p, os_struct *os, e_timer_t t)
+{
+
+	if (t >= MAX_TIMER_NUM)
+		return;
+
+	del_timer(&os->timer);
+
+}
+
+void os_stop_timer(void *p, os_struct *os, e_timer_t t)
+{
+
+	if (t >= MAX_TIMER_NUM)
+		return;
+}
+
+/*
+ *
+ * Function Name:  os_set_event
+ *
+ * Abstract:
+ *
+ *			 1. set event to thread
+ *
+ * Input:
+ *
+ *			os_struct *os [in]: pointer to the OS structure
+ *			e_event_t event [in]: the event need set
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			None
+ *
+ * IRQL:any
+ *
+ *
+ */
+
+void os_set_event(os_struct *os, e_event_t event)
+{
+	spin_lock(&os->event.lock);
+
+	set_bit((int)event, &os->event.evt_flag);
+
+	atomic_set(&os->event.evt_comming, 1);
+
+	spin_unlock(&os->event.lock);
+
+	wake_up_interruptible(&os->event.evt_control);
+}
+
+/*
+ *
+ * Function Name:  os_clear_event
+ *
+ * Abstract:
+ *
+ *			 1. clear event to thread
+ *
+ * Input:
+ *
+ *			os_struct *os [in]: pointer to the OS structure
+ *			e_event_t event [in]: the event need send
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			None
+ *
+ *
+ *
+ */
+
+void os_clear_event(os_struct *os, e_event_t event)
+{
+	/* nothing to do as event is auto cleared  by os_wait_event */
+}
+
+/*
+ *
+ * Function Name:  os_wait_event
+ *
+ * Abstract:
+ *
+ *			 1. wait any one signaled event of  multiple events
+ *
+ * Input:
+ *
+ *			os_struct *os [in]: pointer to the OS structure
+ *			s32 timeout [in]: timeout value
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			None
+ *
+ *
+ *
+ */
+e_event_t os_wait_event(os_struct *os)
+{
+	e_event_t event = EVENT_NONE;
+	int i;
+
+	if (atomic_read(&os->event.evt_comming))
+		goto next;
+
+	wait_event_interruptible(os->event.evt_control,
+				 ((atomic_read(&os->event.evt_comming) != 0)
+				  || (kthread_should_stop())));
+
+next:
+	if (kthread_should_stop()) {
+		event = EVENT_TERMINATE;
+		clear_bit(event, &os->event.evt_flag);
+		atomic_set(&os->event.evt_comming, 0);
+		goto exit;
+	}
+
+	if (atomic_read(&os->event.evt_comming) == 0)
+		goto exit;
+
+	spin_lock(&(os->event.lock));
+
+	for (i = 0; i < EVENT_NONE; i++) {
+		if (test_bit(i, &os->event.evt_flag)) {
+			event = i;
+			clear_bit(i, &os->event.evt_flag);
+			break;
+		}
+	}
+
+	if (os->event.evt_flag == 0)
+		atomic_set(&os->event.evt_comming, 0);
+
+	spin_unlock(&(os->event.lock));
+
+exit:
+	return event;
+
+}
+
+/*
+ *
+ * Function Name:  os_create_thread
+ *
+ * Abstract:
+ *
+ *			 1. create thread
+ *
+ * Input:
+ *
+ *			thread_t *[in]: pointer to the thread entry
+ *			void *param, [in]:  the parameter which pass to thread entity
+ *			thread_cb_t func [in]: the entry of thread enity
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			TRUE: create ok
+ *			FALSE: create failed
+ *
+ *
+ *
+ */
+bool os_create_thread(thread_t *thr, void *param, thread_cb_t func)
+{
+
+	thr->pthread =
+	    kthread_run((void *)func, (PVOID) param, "bhtsd_scsi_thread");
+	if (thr->pthread == NULL)
+		return FALSE;
+	return TRUE;
+}
+
+/*
+ *
+ * Function Name:  os_stop_thread
+ *
+ * Abstract:
+ *
+ *			 1. stop the thread
+ *
+ * Input:
+ *
+ *			thread_t *[in]: pointer to the thread entry
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			TRUE: stop ok
+ *			FALSE:stop failed
+ *
+ *
+ *
+ */
+bool os_stop_thread(os_struct *os, thread_t *thr)
+{
+	bool ret = FALSE;
+	int i = 0;
+
+	DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+
+	if (thr != NULL && thr->pthread != NULL && os != NULL) {
+		os->thread.freeze = TRUE;
+		os_set_event(os, EVENT_TERMINATE);
+		kthread_stop(os->thread.pthread);
+		while (test_bit(EVENT_TERMINATE, &os->event.evt_flag)) {
+			os_mdelay(20);
+			i++;
+			if (i > 150)
+				goto exit;
+		}
+
+		ret = TRUE;
+	}
+exit:
+	DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM,
+		"Exit %s  ret=%xh\n", __func__, ret);
+	return ret;
+}
+
+void os_kill_thread(os_struct *os, thread_t *thr)
+{
+}
+
+bool os_thread_is_freeze(void *pdx)
+{
+	os_struct *os = &((bht_dev_ext_t *) pdx)->os;
+
+	if (os->thread.freeze || os->thread.pthread == NULL)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+/*
+ *
+ * Function Name:  os_pending_thread
+ *
+ * Abstract:
+ *
+ *			 pending thread or resume thread
+ *
+ *
+ * Input:
+ *
+ *			thread_t *[in]: pointer to the thread entry
+ *			bool pending [in]: TRUE means pending thread
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *			TRUE: operate ok
+ *			FALSE:operate failed
+ *
+ * IRQL:ANY
+ *
+ *
+ */
+bool os_pending_thread(void *pdx, bool pending)
+{
+	os_struct *os = &((bht_dev_ext_t *) pdx)->os;
+	u32 i = 600;
+	bool ret = TRUE;
+
+	DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+	if (os->thread.pthread == NULL) {
+		DbgErr("thread obj null\n");
+		return FALSE;
+	}
+
+	os->thread.pending_lock = FALSE;
+	if (pending == TRUE) {
+		ret = FALSE;
+		os_init_completion(pdx, &os->thread.break_pending);
+		os->thread.freeze = TRUE;
+		os_set_event(os, EVENT_PENDING);
+		while (i--) {
+			if (os->thread.pending_lock == TRUE) {
+				ret = TRUE;
+				break;
+			}
+			os_mdelay(20);
+		}
+
+	} else {
+		os_finish_completion(pdx, &os->thread.break_pending);
+		os->thread.freeze = FALSE;
+
+	}
+
+	if (ret == FALSE)
+		DbgErr("%s timeout\n", __func__);
+	DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM,
+		"Exit %s  ret=%xh\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * completion API
+ */
+
+/*
+ *
+ * Function Name:  os_init_completion
+ *
+ * Abstract:
+ *
+ *			 1.init the completion.
+ *
+ * Input:
+ *
+ *			completion_t *p[in]: pointer to completion
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *
+ * IRQL:any
+ *
+ *
+ */
+
+void os_init_completion(void *pdx, completion_t *p)
+{
+	init_completion(p);
+}
+
+/*
+ *
+ * Function Name: os_finish_completion
+ *
+ * Abstract:
+ *
+ *			 1.wake the thread which wait for this completion.
+ *
+ * Input:
+ *
+ *			completion_t *p[in]: pointer to completion
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+void os_finish_completion(void *pdx, completion_t *p)
+{
+	complete(p);
+}
+
+/*
+ *
+ * Function Name: os_finish_completion
+ *
+ * Abstract:
+ *
+ *			 1.wake the thread which wait for this completion.
+ *
+ * Input:
+ *
+ *			completion_t *p[in]: pointer to completion
+ *			s32 timeout [in]: unit ms .if timeout =0,
+ *			mean wait for signal until it completed
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+
+bool os_wait_for_completion(void *pdx, completion_t *p, s32 timeout)
+{
+	s32 ret = wait_for_completion_timeout(p, msecs_to_jiffies(timeout));
+
+	if (ret == 0)
+		return FALSE;
+	else
+		return TRUE;
+}
+
+/*
+ *
+ * Function Name:os_list_locked_remove_head
+ *
+ * Abstract:
+ *
+ *     remove the list_entry to list head node
+ *
+ * Input:
+ *
+ *   list_t *p [in]: pointer to list
+ *
+ * Output:
+ *
+ *    None
+ *
+ * Return value:
+ *
+ *
+ * IRQL:ANY
+ *
+ *
+ */
+list_entry *os_list_locked_remove_head(list_t *p)
+{
+	unsigned long flags;
+	list_entry *entry = NULL;
+
+	spin_lock_irqsave(&p->lock, flags);
+
+	if (list_empty(&p->list_hd))
+		goto exit;
+
+	entry = p->list_hd.next;
+	list_del_init(entry);
+
+exit:
+	spin_unlock_irqrestore(&p->lock, flags);
+	/* need with verify with chuanjing */
+	return entry;
+}
+
+/*
+ *
+ * Function Name: os_list_locked_insert_tail
+ *
+ * Abstract:
+ *
+ *     insert the list etry to list tail
+ *
+ * Input:
+ *
+ *   list_t *p [in]: pointer to list
+ *
+ * Output:
+ *
+ *    None
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+void os_list_locked_insert_tail(list_t *p, list_entry *entry)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&p->lock, flags);
+
+	list_add_tail(entry, &p->list_hd);
+
+	spin_unlock_irqrestore(&p->lock, flags);
+}
+
+/*
+ *
+ * Function Name: os_list_locked_insert_head
+ *
+ * Abstract:
+ *
+ *     insert the node to list head
+ *
+ * Input:
+ *
+ *   list_t *p [in]: pointer to list
+ *
+ * Output:
+ *
+ *    None
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+void os_list_locked_insert_head(list_t *p, list_entry *entry)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&p->lock, flags);
+
+	list_add(entry, &p->list_hd);
+
+	spin_unlock_irqrestore(&p->lock, flags);
+
+}
+
+/*
+ *
+ * Function Name: os_list_init
+ *
+ * Abstract:
+ *
+ *			  init the list with lock
+ *
+ * Input:
+ *
+ *			list_t *p [in]: pointer to list
+ *
+ * Output:
+ *
+ *			 None
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+void os_list_init(list_t *p)
+{
+	p->lock = __SPIN_LOCK_UNLOCKED(p->lock);
+	INIT_LIST_HEAD(&p->list_hd);
+	os_atomic_set(&p->cnt, 0);
+
+}
+
+/*
+ *
+ * Function Name:  os_layer_init
+ *
+ * Abstract:
+ *
+ *			 1.init the os layer.
+ *
+ * Input:
+ *
+ *			os_struct *os [in]: pointer to os structure
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *
+ * irql:any
+ *
+ *
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+typedef void (*func_timer)(struct timer_list *);
+#else
+typedef void (*func_timer)(unsigned long);
+#endif
+
+bool os_layer_init(void *p, os_struct *os)
+{
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+	timer_setup(&os->timer, timer_auto_cb2, 0);
+#else
+	init_timer(&os->timer);
+	os->timer.data = (unsigned long)pdx;
+	os->timer.function = (func_timer) timer_auto_cb2;
+#endif
+	os->lock = __SPIN_LOCK_UNLOCKED(os->lock);
+
+	atomic_set(&os->event.evt_comming, 0);
+	init_waitqueue_head(&os->event.evt_control);
+	os->rt_pm_cnt = 0;
+	os->event.evt_flag = 0;
+	os->dma_mapped = 0;
+	os->virt_buff = vmalloc(CFG_MAX_TRANSFER_LENGTH);
+
+	if (os->virt_buff == NULL) {
+		DbgErr("Vmalloc failed for %d length\n",
+		       CFG_MAX_TRANSFER_LENGTH);
+	}
+
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n",
+		__func__);
+	return TRUE;
+}
+
+/*
+ *
+ * Function Name:  os_layer_uinit
+ *
+ * Abstract:
+ *
+ *			 1.uninit the os layer
+ *
+ * Input:
+ *
+ *			os_struct *os [in]: pointer to os structure
+ *
+ * Output:
+ *
+ *			None
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+bool os_layer_uinit(void *pdx, os_struct *os)
+{
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+
+	os_cancel_timer(pdx, os, (e_timer_t) 0);
+	if (os->virt_buff != NULL)
+		vfree(os->virt_buff);
+
+	return TRUE;
+}
+
+/*
+ *
+ * Function Name:  os_alloc_dma_buffer
+ *
+ * Abstract:
+ *
+ *			 1. allocate DMA buffer resource
+ *
+ * Input:
+ *
+ *			bht_dev_ext_t *pdx [in]: pointer to device extension
+ *			PPORT_CONFIGURATION_INFORMATION ConfigInfo [in]:
+ *				pointer to config information
+ *			u32 nbytes [in]: the size allocate
+ *			dma_desc_buf_t *pdma [in]: pointer to the DMA information,
+ *				include physical address & virtual address
+ *
+ * Output:
+ *
+ *			dma_desc_buf_t *pdma [out]: pointer to the DMA information,
+ *				include physical address & virtual address
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ *
+ *
+ */
+bool os_alloc_dma_buffer(void *p, void *ctx, u32 nbytes, dma_desc_buf_t *pdma)
+{
+#define DMA_BUF_ALIGN_SIZE (1<<12)
+	u32 retry = 0;
+	bht_dev_ext_t *pdx = p;
+
+	if (pdma->len < DMA_BUF_ALIGN_SIZE)
+		pdma->len = DMA_BUF_ALIGN_SIZE;
+
+	pdma->len = nbytes;
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"Enter %s size=%xh\n", __func__, nbytes);
+
+	for (retry = 1; retry < 10; retry++) {
+		pdma->pa = 0;
+		pdma->va =
+		    (void *)dmam_alloc_coherent(&pdx->host.pci_dev.pci_dev->dev,
+						pdma->len,
+						(dma_addr_t *) (&pdma->pa),
+						GFP_KERNEL);
+
+		if (pdma->va == NULL) {
+			DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0,
+				"os alloc dma buffer failed!!!,retry %d\n",
+				retry);
+		} else {
+			pdx->os.dma_info = *pdma;
+			break;
+		}
+	}
+	if (pdma->va == NULL) {
+		memset(&pdx->os.dma_info, 0, sizeof(dma_desc_buf_t));
+		DbgErr("dmam_alloc_coherent(%xh) failed!!!\n", pdma->len);
+		return FALSE;
+	} else {
+		DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0,
+			"GetUncachedExtension return OK with length 0x%x\n",
+			pdma->len);
+		DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0,
+			" - Before alignment, DMA buffer physical address =h[%x] l[%X]\n",
+			os_get_phy_addr32h(pdma->pa),
+			os_get_phy_addr32l(pdma->pa));
+		DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0,
+			" - Before alignment, DMA buffer virtual address = 0x%Xh\n",
+			pdma->va);
+	}
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n",
+		__func__);
+
+	return TRUE;
+}
+
+/*
+ *
+ * Function Name: os_free_dma_buffer
+ *
+ * Abstract:
+ *
+ *			 1. free DMA buffer resource
+ *
+ * Input:
+ *
+ *			bht_dev_ext_t *pdx [in]: pointer to device extension
+ *			dma_desc_buf_t *pdma [in]: pointer to the DMA information,
+ *				include physical address & virtual address
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *
+ * Notes:
+ */
+bool os_free_dma_buffer(void *p, dma_desc_buf_t *dma)
+{
+	bht_dev_ext_t *pdx = p;
+	dma_desc_buf_t *pdma = &pdx->os.dma_info;
+
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+
+	if (pdma->len == 0)
+		goto exit;
+
+	dmam_free_coherent(&pdx->host.pci_dev.pci_dev->dev, pdma->len, pdma->va,
+			   (dma_addr_t) pdma->pa);
+	pdma->pa = 0;
+	pdma->va = 0;
+	pdma->len = 0;
+exit:
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n",
+		__func__);
+	return TRUE;
+}
+
+/*
+ *
+ * Function Name: _os_sleep
+ *
+ * Abstract:
+ *
+ *			 1. sleep current thread
+ *
+ * Input:
+ *
+ *			u32 time_ms [in]:  sleep times
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *			FALSE : means can't sleep, reture immediately
+ *			TRUE: sleep ok
+ *
+ * Notes:
+ *			Delay in ms. It won't ties up the CPU.
+ *			But it is not so accurate. And it must be called in PASSIVE_LEVEL
+ *
+ */
+void os_sleep(u32 time_ms)
+{
+	u32 tick = os_get_cur_tick();
+
+	while (os_is_timeout(tick, time_ms) == FALSE)
+		schedule();
+}
+
+/*
+ *
+ * Function Name: os_udelay
+ *
+ * Abstract:
+ *
+ *			 1. CPU busy delay
+ *
+ * Input:
+ *
+ *			u32 time_us [in]:  busy delay times
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ * Notes:
+ *
+ */
+
+void os_udelay(u32 time_us)
+{
+	udelay(time_us);
+}
+
+/*
+ *
+ * Function Name: os_mdelay
+ *
+ * Abstract:
+ *
+ *			 1. CPU busy delay
+ *
+ * Input:
+ *
+ *			u32 time_ms [in]:  busy delay times, unit ms
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ * Notes:
+ *
+ */
+void os_mdelay(u32 time_ms)
+{
+	mdelay(time_ms);
+}
+
+/*
+ *
+ * Function Name: os_print
+ *
+ * Abstract:
+ *
+ *			 1. print string to debugview
+ *
+ * Input:
+ *
+ *			byte * s [in]:  pointer to string
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ * Notes:
+ *
+ */
+
+void os_print(byte *s)
+{
+	printk(s);
+}
+
+/*
+ *
+ * Function Name: os_alloc_vbuff
+ *
+ * Abstract:
+ *
+ *			 1. allocate virtual buffer
+ *
+ * Input:
+ *
+ *			u32 length [in]:  buffer length to allocate
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			the buffer address which allocate
+ *
+ * Notes:
+ *
+ */
+
+void *os_alloc_vbuff(u32 length)
+{
+	return kmalloc(length, GFP_KERNEL);
+}
+
+/*
+ *
+ * Function Name: os_free_vbuff
+ *
+ * Abstract:
+ *
+ *			 1. free the virtual buffer
+ *
+ * Input:
+ *
+ *			u32 length [in]:  buffer length to free
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+void os_free_vbuff(void *vbuff)
+{
+	kfree(vbuff);
+}
+
+/*
+ *
+ * Function Name: os_get_phy_addr32l
+ *
+ * Abstract:
+ *
+ *			 1.  get physical address low 32bit value
+ *
+ * Input:
+ *
+ *
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+u32 os_get_phy_addr32l(phy_addr_t phy_addr)
+{
+	return (phy_addr & 0x000000FFFFFFFF);
+}
+
+u64 os_get_phy_addr64(phy_addr_t phy_addr)
+{
+	return phy_addr;
+}
+
+/*
+ *
+ * Function Name: os_get_phy_addr32h
+ *
+ * Abstract:
+ *
+ *			 1.  get physical address high 32bit value
+ *
+ * Input:
+ *
+ *
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+u32 os_get_phy_addr32h(phy_addr_t phy_addr)
+{
+	return ((phy_addr & 0xFFFFFFFF00000000) >> 32);
+}
+
+/*
+ *
+ * Function Name: os_set_phy_addr32l
+ *
+ * Abstract:
+ *
+ *			 1.  set physical address low 32bit value
+ *
+ * Input:
+ *
+ *
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+void os_set_phy_addr32l(phy_addr_t *phy_addr, u32 addr)
+{
+	u32 *haddr = (u32 *) phy_addr;
+	*haddr = addr;
+
+}
+
+/*
+ *
+ * Function Name: os_set_phy_addr32h
+ *
+ * Abstract:
+ *
+ *			 1.  set physical address high 32bit value
+ *
+ * Input:
+ *
+ *
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+void os_set_phy_addr32h(phy_addr_t *phy_addr, u32 addr)
+{
+	u32 *haddr = (u32 *) phy_addr;
+
+	haddr++;
+	*haddr = addr;
+}
+
+/*
+ *
+ * Function Name: os_set_phy_add64
+ *
+ * Abstract:
+ *
+ *			 1.  set 64bit physical address
+ *
+ * Input:
+ *
+ *
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+void os_set_phy_add64(phy_addr_t *phy_addr, u64 addr)
+{
+
+	*phy_addr = addr;
+
+}
+
+/*
+ *
+ * Function Name: srb_parse_sgl
+ *
+ * Abstract:
+ *
+ *			 1.  get the SG list information for the SRB
+ *
+ * Input:
+ *
+ *
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+u32 os_get_sg_list(void *p, scsi_srb *pSrb, sg_list_t *sg)
+{
+	bht_dev_ext_t *pdx = p;
+	u32 sg_count = 0;
+	u32 i = 0;
+	u32 k = 0;
+	struct scatterlist *pAddList = NULL;
+	u32 dma_64bit =
+	    pdx->cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address;
+
+	sg_count =
+	    dma_map_sg(&pdx->host.pci_dev.pci_dev->dev, scsi_sglist(pSrb),
+		       scsi_sg_count(pSrb), pSrb->sc_data_direction);
+	if (sg_count > 0)
+		pdx->os.dma_mapped = TRUE;
+
+	scsi_for_each_sg(pSrb, pAddList, sg_count, k) {
+		if (dma_64bit)
+			sg[i].Address =
+			    os_get_phy_addr64(sg_dma_address(pAddList));
+		else
+			sg[i].Address =
+			    os_get_phy_addr32l(sg_dma_address(pAddList));
+
+		sg[i].Length = sg_dma_len(pAddList);
+		if (sg[i].Length % 4) {
+			DbgErr
+			    ("PortGetPhysicalAddress() return unaligned length: 0x%x!\n",
+			     sg[i].Length);
+			return 0;
+		}
+		i++;
+	}
+
+	return i;
+}
+
+void os_free_sg_list(void *p, scsi_srb *Srb)
+{
+	bht_dev_ext_t *pdx = p;
+
+	if (pdx->os.dma_mapped) {
+		pdx->os.dma_mapped = FALSE;
+		dma_unmap_sg(&pdx->host.pci_dev.pci_dev->dev, scsi_sglist(Srb),
+			     scsi_sg_count(Srb), Srb->sc_data_direction);
+	}
+}
+
+/*
+ *
+ * Function Name: os_set_dev_busy
+ *
+ * Abstract:
+ *
+ *			 1. notify Power manager device is busy state
+ *
+ * Input:
+ *
+ *			void *p [in]: pointer to device extension
+ *
+ * Output:
+ *
+ *
+ *
+ * Return value:
+ *
+ *			none
+ *
+ * Notes:
+ *
+ */
+#if BHT_LINUX_ENABLE_RTD3
+void os_set_dev_busy(void *p)
+{
+	bht_dev_ext_t *pdx = p;
+
+	if (pdx->pm_state.rtd3_en == FALSE)
+		return;
+	pdx->os.rt_pm_cnt++;
+	if (pdx->os.rt_pm_cnt == 1)
+		pm_runtime_get_sync(&pdx->host.pci_dev.pci_dev->dev);
+}
+
+void os_set_dev_idle(void *p)
+{
+	bht_dev_ext_t *pdx = p;
+
+	if (pdx->pm_state.rtd3_en == FALSE)
+		return;
+	if (pdx->os.rt_pm_cnt == 0) {
+		DbgErr("rt pm set idle cnt not right\n");
+	} else {
+
+		pdx->os.rt_pm_cnt--;
+		if (pdx->os.rt_pm_cnt == 0) {
+			pm_runtime_mark_last_busy(&pdx->host.pci_dev.pci_dev->dev);
+			pm_runtime_put_autosuspend(&pdx->host.pci_dev.pci_dev->dev);
+		}
+	}
+}
+#else
+void os_set_dev_busy(void *p)
+{
+}
+
+void os_set_dev_idle(void *pdx)
+{
+}
+
+#endif
+
+void os_bus_change(void *p)
+{
+	bht_dev_ext_t *pdx = p;
+
+	DbgErr("Enter os bus change with (%d)\n", pdx->card.card_present);
+
+	if (pdx->card.card_present)
+		bht_scsi_init(pdx, pdx->dev);
+	else
+		bht_scsi_uinit(pdx);
+
+}
+
+u32 os_get_cur_tick(void)
+{
+	return jiffies_to_msecs(jiffies);
+}
+
+bool os_is_timeout(u32 start_tck, u32 time_ms)
+{
+	if ((start_tck + time_ms) <= os_get_cur_tick())
+		return TRUE;
+	else
+		return FALSE;
+}
+
+/*
+ *
+ * Function Name: os_memset
+ *
+ * Abstract:
+ *
+ *			Fill a block of memory with zeros,
+ *			given a pointer to the block and the length,
+ *			in bytes, to be filled.
+ *
+ * Input:
+ *
+ *
+ *                          memory_len [in]: The number of bytes to fill with zeros.
+ *
+ *
+ * Output:
+ *
+ *			buffer [out]: A pointer to the memory block to be filled with zeros.
+ *
+ * Return value:
+ *
+ *			None
+ *
+ * Notes:
+ *
+ *                         IRQL Any level
+ *
+ */
+void os_memset(void *buffer, byte fill, s32 len)
+{
+	memset(buffer, fill, len);
+}
+
+/*
+ *
+ * Function Name: os_memcpy
+ *
+ * Abstract:
+ *
+ *			Copy the contents of a source memory block to a destination memory block.
+ *
+ * Input:
+ *
+ *			sbuf [in]: A pointer to the source memory block to copy the bytes from.
+ *			memory_len [in]: The number of bytes to copy from
+ *				the source to the destination.
+ *
+ * Output:
+ *
+ *			dbuf [out]: A pointer to the destination memory block to copy the bytes to.
+ *
+ * Return value:
+ *
+ *			None
+ *
+ * Notes:
+ *
+ *                         IRQL Any level
+ *
+ */
+void os_memcpy(void *dbuf, void *sbuf, s32 len)
+{
+	memcpy(dbuf, sbuf, len);
+}
+
+s32 os_memcpr(void *dbuf, void *sbuf, s32 len)
+{
+	return memcmp(dbuf, sbuf, len);
+}
+
+void os_enum_reg_cfg(void *cfg_item, e_chip_type chip_type, const byte *ustr,
+		     cb_enum_reg_t func)
+{
+	char *idx_string = NULL;
+	char *addr_string = NULL;
+	char *value_string = NULL;
+	char *separator = NULL;
+	u32 i = 0;
+	u32 idx = 0;
+	u32 type = (u32) -1;
+	u32 addr = 0;
+	u32 value = 0;
+	int err = 0;
+	char cfg_buf[512];
+
+	if (strcmp(ustr, "\\pcr") == 0) {
+		for (i = 0; i < pcr_setting_count; i++) {
+			PrintMsg("pcr_settings[%d] = %s\n", i, pcr_settings[i]);
+
+			if (strlen(pcr_settings[i]) >= sizeof(cfg_buf)) {
+				DbgErr("pcr setting string %d is too long\n",
+				       i);
+				continue;
+			} else if (strlen(pcr_settings[i]) < 14) {
+				DbgErr("pcr setting string %d is too short\n",
+				       i);
+				continue;
+			}
+			strcpy(cfg_buf, pcr_settings[i]);
+			idx_string = cfg_buf;
+			if (idx_string[3] != '_') {
+				DbgErr
+				    ("failed to find separator between idx and type\n");
+				continue;
+			}
+			idx_string[3] = '\0';
+			err = kstrtou32(idx_string, 16, &idx);
+			if (err) {
+				DbgErr
+				    ("failed to parse idx from pcr setting: %s\n",
+				     pcr_settings[i]);
+				continue;
+			}
+			if (idx_string[4] == 'P' || idx_string[4] == 'p')
+				type = 0;
+			else if (idx_string[4] == 'M' || idx_string[4] == 'm')
+				type = 1;
+			else if (idx_string[4] == 'V' || idx_string[4] == 'v')
+				type = 2;
+			else {
+				DbgErr
+				    ("failed to parse type from pcr setting: %s\n",
+				     pcr_settings[i]);
+				continue;
+			}
+			addr_string = cfg_buf + 5;
+			separator = strchr(addr_string, '=');
+			if (separator == NULL) {
+				DbgErr
+				    ("failed to find separator between addr and value\n");
+				continue;
+			}
+			*separator = '\0';
+			value_string = ++separator;
+			err = kstrtou32(addr_string, 16, &addr);
+			if (err) {
+				DbgErr
+				    ("failed to parse addr from pcr setting: %s\n",
+				     pcr_settings[i]);
+				continue;
+			}
+			err = kstrtou32(value_string, 16, &value);
+			if (err) {
+				DbgErr
+				    ("failed to parse value from pcr setting: %s\n",
+				     pcr_settings[i]);
+				continue;
+			}
+			PrintMsg("pcr[0x%08x]=0x%08x, type = %d, idx=%d\n",
+				 addr, value, type, idx);
+			if (func)
+				func(cfg_item, type, idx, addr, value);
+		}
+	} else if (strcmp(ustr, "\\dmdn") == 0) {
+		for (i = 0; i < dmdn_setting_count; i++) {
+			if (strlen(pcr_settings[i]) >= sizeof(cfg_buf)) {
+				DbgErr("dmdn setting string %d is too long\n",
+				       i);
+				continue;
+			}
+			strcpy(cfg_buf, dmdn_settings[i]);
+			addr_string = cfg_buf;
+			separator = strchr(addr_string, '=');
+			if (separator == NULL) {
+				DbgErr
+				    ("failed to parse value from dmdn setting: %s\n",
+				     dmdn_settings[i]);
+				continue;
+			}
+			*separator = '\0';
+			value_string = ++separator;
+			err = kstrtou32(addr_string, 16, &addr);
+			if (err) {
+				DbgErr
+				    ("failed to parse addr from dmdn setting: %s\n",
+				     dmdn_settings[i]);
+				continue;
+			}
+			err = kstrtou32(value_string, 16, &value);
+			if (err) {
+				DbgErr
+				    ("failed to parse value from dmdn setting: %s\n",
+				     dmdn_settings[i]);
+				continue;
+			}
+			PrintMsg("dmdn[0x%08x]=0x%08x\n", addr, value);
+			if (func)
+				func(cfg_item, 0, 0, addr, value);
+		}
+	} else
+		PrintMsg("current only support pcr or dmdnsettings\n",
+			 __func__);
+}
+
+void os_pm_init(void *dev_evt)
+{
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+	/* todo for PM part */
+
+	DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n",
+		__func__);
+
+}
+
+bool os_pcr_pesistent_restore(u16 *addr_tb, u32 *val_tb, u32 tb_len)
+{
+	return FALSE;
+}
+
+bool os_pcr_pesistent_save(u16 *addr_tb, u32 *val_tb, u32 tb_len)
+{
+	return FALSE;
+}
+
+void os_random_init(void)
+{
+	/* no implement for linux */
+
+}
+
+u32 os_random_get(u32 max_value)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0)
+	return (get_random_long() % max_value);
+#else
+	return random32();
+#endif
+}
+
+void os_bak_reg_hibernate(void)
+{
+	/* no implement for linux */
+
+}
+
+u64 os_get_performance_tick(u64 *cpu_freq)
+{
+	/* no implement for linux */
+	return 0;
+}
+
+/*
+ *
+ * Function Name: os_cfg_load
+ *
+ * Abstract:
+ *			 1. Read total reigstry information
+ *
+ * Input:
+ *            cfg_item_t *cfg: Pointer to the registry config structure
+ *            e_chip_type chip_type;
+ *
+ * Output:
+ *			None
+ *
+ * Return value:
+ *           None
+ *
+ * Notes:
+ *           Caller: cfgmng_init
+ */
+void os_cfg_load(void *cfg_item, e_chip_type chip_type)
+{
+	cfg_item_t *cfg = cfg_item;
+
+	if (m_emmc_mode & BIT31)
+		os_memcpy(&cfg->card_item.emmc_mode, &m_emmc_mode, 4);
+
+	if (m_dma_mode & BIT31) {
+		os_memcpy(&cfg->host_item.test_dma_mode_setting, &m_dma_mode,
+			  4);
+	}
+
+	if (m_infinite_ctrl & BIT31) {
+		os_memcpy(&cfg->host_item.test_infinite_transfer_mode,
+			  &m_infinite_ctrl, 4);
+	}
+
+	if (m_tag_cap & BIT31) {
+		os_memcpy(&cfg->host_item.test_tag_queue_capability, &m_tag_cap,
+			  4);
+	}
+
+	if (m_sdmode_dis & BIT31)
+		os_memcpy(&cfg->card_item.sd_card_mode_dis, &m_sdmode_dis, 4);
+
+	if (m_mmc_dis & BIT31)
+		os_memcpy(&cfg->card_item.mmc_mode_dis, &m_mmc_dis, 4);
+
+	if (m_uhs2_ctrl & BIT31)
+		os_memcpy(&cfg->card_item.uhs2_setting, &m_uhs2_ctrl, 4);
+
+	if (m_output_tuning & BIT31) {
+		os_memcpy(&cfg->feature_item.output_tuning_item,
+			  &m_output_tuning, 4);
+	}
+
+	if (m_auto_dmt & BIT31)
+		os_memcpy(&cfg->timer_item.auto_dormant_timer, &m_auto_dmt, 4);
+
+	os_memcpy(&cfg->driver_item, &m_driver_item, 4);
+
+	os_memcpy(&cfg->card_item.sd7_sdmode_switch_control,
+		  &m_sd7_sdmode_switch_control, 4);
+	os_memcpy(&cfg->card_item.sd_card_mode_dis, &m_sd_card_mode_dis, 4);
+	os_memcpy(&cfg->card_item.test_max_access_mode, &m_test_max_access_mode,
+		  4);
+	os_memcpy(&cfg->card_item.test_uhs2_setting2, &m_test_uhs2_setting2, 4);
+	os_memcpy(&cfg->card_item.test_driver_strength_sel,
+		  &m_test_driver_strength_sel, 4);
+	os_memcpy(&cfg->card_item.test_max_power_limit, &m_test_max_power_limit,
+		  4);
+	os_memcpy(&cfg->card_item.mmc_mode_dis, &m_mmc_mode_dis, 4);
+
+	os_memcpy(&cfg->feature_item.pcie_wake_setting, &m_pcie_wake_setting,
+		  4);
+	os_memcpy(&cfg->feature_item.hsmux_vcme_enable, &m_hsmux_vcme_enable,
+		  4);
+
+	os_memcpy(&cfg->host_item.vdd_power_source_item,
+		  &m_vdd_power_source_item, 4);
+	os_memcpy(&cfg->host_item.host_drive_strength, &m_host_drive_strength,
+		  4);
+	os_memcpy(&cfg->host_item.test_sdma_boundary, &m_test_sdma_boundary, 4);
+	os_memcpy(&cfg->host_item.test_dma_mode_setting,
+		  &m_test_dma_mode_setting, 4);
+	os_memcpy(&cfg->host_item.test_ocb_ctrl, &m_test_ocb_ctrl, 4);
+	os_memcpy(&cfg->host_item.bios_l1_substate, &m_bios_l1_substate, 4);
+
+	os_memcpy(&cfg->feature_item.auto_detect_refclk_counter_range_ctl,
+		  &m_auto_detect_refclk_counter_range_ctl, 4);
+	os_memcpy(&cfg->feature_item.refclk_stable_detection_counter1,
+		  &m_refclk_stable_detection_counter1, 4);
+	os_memcpy(&cfg->feature_item.refclk_stable_detection_counter2,
+		  &m_refclk_stable_detection_counter2, 4);
+	os_memcpy(&cfg->feature_item.refclk_stable_detection_counter3,
+		  &m_refclk_stable_detection_counter3, 4);
+	os_memcpy(&cfg->feature_item.l1_enter_exit_logic_ctl,
+		  &m_l1_enter_exit_logic_ctl, 4);
+	os_memcpy(&cfg->feature_item.pcie_phy_amplitude_adjust,
+		  &m_pcie_phy_amplitude_adjust, 4);
+	os_memcpy(&cfg->feature_item.output_tuning_item, &m_output_tuning_item,
+		  4);
+	os_memcpy(&cfg->feature_item.test_main_ldo_setting,
+		  &m_test_main_ldo_setting, 4);
+
+	os_memcpy(&cfg->timeout_item.power_wait_time, &m_power_wait_time, 4);
+	os_memcpy(&cfg->timeout_item.test_write_data_timeout,
+		  &m_test_write_data_timeout, 4);
+	os_memcpy(&cfg->timeout_item.test_read_data_timeout,
+		  &m_test_read_data_timeout, 4);
+	os_memcpy(&cfg->timeout_item.test_non_data_timeout,
+		  &m_test_non_data_timeout, 4);
+	os_memcpy(&cfg->timeout_item.test_r1b_data_timeout,
+		  &m_test_r1b_data_timeout, 4);
+	os_memcpy(&cfg->timeout_item.test_card_init_timeout,
+		  &m_test_card_init_timeout, 4);
+
+#if BHT_LINUX_ENABLE_RTD3
+	os_memcpy(&cfg->feature_item.psd_mode, &m_psd_mode, 4);
+#else
+	cfg->feature_item.psd_mode.enable_rtd3 = 0;
+
+	/* software control */
+	cfg->feature_item.psd_mode.rtd3_ctrl_mode = 1;
+#endif
+
+}
+
+/* currently don't implement */
+void os_set_sdio_val(void *p, u8 val, bool need_set_did)
+{
+}
+
+void os_rtd3_req_wait_wake(void *pdx)
+{
+
+}
diff --git a/drivers/scsi/bht/linux_os/linux_api.h b/drivers/scsi/bht/linux_os/linux_api.h
new file mode 100644
index 000000000000..960f9cc4dd5d
--- /dev/null
+++ b/drivers/scsi/bht/linux_os/linux_api.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014 BHT Inc.
+ *
+ * File Name: linux_api.h
+ *
+ * Abstract: Include apis for Linux
+ *
+ * Version: 1.00
+ *
+ * Author: Peter.Guo
+ *
+ * Environment:	Linux
+ *
+ * History:
+ *
+ * 5/20/2015		Creation	Peter.Guo
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/vmalloc.h>
+
+#include "../include/basic.h"
+#define TASK_STATUS_IDLE 0
+#define TASK_STATUS_OCCUR 1
+
+typedef struct completion linux_completion_t;
+
+typedef struct {
+	spinlock_t lock;
+	/* add for list */
+	struct list_head list_hd;
+	atomic_t cnt;
+} linux_list_t;
+
+typedef struct {
+	struct task_struct *pthread;
+	bool pending_lock;
+	bool freeze;
+	linux_completion_t break_pending;
+} thread_t;
+
+typedef void (*timer_cb_t)(PVOID, PVOID);
+
+typedef struct {
+	void *timer;
+	timer_cb_t timer_callback;
+} linux_timer_t;
+
+typedef struct {
+	wait_queue_head_t evt_control;
+	/* Command control thread - command in flag */
+	atomic_t evt_comming;
+	unsigned long evt_flag;
+	spinlock_t lock;
+} t_linux_event;
+
+typedef struct {
+	u8 status;
+	u32 id;
+	u8 shared_task_cnt;
+} win_task_t;
+
+typedef struct {
+	win_task_t tasks[10];
+	u32 task_cnt;
+} task_mgr_t;
+
+typedef struct {
+	struct timer_list timer;
+	thread_t thread;
+
+	task_mgr_t task_mgr;
+	t_linux_event event;
+	spinlock_t lock;
+	dma_desc_buf_t dma_info;
+	struct Scsi_Host *scsi_host;
+	byte *virt_buff;
+	/*
+	 * If os_get_sg_list called successful, set this flag to 1.
+	 * and need call dma_unmap_sg when transfer done
+	 */
+	bool dma_mapped;
+	int rt_pm_cnt;
+	bool thread_terminate_stop;
+} t_linux_os_struct;
+
+void os_free_sg_list(void *p, scsi_srb *Srb);
diff --git a/drivers/scsi/bht/linux_os/linux_base.c b/drivers/scsi/bht/linux_os/linux_base.c
new file mode 100644
index 000000000000..a651141b7608
--- /dev/null
+++ b/drivers/scsi/bht/linux_os/linux_base.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 BHT Inc.
+ *
+ * File Name: linux_base.c
+ *
+ * Abstract: Driver Entry  for Linux
+ *
+ * Version: 1.00
+ *
+ * Author: Peter.Guo
+ *
+ * Environment:	Linux
+ *
+ * History:
+ *
+ * 5/20/2015		Creation	Peter.Guo
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "../include/basic.h"
+#include "../include/debug.h"
+#include "../include/reqapi.h"
+#include "../include/hostapi.h"
+#include "../include/hostvenapi.h"
+
+#include "linux_scsi.h"
+#if BHT_LINUX_ENABLE_RTD3
+#include <linux/pm_runtime.h>
+#endif
+
+#define VENDOR_O2MICRO 0x1217
+#define BHT_SD_MAX_DEV	8
+#define BHT_USE_PCI_MSI 1
+#define SRB_MIN_REQ	64
+
+#define INPUT_TUNING_PASS_WINDOW	0x44
+
+extern u32 g_dbg_module;
+extern u32 g_dbg_feature;
+extern u32 g_dbg_ctrl;
+
+extern uint m_sd_3v3_clk_driver_strength;
+extern uint m_sd_3v3_cmddata_driver_strength;
+extern uint m_sd_1v8_clk_driver_strength;
+extern uint m_sd_1v8_cmddata_driver_strength;
+extern uint m_ram_ema;
+extern uint m_vdd1_vdd2_source;
+extern uint m_vdd18_debounce_time;
+extern uint m_ssc_enable;
+extern uint m_cnfg_drv;
+extern uint m_cnfg_trm_code_tx;
+extern uint m_cnfg_trm_code_rx;
+extern uint m_cnfg_rint_code;
+
+struct kmem_cache *bht_srb_ext_cachep;
+mempool_t *bht_sd_mem_pool;
+
+typedef struct {
+	int used;
+	bht_dev_ext_t data;
+} bht_sd_slot_t;
+
+static bht_sd_slot_t bht_slot[BHT_SD_MAX_DEV];
+
+static bht_sd_slot_t *bht_slot_getfree(void)
+{
+	int i = 0;
+
+	for (i = 0; i < BHT_SD_MAX_DEV; i++) {
+		if (bht_slot[i].used == 0) {
+			os_memset(&bht_slot[i].data, 0, sizeof(bht_dev_ext_t));
+			DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT,
+				NOT_TO_RAM, "Get Slot=%p i=%d\n", &bht_slot[i],
+				i);
+			return &bht_slot[i];
+		}
+	}
+	return NULL;
+}
+
+static void bht_global_init(void)
+{
+	int i = 0;
+
+	for (i = 0; i < BHT_SD_MAX_DEV; i++) {
+		os_memset(&bht_slot[i].data, 0, sizeof(bht_dev_ext_t));
+		bht_slot[i].used = 0;
+	}
+
+	DbgRamInit();
+#if DBG || _DEBUG
+	g_dbg_module = DBG_MODULE_CONTROL;
+	g_dbg_feature = DBG_FEATURE_CONTROL;
+	g_dbg_ctrl = DBG_CTRL_CONTROL;
+
+	DbgErr("Dbg Module=0x%08X feature=0x%08X control=0x%08X\n",
+	       g_dbg_module, g_dbg_feature, g_dbg_ctrl);
+#else
+	g_dbg_module = 0;
+	g_dbg_feature = 0;
+	g_dbg_ctrl = 0;
+#endif
+	cfgmng_init();
+}
+
+static void bht_global_uninit(void)
+{
+	int i;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"bht sd free slots\n");
+	for (i = 0; i < BHT_SD_MAX_DEV; i++) {
+		os_memset(&bht_slot[i].data, 0, sizeof(bht_dev_ext_t));
+		bht_slot[i].used = 0;
+	}
+
+	DbgRamFree();
+}
+
+struct pci_device_id bht_id_table[] = {
+	{
+	 /* SDS0 */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8420,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+
+	{
+	 /* SDS1 */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8421,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+
+	{
+	 /* Fujin2A Fjin2B */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8520,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+
+	{
+	 /* Searbird */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8620,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+
+	{
+	 /* SeaBird */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8621,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 /* 0x8720 to 0x8723 is SeaEagle2 */
+	 .device = 0x8720,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8721,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8722,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8723,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 /* 0x8750,0x8751 and 0x8740 is SandStorm2 */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8750,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8751,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x8740,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 /* gg8 */
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x9860,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x9861,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x9862,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{
+	 .vendor = VENDOR_O2MICRO,
+	 .device = 0x9863,
+	 .subvendor = PCI_ANY_ID,
+	 .subdevice = PCI_ANY_ID,
+	  },
+	{ 0, 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, bht_id_table);
+
+/* ----------BIOS GUIDE setting part---------- */
+
+void bht_bios_setting(bht_dev_ext_t *pdx)
+{
+	sd_host_t *host = &pdx->host;
+	cfg_item_t *cfg = pdx->cfg;
+	u32 regaddr;
+	u32 regval;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM,
+		"Enter %s chip type = 0x%x\n", __func__, host->chip_type);
+
+	switch (host->chip_type) {
+	case CHIP_GG8:
+		regval = pci_readl(host, 0x300);
+		regval &= ~(0xff);
+		regval |= 0x33;
+		pci_writel(host, 0x300, regval);
+		break;
+	case CHIP_SEAEAGLE2:
+		/* we need set this to enable hs400 function */
+		if (cfg->card_item.emmc_mode.enable_force_hs400)
+			pci_orl(host, 0x3f8, BIT13);
+		else
+			pci_andl(host, 0x3f8, ~BIT13);
+		break;
+	case CHIP_SDS2_SD0:
+	case CHIP_SDS2_SD1:
+		/* pcie & pm #2, should be the first operation after detect host type */
+		pci_writel(host, 0x438, m_ram_ema);
+
+		/* uhsi #1 */
+		pci_orl(host, 0x3e4, 0x80000000);
+		/* uhsi #3 */
+		regaddr = (host->chip_type == CHIP_SDS2_SD0) ? 0x300 : 0x380;
+		pci_writel(host, regaddr, INPUT_TUNING_PASS_WINDOW);
+
+		/* ushi #4 */
+		regval = pci_readl(host, 0x3f8);
+		regval &= ~(0x03u << 19);
+		regval |= (m_vdd18_debounce_time << 19);
+		pci_orl(host, 0x3f8, regval);
+
+		/* uhsi #6 */
+		regaddr = host->chip_type == CHIP_SDS2_SD0 ? 0x304 : 0x384;
+		regval = pci_readl(host, regaddr);
+		regval &= 0xffff8881;
+		regval |= ((m_sd_3v3_clk_driver_strength & 0x07) << 12 |
+			   (m_sd_3v3_cmddata_driver_strength & 0x07) << 8 |
+			   (m_sd_1v8_clk_driver_strength & 0x07) << 4 |
+			   (m_sd_1v8_cmddata_driver_strength & 0x07) << 1 | 1);
+		pci_writel(host, regaddr, regval);
+
+		/* ushii #1  */
+		regval = pci_readl(host, 0x3fc);
+		regval &= ~0x7f000000;
+		regval |= ((m_cnfg_drv & 0x7f) << 24);
+		pci_writel(host, 0x3fc, regval);
+
+		regval = pci_readl(host, 0x400);
+		regval &= ~(0xfff);
+		regval |= ((m_cnfg_trm_code_tx & 0x0f) << 8);
+		regval |= ((m_cnfg_trm_code_rx & 0x0f) << 4);
+		regval |= (m_cnfg_rint_code & 0x0f);
+		pci_writel(host, 0x400, regval);
+
+		/* uhsii 2 */
+		switch (m_vdd1_vdd2_source) {
+		case 0:
+			regval = pci_readl(host, 0x508);
+			/* bit[25:24] = 2'b00 */
+			regval &= ~(0x3 << 24);
+			/* bit[9:8] = 2'b00 */
+			regval &= ~(0x3 << 8);
+			regval &= ~(0xf << 2);
+			/* bit[5:2]=4'b0101 */
+			regval |= 0x14;
+			break;
+		case 1:
+			regval = pci_readl(host, 0x508);
+			/* bit[25:24] = 2'b11 */
+			regval |= (0x3 << 24);
+			/* bit[9:8] = 2'b11 */
+			regval |= (0x3 << 8);
+			/* bit[5:2]=4'b1111 */
+			regval |= (0xf << 2);
+			break;
+		case 2:
+			regval = pci_readl(host, 0x508);
+			/* bit[25:24] = 2'b00 */
+			regval &= ~(0x3 << 24);
+			/* bit[25:24] = 2'b10 */
+			regval |= (0x2 << 24);
+			/* bit[9:8] = 2'b00 */
+			regval &= ~(0x3 << 8);
+			/* bit[9:8] = 2'b10 */
+			regval |= (0x2 << 8);
+			regval &= ~(0xf << 2);
+			/* bit[5:2]=4'b1010 */
+			regval |= 0x28;
+			break;
+		default:
+			break;
+		}
+		pci_writel(host, 0x508, regval);
+
+		/* clock & generator #1 */
+		if (m_ssc_enable != 0) {
+			pci_writel(host, 0x420, 0xa0005e1e);
+			pci_writel(host, 0x424, 0x61180000);
+		} else {
+			pci_writel(host, 0x420, 0xa000561e);
+			pci_writel(host, 0x424, 0x21180000);
+		}
+		/* clock & generator #2 */
+		pci_writel(host, 0x428, 0x01f0f0fa);
+		pci_writel(host, 0x42c, 0x01f0f0fa);
+		break;
+	default:
+		break;
+	}
+
+}
+
+#if BHT_LINUX_ENABLE_RTD3
+
+static void bht_sd_runtime_pm_allow(bht_dev_ext_t *pdx, struct device *dev)
+{
+	if (pdx->pm_state.rtd3_en == FALSE)
+		return;
+
+	pm_runtime_put_noidle(dev);
+	pm_runtime_allow(dev);
+	pm_runtime_set_autosuspend_delay(dev,
+					 pdx->cfg->feature_item.psd_mode.disk_idle_time_s * 1000);
+	pm_runtime_use_autosuspend(dev);
+	pm_suspend_ignore_children(dev, 1);
+}
+
+static void bht_sd_runtime_pm_forbid(bht_dev_ext_t *pdx, struct device *dev)
+{
+
+	if (pdx->pm_state.rtd3_en == FALSE)
+		return;
+
+	pm_runtime_forbid(dev);
+	pm_runtime_get_noresume(dev);
+}
+
+static int bht_sd_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+	int ret = 0;
+	bht_sd_slot_t *slot = pci_get_drvdata(pdev);
+	bht_dev_ext_t *pdx = 0;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd rt supspend begin\n");
+
+	if (slot == NULL || slot->used == FALSE)
+		goto exit;
+
+	pdx = &slot->data;
+	pdx->pm_state.rtd3_entered = TRUE;
+	req_enter_d3(pdx);
+
+exit:
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd rt supspend exit\n");
+	return ret;
+}
+
+static int bht_sd_runtime_resume(struct device *dev)
+{
+	struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+	int ret = 0;
+	bht_sd_slot_t *slot = pci_get_drvdata(pdev);
+	bht_dev_ext_t *pdx = 0;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd rt resume begin\n");
+
+	if (slot == NULL || slot->used == FALSE)
+		goto exit;
+
+	pdx = &slot->data;
+	req_enter_d0_sync(pdx);
+
+exit:
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd rt resume exit\n");
+	return ret;
+}
+
+static int bht_sd_runtime_idle(struct device *dev)
+{
+	return 0;
+}
+
+#endif
+
+static irqreturn_t bht_sd_irq(int irq, void *dev_id)
+{
+	irqreturn_t irqret = IRQ_NONE;
+	bht_dev_ext_t *pdx = (bht_dev_ext_t *) dev_id;
+	bool ret = FALSE;
+
+	ret = sdhci_irq(pdx);
+
+	if (pdx->host.pci_dev.use_msi)
+		irqret |= IRQ_HANDLED;
+	else {
+		if (ret)
+			irqret |= IRQ_HANDLED;
+	}
+
+	return irqret;
+}
+
+#ifdef CONFIG_PM
+static int bht_sd_suspend(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+	bht_sd_slot_t *slot = pci_get_drvdata(pdev);
+	bht_dev_ext_t *pdx = &slot->data;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd suspend begin\n");
+
+	if (slot->used == 0)
+		goto exit;
+
+	pdx->pm_state.s3s4_entered = TRUE;
+	req_pre_enter_d3(pdx);
+	req_enter_d3(pdx);
+
+exit:
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd suspend end\n");
+	return 0;
+}
+
+static int bht_sd_resume(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+	bht_sd_slot_t *slot = pci_get_drvdata(pdev);
+	bht_dev_ext_t *pdx = &slot->data;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd resume begin\n");
+
+	if (slot->used == 0)
+		goto exit;
+
+	req_enter_d0(pdx);
+
+exit:
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM,
+		"BHT sd resume end\n");
+	return 0;
+}
+
+#else
+#define bht_sd_suspend	NULL
+#define bht_sd_resume	NULL
+#endif
+
+/*
+ * pci dev interrupt init
+ */
+static int bht_pci_init_irq(struct pci_dev *pdev, bht_dev_ext_t *pdx)
+{
+	int ret = -1;
+
+	pdx->host.pci_dev.use_msi = 0;
+
+	/* Firstly try MSI interrupt if enabled */
+#if defined(BHT_USE_PCI_MSI) && defined(CONFIG_PCI_MSI)
+	if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) {
+		int err;
+		/* PCI device support MSI interrupt mode. */
+		err = pci_enable_msi(pdev);
+		if (!err) {
+			pdx->host.pci_dev.irq = pdev->irq;
+			pdx->host.pci_dev.use_msi = 1;
+			DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT,
+				NOT_TO_RAM, "Use Msi irq irq=%d\n", pdev->irq);
+		} else {
+			DbgErr("MSI init failed %d\n", err);
+		}
+
+		if (request_irq(pdev->irq, bht_sd_irq, IRQF_SHARED,
+				(const char *)"bht-sd", (void *)pdx)) {
+			DbgErr("request msi irq %d for bht-sd failed",
+			       pdev->irq);
+			pci_disable_msi(pdev);
+			pdx->host.pci_dev.use_msi = 0;
+		}
+		ret = 0;
+		goto exit;
+	}
+#endif
+
+	pdx->host.pci_dev.irq = pdev->irq;
+	pdx->host.pci_dev.use_msi = 0;
+	if (request_irq(pdev->irq, bht_sd_irq, IRQF_SHARED,
+			(const char *)"bht-sd", (void *)pdx)) {
+		DbgErr("request irq %d for bht-sd failed", pdev->irq);
+		ret = -1;
+		goto exit;
+	}
+
+	ret = 0;
+
+exit:
+	return ret;
+
+}
+
+/*
+ * pci device basci info init
+ */
+static int bht_pci_init(struct pci_dev *pdev, bht_dev_ext_t *pdx)
+{
+	int ret = -1;
+	u32 bus_address;
+
+	pdx->host.pci_dev.pci_dev = pdev;
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		DbgErr("pci enable device failed\n");
+		return ret;
+	}
+
+	pci_set_master(pdev);
+
+	bus_address = pci_resource_start(pdev, 0);
+	if (pci_request_regions(pdev, "bht-sd") < 0) {
+		DbgErr("o2sd request_region failed(%d)\n", 0);
+		ret = -1;
+		goto exit;
+	}
+	/* BAR 0 and BAR1 */
+	bus_address = pci_resource_start(pdev, 0);
+	pdx->host.pci_dev.membase =
+	    (void *)ioremap(bus_address, pci_resource_len(pdev, 0));
+	bus_address = pci_resource_start(pdev, 1);
+	pdx->host.pci_dev.membase2 =
+	    (void *)ioremap(bus_address, pci_resource_len(pdev, 1));
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"membase1=%p membase2=%p\n", pdx->host.pci_dev.membase,
+		pdx->host.pci_dev.membase2);
+	pdx->host.vendor_id = pdev->vendor;
+	pdx->host.device_id = pdev->device;
+	pdx->host.revision_id = pdev->revision;
+
+	ret = 0;
+exit:
+	if (ret != 0)
+		pci_disable_device(pdev);
+	return ret;
+
+}
+
+/*
+ * pci dev dma resource init
+ */
+static bool bht_sd_dma_init(bht_dev_ext_t *pdx)
+{
+	bool ret = FALSE;
+	struct pci_dev *pdev = pdx->host.pci_dev.pci_dev;
+
+#if	CONFIG_X86_64
+
+#else
+
+	/* Set to 32Bit DMA currently */
+	cfg_dma_addr_range_dec(pdx->cfg, 0);
+#endif
+
+	if (pdx->cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address) {
+		if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))
+		    || dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)))
+			DbgErr("dma_set_mask 64bit failed\n");
+		else
+			goto next;
+	}
+
+	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))
+	    || dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)))
+		DbgErr("dma_set_mask 32bit failed\n");
+next:
+
+	if (pdx->signature != BHT_PDX_SIGNATURE) {
+		u32 max_req_numb = 0;
+		u32 dma_mode = 0;
+		u32 buf_size = 0x10;
+		u32 node_size = 0;
+
+		max_req_numb =
+		    pdx->cfg->host_item.test_tag_queue_capability.max_srb;
+		dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode;
+
+		if (cfg_dma_need_sdma_like_buffer(dma_mode) == TRUE)
+			node_size = MAX_SDMA_LIKE_MODE_NODE_BUF_SIZE;
+		else
+			node_size = MAX_NODE_BUF_SIZE;
+
+		buf_size = (max_req_numb + TQ_RESERVED_NODE_SIZE) * node_size;
+
+		while (1) {
+			/* preseved for ADMA2 API DMA buffer */
+			buf_size += MIN_DMA_API_BUF_SIZE;
+
+			/* less than minimum buf size  will failed. */
+			if (buf_size <
+			    (MIN_DMA_API_BUF_SIZE + MAX_NODE_BUF_SIZE)) {
+				DbgErr
+				    ("dma buffer less than min(0x%x),so Failed\n",
+				     (MIN_DMA_API_BUF_SIZE +
+				      MAX_NODE_BUF_SIZE));
+				break;
+			}
+			DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT,
+				NOT_TO_RAM, "os alloc dma buf len %x\n",
+				buf_size);
+			ret =
+			    os_alloc_dma_buffer(pdx, NULL, buf_size,
+						&pdx->dma_buff);
+			if (ret == TRUE)
+				break;
+			/* small requirement size */
+			buf_size = buf_size / 2;
+		}
+	}
+
+	return ret;
+
+}
+
+/*
+ * Release pci layer resource
+ */
+static void bht_sd_pci_release(bht_dev_ext_t *pdx)
+{
+	struct pci_dev *pdev = pdx->host.pci_dev.pci_dev;
+
+	free_irq(pdx->host.pci_dev.irq, pdx);
+	if (pdx->host.pci_dev.use_msi)
+		pci_disable_msi(pdev);
+	os_free_dma_buffer(pdx, &pdx->dma_buff);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+/*
+ * Update Linux Cfg Setting
+ */
+static void bht_linux_cfg_update(bht_dev_ext_t *pdx)
+{
+	u32 dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode;
+
+	switch (dma_mode) {
+	case CFG_TRANS_MODE_SDMA:
+	case CFG_TRANS_MODE_ADMA2_SDMA_LIKE:
+	case CFG_TRANS_MODE_PIO:
+	case CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE:
+		/* don't support tagqueue */
+		pdx->cfg->host_item.test_tag_queue_capability.max_srb = 2;
+		pdx->cfg->host_item.test_tag_queue_capability.enable_srb_merge =
+		    0;
+		break;
+	case CFG_TRANS_MODE_ADMA2:
+	case CFG_TRANS_MODE_ADMA3:
+		break;
+		/* Linux don't support adma3_sdma like mode */
+	case CFG_TRANS_MODE_ADMA3_SDMA_LIKE:
+		pdx->cfg->host_item.test_dma_mode_setting.dma_mode =
+		    CFG_TRANS_MODE_ADMA3;
+		break;
+	default:
+		break;
+	}
+	/* This function is add for degrade frequency for HS400 */
+	if ((*(u32 *) (&pdx->cfg->card_item.emmc_mode)) & BIT13)
+		pdx->cfg->dmdn_tbl[4] = 0x23040000;
+
+	bht_bios_setting(pdx);
+}
+
+/*
+ * pci device remove init
+ */
+static int bht_sd_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+	bht_sd_slot_t *slot = bht_slot_getfree();
+	bht_dev_ext_t *pdx = &slot->data;
+	int ret = -1;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"BHT sd probe begin\n");
+	if (slot == NULL) {
+		DbgErr("all slot is used\n");
+		goto exit;
+	}
+
+	ret = bht_pci_init(pdev, &slot->data);
+	if (ret)
+		goto exit;
+
+	pdx->host.pdx = pdx;
+	hostven_chip_type_check(&pdx->host);
+	/* Get the configuration pointer for the specified chip */
+	pdx->cfg = cfgmng_get((void *)pdx, pdx->host.chip_type, FALSE);
+	pdx->host.cfg = pdx->cfg;
+	/*
+	 * Linux Adjust the input parameter
+	 */
+	bht_linux_cfg_update(pdx);
+	os_memset(&pdx->testcase, 0, sizeof(testcase_t));
+	pdx->testcase.test_type = 0;
+
+	if (bht_sd_dma_init(pdx) == FALSE) {
+		ret = -1;
+		DbgErr("DMA Init failed\n");
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		goto exit;
+	}
+
+	req_global_init(pdx);
+	ret = bht_pci_init_irq(pdev, pdx);
+	if (ret) {
+		ret = -1;
+		DbgErr("Irq Init failed\n");
+		os_free_dma_buffer(pdx, &pdx->dma_buff);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		goto exit;
+	}
+
+	if (pdx->card.card_present)
+		os_set_event(&pdx->os, EVENT_CARD_CHG);
+
+	ret = 0;
+
+exit:
+	if (ret == 0) {
+		slot->used = 1;
+		pci_set_drvdata(pdev, slot);
+#if BHT_LINUX_ENABLE_RTD3
+		bht_sd_runtime_pm_allow(pdx, &pdev->dev);
+#endif
+	}
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"BHT sd probe end ret=%d\n", ret);
+	return ret;
+
+}
+
+/*
+ * pci device remove interface
+ * Thomas change for direct remove
+ */
+void bht_sd_remove(struct pci_dev *pdev)
+{
+	bht_sd_slot_t *slot = pci_get_drvdata(pdev);
+	bht_dev_ext_t *pdx = &slot->data;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"BHT sd remove begin\n");
+
+	if (slot->used == FALSE)
+		goto exit;
+
+#if BHT_LINUX_ENABLE_RTD3
+	bht_sd_runtime_pm_forbid(pdx, &pdev->dev);
+#endif
+	bht_scsi_uinit(pdx);
+
+	/*
+	 * Thomas comment because it cause AER 0xe6 and block the remove.
+	 *
+	 * if(os_stop_thread(&pdx->os, &pdx->os.thread) == FALSE)
+	 * {
+	 *      DbgErr("stop thread timeout\n");
+	 *      os_kill_thread(&pdx->os, &pdx->os.thread);
+	 * }
+	 *
+	 */
+
+	req_global_uninit(pdx);
+	bht_sd_pci_release(pdx);
+	os_memset(slot, 0, sizeof(bht_sd_slot_t));
+	pci_set_drvdata(pdev, NULL);
+
+exit:
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM,
+		"BHT sd remove end\n");
+
+}
+
+/*
+ * pci_dev shutdown interface
+ */
+static void bht_sd_shutdown(struct pci_dev *pdev)
+{
+	bht_sd_slot_t *slot = pci_get_drvdata(pdev);
+	bht_dev_ext_t *pdx = &slot->data;
+
+	if (slot->used == FALSE)
+		return;
+
+	if (os_stop_thread(&pdx->os, &pdx->os.thread) == FALSE) {
+		DbgErr("stop thread timeout\n");
+		os_kill_thread(&pdx->os, &pdx->os.thread);
+	}
+	req_global_uninit(pdx);
+	bht_sd_pci_release(pdx);
+	pci_set_drvdata(pdev, NULL);
+	os_memset(slot, 0, sizeof(bht_sd_slot_t));
+}
+
+static const struct dev_pm_ops bht_sd_pm_ops = {
+	.suspend = bht_sd_suspend,
+	.resume = bht_sd_resume,
+	.freeze = bht_sd_suspend,
+	.thaw = bht_sd_resume,
+	.poweroff = bht_sd_suspend,
+	.restore = bht_sd_resume,
+#if	BHT_LINUX_ENABLE_RTD3
+	SET_RUNTIME_PM_OPS(bht_sd_runtime_suspend,
+			   bht_sd_runtime_resume, bht_sd_runtime_idle)
+#endif
+};
+
+static struct pci_driver bht_sd_pci_driver = {
+	.name = "bht-sd",
+	.id_table = bht_id_table,
+	.probe = bht_sd_probe,
+	.remove = bht_sd_remove,
+	.shutdown = bht_sd_shutdown,
+	.driver = {
+		   .pm = &bht_sd_pm_ops },
+};
+
+/*
+ * Uninit linux global resource
+ */
+static void bht_linux_global_uninit(void)
+{
+	if (bht_sd_mem_pool)
+		mempool_destroy(bht_sd_mem_pool);
+
+	if (bht_srb_ext_cachep)
+		kmem_cache_destroy(bht_srb_ext_cachep);
+
+	bht_srb_ext_cachep = NULL;
+	bht_sd_mem_pool = NULL;
+}
+
+/*
+ * init linux global resource
+ */
+static int bht_linux_global_init(void)
+{
+	int ret = 0;
+
+	bht_srb_ext_cachep = NULL;
+	bht_sd_mem_pool = NULL;
+
+	bht_srb_ext_cachep =
+	    kmem_cache_create("bht-sd-srbext", sizeof(srb_ext_t), 0,
+			      SLAB_HWCACHE_ALIGN, NULL);
+
+	if (bht_srb_ext_cachep == NULL) {
+		ret = -1;
+		DbgErr("Allocate Memory cache failed\n");
+		goto exit;
+	}
+
+	bht_sd_mem_pool =
+	    mempool_create(LINUX_SCSI_MAX_QUEUE_DPETH * 2, mempool_alloc_slab,
+			   mempool_free_slab, bht_srb_ext_cachep);
+exit:
+	if (bht_sd_mem_pool == NULL) {
+		DbgErr("Allocate Memory Pool failed\n");
+		bht_linux_global_uninit();
+		ret = -1;
+	}
+
+	return ret;
+
+}
+
+/*
+ * bht_sd_init - First called function when driver load/insert
+ */
+static int __init bht_sd_init(void)
+{
+	int ret = 0;
+
+	ret = bht_linux_global_init();
+	if (ret)
+		goto exit;
+
+	bht_global_init();
+
+	ret = pci_register_driver(&bht_sd_pci_driver);
+
+exit:
+	if (ret != 0) {
+		bht_linux_global_uninit();
+		DbgErr("Register bht pci driver failed\n");
+		bht_global_uninit();
+	}
+
+	return ret;
+}
+
+/*
+ * bht_sd_exit - First called when dirver unload/remove
+ */
+static void __exit bht_sd_exit(void)
+{
+	pci_unregister_driver(&bht_sd_pci_driver);
+	bht_linux_global_uninit();
+	bht_global_uninit();
+}
+
+/*
+ * Driver parameter and Information
+ */
+
+/* set first call function name when driver insert/load. */
+module_init(bht_sd_init);
+/* set first call function name when driver remove/unload. */
+module_exit(bht_sd_exit);
+MODULE_DESCRIPTION("BayHub SD Card reader device driver");
+MODULE_AUTHOR("BayHub Inc.");
+#if BHT_LINUX_ENABLE_RTD3
+MODULE_LICENSE("GPL");
+#else
+MODULE_LICENSE("GPL");
+#endif
+
+MODULE_VERSION("v1008_00_00");
diff --git a/drivers/scsi/bht/linux_os/linux_scsi.c b/drivers/scsi/bht/linux_os/linux_scsi.c
new file mode 100644
index 000000000000..64b34cc7ad56
--- /dev/null
+++ b/drivers/scsi/bht/linux_os/linux_scsi.c
@@ -0,0 +1,1076 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015 BHT Inc.
+ *
+ * File Name: linux_scsi.c
+ *
+ * Abstract: SCSI function
+ *
+ * Version: 1.00
+ *
+ * Author: Peter.Guo
+ *
+ * Environment:	Linux
+ *
+ * History:
+ *
+ * 5/20/2015		Creation	Peter.Guo
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/mempool.h>
+
+#include "../include/basic.h"
+#include "../include/reqapi.h"
+#include "../include/funcapi.h"
+#include "../include/tqapi.h"
+#include "../include/cardapi.h"
+#include "../include/hostapi.h"
+#include "../include/debug.h"
+#include "../include/util.h"
+#include "linux_scsi.h"
+#include "../tagqueue/tq_trans_api.h"
+#include "../include/cmdhandler.h"
+
+extern struct scsi_host_template bht_scsi_template;
+
+static srb_ext_t *bht_scsi_alloc_srb_ext(bht_dev_ext_t *pdx,
+					 struct scsi_cmnd *srb)
+{
+	/* todo use cache to improve performance */
+	srb_ext_t *srb_ext = mempool_alloc(bht_sd_mem_pool, GFP_ATOMIC);
+
+	if (srb_ext == NULL) {
+		DbgErr("alloc srbext is null\n");
+
+		goto exit;
+	}
+	memset(srb_ext, 0, sizeof(srb_ext_t));
+	srb_ext->psrb = srb;
+exit:
+	return srb_ext;
+}
+
+static void bht_scsi_free_srb_ext(bht_dev_ext_t *pdx, srb_ext_t *srb_ext)
+{
+	if (srb_ext != NULL)
+		mempool_free(srb_ext, bht_sd_mem_pool);
+}
+
+/*
+ *	The function to register and Init scsi host
+ */
+bool bht_scsi_init(bht_dev_ext_t *pdx, struct device *dev)
+{
+	bool ret = FALSE;
+	int error = 0;
+
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM,
+		"BHT scsi init begin\n");
+
+	if (pdx->scsi_init_flag == 1) {
+		DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM,
+			"BHT scsi initialized, and return\n");
+		return TRUE;
+	}
+
+	bht_scsi_template.can_queue = bht_scsi_template.cmd_per_lun =
+	    os_min(pdx->cfg->host_item.test_tag_queue_capability.max_srb,
+		   LINUX_SCSI_MAX_QUEUE_DPETH);
+	pdx->os.scsi_host =
+	    scsi_host_alloc(&bht_scsi_template, sizeof(unsigned long));
+	if (!pdx->os.scsi_host) {
+		DbgErr("Unable to register controller with SCSI subsystem\n");
+		goto exit;
+	}
+
+	pdx->dev = dev;
+	pdx->os.scsi_host->hostdata[0] = (unsigned long)pdx;
+	pdx->os.scsi_host->irq = pdx->host.pci_dev.irq;
+	pdx->os.scsi_host->base = (unsigned long)pdx->host.pci_dev.membase;
+	pdx->os.scsi_host->max_id = 1;
+	pdx->os.scsi_host->max_lun = 1;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)
+	pdx->os.scsi_host->no_write_same = 1;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
+	pdx->os.scsi_host->use_clustering = 1;
+#endif
+	pdx->os.scsi_host->max_sectors = bht_scsi_template.max_sectors;
+
+	error = scsi_add_host(pdx->os.scsi_host, dev);
+	if (error) {
+		DbgErr("scsi add host failed\n");
+		return FALSE;
+	}
+	scsi_scan_host(pdx->os.scsi_host);
+	pdx->scsi_init_flag = 1;
+	ret = TRUE;
+
+exit:
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM,
+		"BHT scsi init end\n");
+	return ret;
+}
+
+/*
+ *	The function to remove scsi host
+ */
+void bht_scsi_uinit(bht_dev_ext_t *pdx)
+{
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM,
+		"BHT scsi uinit begin\n");
+	if (pdx->scsi_init_flag == 0) {
+		DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM,
+			"BHT scsi uinit excuted,and return\n");
+		return;
+	}
+	if ((pdx != NULL) && (&pdx->os != NULL) && (pdx->os.scsi_host != NULL)) {
+		scsi_remove_host(pdx->os.scsi_host);
+		scsi_host_put(pdx->os.scsi_host);
+		pdx->scsi_init_flag = 0;
+	}
+	DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM,
+		"BHT scsi uinit end\n");
+}
+
+/*
+ *	This function is used to save the sense code and fill sense buffer
+ */
+static void bht_scsi_set_sensecode(bht_dev_ext_t *pdx, struct scsi_cmnd *srb,
+				   byte sense_key, byte sense_code)
+{
+	PSENSE_DATA senseBuffer = (PSENSE_DATA) srb->sense_buffer;
+
+	pdx->scsi.sense_key = sense_key;
+	pdx->scsi.sense_code = sense_code;
+
+	if (senseBuffer != NULL && sense_key != 0) {
+		senseBuffer->ErrorCode = 0x70;
+		senseBuffer->AdditionalSenseLength = 0xb;
+		senseBuffer->SenseKey = pdx->scsi.sense_key;
+		senseBuffer->AdditionalSenseCode = pdx->scsi.sense_code;
+		senseBuffer->AdditionalSenseCodeQualifier = 0;
+		DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+			"%s sense=%d code=%d\n", __func__, sense_key,
+			sense_code);
+	}
+
+}
+
+/*
+ *	Handle Scsi command Test unit ready
+ */
+static void bht_scsi_test_unit_ready(bht_dev_ext_t *pdx, struct scsi_cmnd *srb)
+{
+	if (req_card_ready(pdx)) {
+		srb->result = GOOD;
+	} else {
+		srb->result = CHECK_CONDITION;
+		bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY,
+				       SCSI_ADSENSE_NO_MEDIA_IN_DEVICE);
+	}
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		" %s,  SrbResult:%x\n", __func__, srb->result);
+}
+
+/*
+ *	Handle SCSI Command Request Sense
+ */
+static void bht_scsi_request_sense(bht_dev_ext_t *pdx, struct scsi_cmnd *srb)
+{
+
+	PSENSE_DATA senseBuffer = (PSENSE_DATA) srb->sense_buffer;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+	if (senseBuffer == NULL) {
+		srb->result = CHECK_CONDITION;
+	} else {
+		srb->result = GOOD;
+		senseBuffer->ErrorCode = 0x70;
+		senseBuffer->AdditionalSenseLength = 0xb;
+		senseBuffer->SenseKey = pdx->scsi.sense_key;
+		senseBuffer->AdditionalSenseCode = pdx->scsi.sense_code;
+		senseBuffer->AdditionalSenseCodeQualifier = 0;
+		bht_scsi_set_sensecode(pdx, srb, 0, 0);
+	}
+
+	scsi_sg_copy_from_buffer(srb, (byte *) senseBuffer,
+				 os_min(srb->sdb.length, sizeof(SENSE_DATA)));
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s, SrbResult=%d\n", __func__, srb->result);
+}
+
+/*
+ *	Handle Mode Sense scsi command
+ */
+static void bht_scsi_exec_modesense(bht_dev_ext_t *pdx, struct scsi_cmnd *srb)
+{
+	MODE_PAGE_HEADER hdr;
+	MODE_PAGE_8 pg_8;
+	MODEPAGE19 pg_9;
+	byte pmdata[512];
+	byte mode_code;
+	sd_card_t *card = &pdx->card;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+	memset(pmdata, 0, 512);
+
+	hdr.DataLength = 3;
+	hdr.MediumType = 0x00;
+	hdr.Reserved = 0;
+
+	if (card->write_protected)
+		hdr.Reserved |= 0x80;
+
+	hdr.BlockDescLength = 0;
+	pg_8.PageCode = 0x08;
+	pg_8.PageSavable = 1;
+	pg_8.PageLength = 0x12;
+
+	pg_8.WriteCacheEnable = 1;
+	pg_8.DisablePrefetchTransfer[0] = 0xFF;
+	pg_8.DisablePrefetchTransfer[1] = 0xFF;
+	pg_8.MaximumPrefetch[0] = 0xFF;
+	pg_8.MaximumPrefetch[1] = 0xFF;
+	pg_8.MaximumPrefetchCeiling[0] = 0xFF;
+	pg_8.MaximumPrefetchCeiling[1] = 0xFF;
+	pg_8.NumberofCacheSegments = 0x20;
+
+	pg_9.PageCode = 0x19;
+	pg_9.PageSavable = 1;
+	pg_9.PageLength = 0x04;
+	pg_9.ProtocolIdentifier = 0x0A;
+	pg_9.SynchronousTransferTimeout[0] = 0x00;
+	pg_9.SynchronousTransferTimeout[1] = 0x01;
+
+	mode_code = srb->cmnd[2];
+
+	switch (mode_code) {
+	case 0x08:
+		hdr.DataLength += sizeof(MODE_PAGE_8);
+		memcpy(pmdata, &hdr, sizeof(MODE_PAGE_HEADER));
+		memcpy(pmdata + sizeof(MODE_PAGE_HEADER), &pg_8,
+		       sizeof(MODE_PAGE_8));
+		break;
+	case 0x19:
+		hdr.DataLength += sizeof(MODEPAGE19);
+		memcpy(pmdata, &hdr, sizeof(MODE_PAGE_HEADER));
+		memcpy(pmdata + sizeof(MODE_PAGE_HEADER), &pg_9,
+		       sizeof(MODEPAGE19));
+		break;
+	case 0x3F:
+		hdr.DataLength += (sizeof(MODEPAGE19) + sizeof(MODE_PAGE_8));
+		memcpy(pmdata, &hdr, sizeof(MODE_PAGE_HEADER));
+		memcpy(pmdata + sizeof(MODE_PAGE_HEADER), &pg_8,
+		       sizeof(MODE_PAGE_8));
+		memcpy(pmdata + sizeof(MODE_PAGE_HEADER) + sizeof(MODE_PAGE_8),
+		       &pg_9, sizeof(MODEPAGE19));
+		break;
+	default:
+		break;
+	}
+
+	srb->result = GOOD;
+	scsi_sg_copy_from_buffer(srb, pmdata, os_min(srb->sdb.length, 512));
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s\n", __func__);
+}
+
+/*
+ *	Handle SCSI Command Allow Removalabe
+ */
+static void bht_scsi_allow_removal(bht_dev_ext_t *pdx, struct scsi_cmnd *srb)
+{
+	byte prevent = srb->cmnd[4] & 0x3;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s prevent=%d\n", __func__, prevent);
+	if (req_card_ready(pdx) == FALSE) {
+		srb->result = CHECK_CONDITION;
+
+		bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY,
+				       SCSI_ADSENSE_NO_MEDIA_IN_DEVICE);
+	} else {
+		srb->result = GOOD;
+		if (prevent == 0x01 || prevent == 0x11)
+			pdx->scsi.prevent_eject = 1;
+		else
+			pdx->scsi.prevent_eject = 0;
+	}
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s\n", __func__);
+}
+
+static bool bht_scsi_error_handle(bht_dev_ext_t *pdx, struct scsi_cmnd *srb,
+				  e_req_result result)
+{
+	bool ret = TRUE;
+
+	if (result == REQ_RESULT_PENDING) {
+	} else if (result == REQ_RESULT_QUEUE_BUSY) {
+	} else if (result == REQ_RESULT_NO_CARD) {
+		srb->result = CHECK_CONDITION;
+		bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY,
+				       SCSI_ADSENSE_NO_MEDIA_IN_DEVICE);
+	} else if (result == REQ_RESULT_PROTECTED) {
+		srb->result = CHECK_CONDITION;
+		bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_DATA_PROTECT,
+				       SCSI_ADSENSE_WRITE_PROTECT);
+	} else {
+		srb->result = CHECK_CONDITION;
+		bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_ILLEGAL_REQUEST,
+				       SCSI_ADSENSE_ILLEGAL_BLOCK);
+	}
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		" %s, SrbResult:%x\n", __func__, srb->result);
+	return ret;
+}
+
+/*
+ *	Fill Capacity Data
+ */
+static void bht_scsi_get_capacity(bht_dev_ext_t *pdx, u32 secnt,
+				  struct scsi_cmnd *srb)
+{
+	READ_CAPACITY_DATA capacity;
+	PREAD_CAPACITY_DATA pcapacity = &capacity;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+
+	memset(pcapacity, 0, sizeof(READ_CAPACITY_DATA));
+	/* Big Endian for Capacity data */
+	pcapacity->LogicalBlockAddress = swapu32((secnt > 0) ? (secnt - 1) : 0);
+	/* Big Endian for Capacity data */
+	pcapacity->BytesPerBlock = swapu32(SD_BLOCK_LEN);
+	srb->result = GOOD;
+	scsi_sg_copy_from_buffer(srb, pcapacity,
+				 os_min(srb->sdb.length,
+					sizeof(READ_CAPACITY_DATA)));
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s, capacity:%x\n", __func__,
+		pcapacity->LogicalBlockAddress);
+}
+
+/*
+ *	Fill Inquriy Data function
+ */
+static void bht_scsi_get_inquiry(bht_dev_ext_t *pdx, e_card_type card_type,
+				 struct scsi_cmnd *srb)
+{
+	_INQUIRYDATA inqdata;
+	_INQUIRYDATA *pinqdata = &inqdata;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+
+	os_memset(pinqdata, 0, sizeof(_INQUIRYDATA));
+	/* TYPE_DISK; */
+	pinqdata->DeviceType = 0x00;
+
+	pinqdata->Versions = 0x00;
+	pinqdata->ResponseDataFormat = 0x02;
+	pinqdata->AdditionalLength = sizeof(_INQUIRYDATA) - 4;
+	strncpy((char *)(pinqdata->VendorId), "BHT-SD ", 8);
+	switch (card_type) {
+	case CARD_SDIO:
+		strncpy((char *)(pinqdata->ProductId), "SDIO ", 5);
+		break;
+	case CARD_SD:
+	case CARD_UHS2:
+		strncpy((char *)(pinqdata->ProductId), "SD ", 3);
+		break;
+	case CARD_EMMC:
+	case CARD_MMC:
+		strncpy((char *)(pinqdata->ProductId), "MMC ", 4);
+		break;
+	case CARD_TEST:
+		strncpy((char *)(pinqdata->ProductId), "TestCR ", 6);
+		break;
+	default:
+		strncpy((char *)(pinqdata->ProductId), "CR              ", 16);
+		break;
+	}
+	strncpy((char *)(pinqdata->ProductRevisionLevel), "104a", 4);
+	srb->result = GOOD;
+	scsi_sg_copy_from_buffer(srb, pinqdata,
+				 os_min(srb->sdb.length, sizeof(_INQUIRYDATA)));
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s\n", __func__);
+}
+
+/*
+ *	Callback for basci scsi command
+ */
+static void bht_scsi_srb_basic_cmd_done(void *p, void *srb_ext)
+{
+	srb_ext_t *p_srb_ext = srb_ext;
+	bht_dev_ext_t *pdx = p;
+	struct scsi_cmnd *srb = p_srb_ext->psrb;
+	sd_card_t *card = &pdx->card;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+	if (srb == NULL) {
+		pdx->p_srb_ext = NULL;
+		goto exit;
+	}
+	if (p_srb_ext->req.result != REQ_RESULT_OK) {
+		srb->result = CHECK_CONDITION;
+		bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY,
+				       SCSI_ADSENSE_NO_MEDIA_IN_DEVICE);
+		goto end;
+	}
+
+	switch (srb->cmnd[0]) {
+	case INQUIRY:
+		{
+			bht_scsi_get_inquiry(pdx, card->card_type, srb);
+			break;
+		}
+	case READ_CAPACITY:
+		{
+			bht_scsi_get_capacity(pdx, (u32) card->sec_count, srb);
+			break;
+		}
+	case START_STOP:
+		{
+			srb->result = GOOD;
+			break;
+		}
+	default:
+		break;
+	}
+
+end:
+	bht_scsi_free_srb_ext(pdx, srb_ext);
+	pdx->p_srb_ext = NULL;
+
+	scsi_done(srb);
+exit:
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s\n", __func__);
+}
+
+/*
+ *	handle start stop scsi command
+ *	return true means done while return false means pending
+ */
+static bool bht_scsi_load_unload(bht_dev_ext_t *pdx, struct scsi_cmnd *srb,
+				 bool *busy)
+{
+	bool ret = TRUE;
+	byte load = srb->cmnd[4] & 0x01;
+	byte loej = srb->cmnd[4] & 0x02;
+	e_req_result result = REQ_RESULT_OK;
+	srb_ext_t *srb_ext = NULL;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s load=%d loej=%d\n", __func__, load, loej);
+
+	if (loej == 0) {
+		srb->result = GOOD;
+		goto exit;
+	}
+
+	if (load) {
+		/*
+		 * For Load command we don't need use thread
+		 * because card access module can Init card
+		 */
+
+		pdx->scsi.scsi_eject = 0;
+		srb->result = GOOD;
+
+		if (req_card_ready(pdx) == FALSE) {
+			srb->result = CHECK_CONDITION;
+			DbgErr("Scsi load failed\n");
+			bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY,
+					       SCSI_ADSENSE_NO_MEDIA_IN_DEVICE);
+		}
+		goto exit;
+	} else {
+		/* already ejected */
+		if (pdx->scsi.scsi_eject) {
+			srb->result = GOOD;
+			goto exit;
+		}
+		/* eject is not allowed */
+		else if (pdx->scsi.prevent_eject) {
+			srb->result = CHECK_CONDITION;
+			bht_scsi_set_sensecode(pdx, srb,
+					       SCSI_SENSE_ILLEGAL_REQUEST,
+					       SCSI_ADSENSE_ILLEGAL_COMMAND);
+			goto exit;
+		}
+	}
+
+	/* below code is for unload operation       */
+	srb_ext = bht_scsi_alloc_srb_ext(pdx, srb);
+	if (srb_ext == NULL) {
+		result = REQ_RESULT_ABORT;
+		goto exit;
+	}
+	srb_ext->req.data_dir = DATA_DIR_NONE;
+	srb_ext->req.srb_buff = NULL;
+	srb_ext->req.srb_sg_len = 0;
+	srb_ext->req.gen_req_t.code = GEN_IO_CODE_EJECT;
+	srb_ext->req.gen_req_t.arg1 = load;
+	srb_ext->req.srb_done_cb = bht_scsi_srb_basic_cmd_done;
+	result = req_eject(pdx, srb_ext);
+
+	if (result == REQ_RESULT_OK) {
+		pdx->scsi.scsi_eject = 1;
+		srb->result = GOOD;
+	} else {
+		bht_scsi_error_handle(pdx, srb, result);
+		/* cmd handle by thread case */
+		if (result == REQ_RESULT_PENDING)
+			pdx->scsi.scsi_eject = 1;
+	}
+
+exit:
+	if (result == REQ_RESULT_PENDING) {
+		*busy = TRUE;
+		ret = FALSE;
+	} else {
+		if (result == REQ_RESULT_QUEUE_BUSY)
+			*busy = TRUE;
+		bht_scsi_free_srb_ext(pdx, srb_ext);
+	}
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s, ret:%x\n", __func__, ret);
+	return ret;
+}
+
+/*
+ *	Handle Inquriy scsi command
+ *	return true means done while return false means pending
+ */
+static bool bht_scsi_exec_inquiry(bht_dev_ext_t *pdx, struct scsi_cmnd *srb,
+				  bool *busy)
+{
+
+	e_req_result result = REQ_RESULT_NO_CARD;
+	sd_card_t *card = &pdx->card;
+	srb_ext_t *srb_ext = NULL;
+	bool ret = TRUE;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+
+	srb_ext = bht_scsi_alloc_srb_ext(pdx, srb);
+	if (srb_ext == NULL) {
+		result = REQ_RESULT_ABORT;
+		goto exit;
+	}
+	srb_ext->req.data_dir = DATA_DIR_IN;
+	/* the callback will handle the data itself */
+	srb_ext->req.srb_buff = NULL;
+	srb_ext->req.gen_req_t.code = GEN_IO_CODE_INIT_CARD;
+	srb_ext->req.srb_done_cb = bht_scsi_srb_basic_cmd_done;
+	result = req_chk_card_info(pdx, srb_ext);
+
+	if (result == REQ_RESULT_OK) {
+		bht_scsi_get_inquiry(pdx, card->card_type, srb);
+	} else {
+		if (result == REQ_RESULT_QUEUE_BUSY)
+			*busy = TRUE;
+		bht_scsi_error_handle(pdx, srb, result);
+	}
+exit:
+	if (result == REQ_RESULT_PENDING)
+		ret = FALSE;
+	else
+		bht_scsi_free_srb_ext(pdx, srb_ext);
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s, ret:%x\n", __func__, ret);
+	return ret;
+}
+
+/*
+ *	Handle GetCapacity scsi command
+ *	return true means done while return false means pending
+ */
+static bool bht_scsi_exec_capacity(bht_dev_ext_t *pdx, struct scsi_cmnd *srb,
+				   bool *busy)
+{
+	e_req_result result = REQ_RESULT_NO_CARD;
+	sd_card_t *card = &pdx->card;
+	srb_ext_t *srb_ext;
+
+	bool ret = TRUE;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Enter %s\n", __func__);
+
+	srb_ext = bht_scsi_alloc_srb_ext(pdx, srb);
+	if (srb_ext == NULL) {
+		result = REQ_RESULT_ABORT;
+		goto exit;
+	}
+	srb_ext->req.data_dir = DATA_DIR_IN;
+	/* the callback will handle the data itself */
+	srb_ext->req.srb_buff = NULL;
+	srb_ext->req.srb_sg_len = 0;
+	srb_ext->req.gen_req_t.code = GEN_IO_CODE_INIT_CARD;
+	srb_ext->req.srb_done_cb = bht_scsi_srb_basic_cmd_done;
+
+	result = req_chk_card_info(pdx, srb_ext);
+
+	if (result == REQ_RESULT_OK) {
+		bht_scsi_get_capacity(pdx, (u32) (card->sec_count), srb);
+	} else {
+		if (result == REQ_RESULT_QUEUE_BUSY)
+			*busy = TRUE;
+		bht_scsi_error_handle(pdx, srb, result);
+	}
+exit:
+	if (result == REQ_RESULT_PENDING)
+		ret = FALSE;
+	else
+		bht_scsi_free_srb_ext(pdx, srb_ext);
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM,
+		"Exit %s, ret:%x\n", __func__, ret);
+	return ret;
+}
+
+/*
+ *	Helper function to get rw parameter
+ */
+static void bht_scsi_get_rw_parameter(byte *cdb, u32 *pStartBlock,
+				      u32 *pBlockCount)
+{
+	byte array[4];
+
+	array[0] = cdb[5];
+	array[1] = cdb[4];
+	array[2] = cdb[3];
+	array[3] = cdb[2];
+	*pStartBlock = *((u32 *) &array);
+
+	array[0] = cdb[8];
+	array[1] = cdb[7];
+	array[2] = 0;
+	array[3] = 0;
+	*pBlockCount = *((u32 *) &array);
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM,
+		"%s, StartBlock:%x, BlockCount:%x\n", __func__,
+		*pStartBlock, *pBlockCount);
+}
+
+/*
+ *	Call back for Tagged io and scsi rw
+ */
+static void bht_scsi_srb_tagio_done(void *p, void *srb_ext)
+{
+	srb_ext_t *p_srb_ext = srb_ext;
+	srb_ext_t *p_srb_ext_1 = p_srb_ext;
+	srb_ext_t *p_srb_ext_2 = NULL;
+	bht_dev_ext_t *pdx = p;
+	struct scsi_cmnd *srb = p_srb_ext->psrb;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+	if (srb == NULL) {
+		pdx->p_srb_ext = NULL;
+		DbgErr("tagio complete called with srb is null\n");
+		goto exit;
+	}
+
+	srb->result = CHECK_CONDITION;
+
+	switch (p_srb_ext->req.result) {
+	case REQ_RESULT_OK:
+		{
+			if (p_srb_ext->prev != NULL) {
+				p_srb_ext_1 = p_srb_ext->prev;
+				p_srb_ext_2 = p_srb_ext;
+				DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE,
+					NOT_TO_RAM,
+					"p_srb_ext_2: dir=%d, sec_cnt=%d, sec_addr=0x%x, p_srb_ext_1=%p\n",
+					p_srb_ext_2->req.data_dir,
+					p_srb_ext_2->req.tag_req_t.sec_cnt,
+					p_srb_ext_2->req.tag_req_t.sec_addr,
+					p_srb_ext_2->prev);
+			} else if (p_srb_ext->req.data_dir == DATA_DIR_IN
+				   && p_srb_ext->req.tag_req_t.sec_cnt > 1
+				   && (p_srb_ext->req.tag_req_t.sec_addr +
+				       p_srb_ext->req.tag_req_t.sec_cnt ==
+				       pdx->card.sec_count)) {
+				p_srb_ext_1 = p_srb_ext;
+				p_srb_ext_2 = bht_scsi_alloc_srb_ext(pdx, srb);
+				if (p_srb_ext_2 == NULL) {
+					DbgErr
+					    ("Failed when alloc srb_ext_2 for read latest block\n");
+				} else {
+					p_srb_ext_2->prev = p_srb_ext_1;
+					p_srb_ext_1->next = p_srb_ext_2;
+					p_srb_ext_2->req.data_dir = DATA_DIR_IN;
+					p_srb_ext_2->req.tag_req_t.sec_cnt = 1;
+					p_srb_ext_2->req.tag_req_t.sec_addr =
+					    pdx->card.sec_count - 1;
+					p_srb_ext_2->req.tag_req_t.use_cmd = 0;
+					p_srb_ext_2->req.srb_done_cb =
+					    bht_scsi_srb_tagio_done;
+					p_srb_ext_2->req.srb_buff =
+					    (PVOID) ((unsigned long long)
+						     p_srb_ext->req.srb_buff +
+						     (p_srb_ext->req.tag_req_t.sec_cnt -
+						      1) * SD_BLOCK_LEN);
+				}
+			}
+			if (FALSE ==
+			    cfg_dma_need_sdma_like_buffer(pdx->cfg->host_item.test_dma_mode_setting.dma_mode)) {
+				if (p_srb_ext_2 == NULL
+				    || p_srb_ext_2 == p_srb_ext)
+					os_free_sg_list(pdx, srb);
+				else {
+					if (p_srb_ext_1->req.srb_sg_list[p_srb_ext_1->req.srb_sg_len -
+							    1].Length >=
+					    SD_BLOCK_LEN) {
+						p_srb_ext_2->req.srb_sg_len = 1;
+						p_srb_ext_2->req.srb_sg_list[0].Address =
+						    p_srb_ext_1->req.srb_sg_list
+						    [p_srb_ext_1->req.srb_sg_len - 1].Address +
+						    p_srb_ext_1->req.srb_sg_list
+						    [p_srb_ext_1->req.srb_sg_len - 1].Length -
+						    SD_BLOCK_LEN;
+						p_srb_ext_2->req.srb_sg_list[0].Length =
+						    SD_BLOCK_LEN;
+					} else {
+						DbgErr
+						    ("sg list latest record size is %d, less than SD block size\n",
+						     p_srb_ext_1->req.srb_sg_list
+						     [p_srb_ext_1->req.srb_sg_len
+						      - 1].Length);
+						bht_scsi_free_srb_ext(pdx,
+								      p_srb_ext_2);
+						p_srb_ext_1->next = NULL;
+						p_srb_ext_2 = NULL;
+					}
+				}
+			} else {
+				if (p_srb_ext->req.data_dir == DATA_DIR_IN) {
+					if (p_srb_ext_2 == NULL
+					    || p_srb_ext_2 == p_srb_ext)
+						scsi_sg_copy_from_buffer(srb,
+									 p_srb_ext->req.srb_buff,
+									 srb->sdb.length);
+				}
+			}
+			srb->result = GOOD;
+			break;
+		}
+	case REQ_RESULT_NO_CARD:
+	case REQ_RESULT_ABORT:
+		{
+			bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY,
+					       SCSI_ADSENSE_NO_MEDIA_IN_DEVICE);
+			break;
+		}
+	case REQ_RESULT_PROTECTED:
+		{
+			bht_scsi_set_sensecode(pdx, srb,
+					       SCSI_SENSE_DATA_PROTECT,
+					       SCSI_ADSENSE_WRITE_PROTECT);
+			break;
+		}
+		/* RW error case */
+	default:
+		{
+			bht_scsi_set_sensecode(pdx, srb,
+					       SCSI_SENSE_UNIT_ATTENTION, 0);
+			break;
+		}
+
+	}
+
+	/* tag queue is empty we need to set device to free status */
+	if (tq_is_empty(pdx)) {
+		func_autotimer_start(pdx);
+		if (pdx->host.feature.hw_led_fix == 0)
+			host_led_ctl(&pdx->host, FALSE);
+	}
+
+	if (p_srb_ext_2 == NULL || p_srb_ext_2 == p_srb_ext) {
+		DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM,
+			"scsi io done for srb(%p)\n", srb);
+		bht_scsi_free_srb_ext(pdx, p_srb_ext_2);
+		bht_scsi_free_srb_ext(pdx, p_srb_ext_1);
+		pdx->p_srb_ext = NULL;
+
+		scsi_done(srb);
+	} else {
+		int result = req_tag_io_add(pdx, p_srb_ext_2);
+
+		if (result != REQ_RESULT_OK)
+			bht_scsi_error_handle(pdx, srb, result);
+
+		if (result == REQ_RESULT_PENDING) {
+			DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE,
+				NOT_TO_RAM,
+				"add srb_ext_2(%p) of srb(%p) to tag io queue\n",
+				p_srb_ext_2, srb);
+		} else {
+			DbgErr("Failed to add srb_ext_2 %p to tag io queue\n",
+			       p_srb_ext_2);
+			bht_scsi_free_srb_ext(pdx, p_srb_ext_1);
+			pdx->p_srb_ext = NULL;
+
+			scsi_done(srb);
+		}
+	}
+
+#ifdef DBG_PERFORMANCE
+	calc_io_end(&pdx->tick);
+#endif
+
+exit:
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n",
+		__func__);
+}
+
+/*
+ *	Handle Read10 and Write10 scsi command
+ *	return true means done while return false means pending
+ */
+static bool bht_scsi_exec_rw(bht_dev_ext_t *pdx, struct scsi_cmnd *srb,
+			     bool bWrite, bool *busy)
+{
+	e_req_result result = REQ_RESULT_NO_CARD;
+	srb_ext_t *srb_ext;
+	bool ret = TRUE;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+	srb_ext = bht_scsi_alloc_srb_ext(pdx, srb);
+	if (srb_ext == NULL) {
+		result = REQ_RESULT_ABORT;
+		goto exit;
+	}
+	srb_ext->psrb = srb;
+	srb_ext->req.data_dir = bWrite ? DATA_DIR_OUT : DATA_DIR_IN;
+	srb_ext->req.srb_buff = pdx->os.virt_buff;
+	if (FALSE ==
+	    cfg_dma_need_sdma_like_buffer(pdx->cfg->host_item.test_dma_mode_setting.dma_mode))
+		srb_ext->req.srb_sg_len =
+		    os_get_sg_list(pdx, srb, srb_ext->req.srb_sg_list);
+	else {
+		if (bWrite)
+			scsi_sg_copy_to_buffer(srb, srb_ext->req.srb_buff,
+					       srb->sdb.length);
+		srb_ext->req.srb_sg_len = 0;
+	}
+	srb_ext->req.srb_done_cb = bht_scsi_srb_tagio_done;
+	srb_ext->req.tag_req_t.use_cmd = 0;
+
+	/*
+	 * Get Scsi parameter for RW
+	 */
+	bht_scsi_get_rw_parameter(srb->cmnd,
+				  &srb_ext->req.tag_req_t.sec_addr,
+				  &srb_ext->req.tag_req_t.sec_cnt);
+	calc_req_start(&pdx->tick, srb_ext->req.tag_req_t.sec_cnt, bWrite);
+
+	/* Workaround for GG8 chip DDR200 write operation: timing issue */
+	if (bWrite && pdx->host.chip_type == CHIP_GG8
+	    && pdx->card.info.sw_cur_setting.sd_access_mode ==
+	    SD_FNC_AM_DDR200) {
+		DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM,
+			"Enter GG8 DDR200 workaround patch\n");
+		if (tq_judge_request_continuous
+		    (card_is_low_capacity(&pdx->card), pdx->last_req.data_dir,
+		     pdx->last_req.sec_addr, pdx->last_req.sec_cnt,
+		     srb_ext->req.data_dir,
+		     srb_ext->req.tag_req_t.sec_addr) == FALSE) {
+			DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE,
+				NOT_TO_RAM,
+				"Set flag to add NOP descriptor table\n");
+			srb_ext->req.gg8_ddr200_workaround = 1;
+		} else
+			srb_ext->req.gg8_ddr200_workaround = 0;
+
+	} else
+		srb_ext->req.gg8_ddr200_workaround = 0;
+
+	pdx->last_req.data_dir = srb_ext->req.data_dir;
+	pdx->last_req.sec_addr = srb_ext->req.tag_req_t.sec_addr;
+	pdx->last_req.sec_cnt = srb_ext->req.tag_req_t.sec_cnt;
+
+	result = req_tag_io_add(pdx, srb_ext);
+
+exit:
+	if (result != REQ_RESULT_OK)
+		bht_scsi_error_handle(pdx, srb, result);
+
+	if (result == REQ_RESULT_PENDING)
+		ret = FALSE;
+	else {
+		if (result == REQ_RESULT_QUEUE_BUSY)
+			*busy = TRUE;
+		bht_scsi_free_srb_ext(pdx, srb_ext);
+	}
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM,
+		"Exit %s, ret:%x\n", __func__, ret);
+	return ret;
+}
+
+/*
+ *	The Entry to handle SCSI command
+ */
+static int bht_scsi_queuecommand_lck(struct scsi_cmnd *srb)
+{
+	bht_dev_ext_t *pdx = (bht_dev_ext_t *) (srb->device->host->hostdata[0]);
+	bool cmd_done = TRUE;
+	bool dev_busy = FALSE;
+
+	DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n",
+		__func__);
+
+	if (pdx->signature != BHT_PDX_SIGNATURE) {
+		DbgErr("bht scsi queuecommand pdx signature is wrong\n");
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+
+	if (srb->cmnd == NULL) {
+		DbgErr
+		    ("bhtscsi_queuecommand srb->cmnd is NULL, cmd_len is %d ,srb %p\n",
+		     srb->cmd_len, srb);
+		return SCSI_RETURN_NOT_HANDLED;
+	}
+
+	srb->result = 0;
+	func_thermal_update_time(pdx);
+
+	switch (srb->cmnd[0]) {
+	case TEST_UNIT_READY:
+		{
+			bht_scsi_test_unit_ready(pdx, srb);
+			break;
+		}
+	case REQUEST_SENSE:
+		{
+			bht_scsi_request_sense(pdx, srb);
+			break;
+		}
+	case INQUIRY:
+		{
+			cmd_done = bht_scsi_exec_inquiry(pdx, srb, &dev_busy);
+			break;
+		}
+	case READ_CAPACITY:
+		{
+			cmd_done = bht_scsi_exec_capacity(pdx, srb, &dev_busy);
+			break;
+		}
+	case READ_10:
+		{
+			cmd_done = bht_scsi_exec_rw(pdx, srb, FALSE, &dev_busy);
+			break;
+		}
+	case WRITE_10:
+		{
+			cmd_done = bht_scsi_exec_rw(pdx, srb, TRUE, &dev_busy);
+			break;
+		}
+	case MODE_SENSE:
+		{
+			bht_scsi_exec_modesense(pdx, srb);
+			break;
+		}
+	case VERIFY:
+	case SEND_DIAGNOSTIC:
+	case SYNCHRONIZE_CACHE:
+		{
+			srb->result = GOOD;
+
+			scsi_done(srb);
+			break;
+		}
+	case START_STOP:
+		{
+			cmd_done = bht_scsi_load_unload(pdx, srb, &dev_busy);
+			break;
+		}
+
+	case ALLOW_MEDIUM_REMOVAL:
+		{
+			bht_scsi_allow_removal(pdx, srb);
+			break;
+		}
+
+	default:
+		{
+			srb->result = CHECK_CONDITION;
+			bht_scsi_set_sensecode(pdx, srb,
+					       SCSI_SENSE_ILLEGAL_REQUEST,
+					       SCSI_ADSENSE_ILLEGAL_COMMAND);
+			break;
+		}
+	}
+
+	if (cmd_done && dev_busy == FALSE)
+		scsi_done(srb);
+	else if (dev_busy)
+		return SCSI_MLQUEUE_DEVICE_BUSY;
+	return 0;
+
+}
+
+static enum scsi_timeout_action bht_scsi_eh_timeout(struct scsi_cmnd *srb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
+	enum scsi_timeout_action retval = SCSI_EH_DONE;
+#else
+	enum blk_eh_timer_return retval = BLK_EH_NOT_HANDLED;
+#endif
+
+	bht_dev_ext_t *pdx = (bht_dev_ext_t *) (srb->device->host->hostdata[0]);
+
+	/* todo It is may not safer for busreset is at high IRQL; and one event to slove this */
+	DbgErr("SCSI eh_timeout Enter\n");
+	if (os_pending_thread(pdx, TRUE) == FALSE)
+		DbgErr("bus rest pending thread failed\n");
+	func_autotimer_stop(pdx);
+	card_power_off(&pdx->card, TRUE);
+	host_init(&pdx->host);
+	req_cancel_all_io(pdx);
+	os_pending_thread(pdx, FALSE);
+	DbgErr("SCSI eh_timeout Exit\n");
+
+	return retval;
+}
+
+static DEF_SCSI_QCMD(bht_scsi_queuecommand)
+/*
+ * this defines our 'SCSI host'
+ */
+struct scsi_host_template bht_scsi_template = {
+	.name = "BHT SD Card Reader",
+	.module = THIS_MODULE,
+	.proc_name = "bht_scsi_host",
+	.queuecommand = bht_scsi_queuecommand,
+	.eh_timed_out = bht_scsi_eh_timeout,
+	.can_queue = 1,
+	.this_id = -1,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = CFG_MAX_TRANSFER_LENGTH / SD_BLOCK_LEN,
+	.cmd_per_lun = 1,
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
+	.use_clustering = TRUE,
+#endif
+
+	.emulated = FALSE,
+};
diff --git a/drivers/scsi/bht/linux_os/linux_scsi.h b/drivers/scsi/bht/linux_os/linux_scsi.h
new file mode 100644
index 000000000000..0b4d2a8bd5db
--- /dev/null
+++ b/drivers/scsi/bht/linux_os/linux_scsi.h
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015 BHT Inc.
+ *
+ * File Name: linux_scsi.h
+ *
+ * Abstract: SCSI function
+ *
+ * Version: 1.00
+ *
+ * Author: Peter.Guo
+ *
+ * Environment:	Linux
+ *
+ * History:
+ *
+ * 5/20/2015		Creation	Peter.Guo
+ */
+
+#ifndef _BHT_SCSI_H
+#define _BHT_SCSI_H
+
+#define LINUX_SCSI_MAX_QUEUE_DPETH	32
+
+bool bht_scsi_init(bht_dev_ext_t *pdx, struct device *dev);
+void bht_scsi_uinit(bht_dev_ext_t *pdx);
+
+typedef struct {
+	byte ErrorCode;
+	byte Reserved;
+	byte SenseKey;
+	byte Infomation[4];
+	byte AdditionalSenseLength;
+	byte CommandSpecInfo[4];
+	byte AdditionalSenseCode;
+	byte AdditionalSenseCodeQualifier;
+	byte FieldReplaceable;
+	byte SenseKeySpec[3];
+} SENSE_DATA, *PSENSE_DATA;
+
+typedef struct {
+	byte DeviceType:5;
+	byte DeviceTypeQualifier:3;
+	byte DeviceTypeModifier:7;
+	byte RemovableMedia:1;
+	byte Versions;
+	byte ResponseDataFormat:4;
+	byte HiSupport:1;
+	byte NormACA:1;
+	byte ReservedBit:1;
+	byte AERC:1;
+	byte AdditionalLength;
+	byte Reserved[2];
+	byte SoftReset:1;
+	byte CommandQueue:1;
+	byte Reserved2:1;
+	byte LinkedCommands:1;
+	byte Synchronous:1;
+	byte Wide16Bit:1;
+	byte Wide32Bit:1;
+	byte RelativeAddressing:1;
+	byte VendorId[8];
+	byte ProductId[16];
+	byte ProductRevisionLevel[4];
+	byte VendorSpecific[20];
+	byte IUS:1;
+	byte QAS:1;
+	byte Clocking:2;
+	byte Reserved3_first_byte:4;
+	byte Reserved3[39];
+} _INQUIRYDATA;
+
+typedef struct {
+	u32 LogicalBlockAddress;
+	u32 BytesPerBlock;
+} READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+typedef struct {
+	byte DataLength;
+	byte MediumType;
+	byte Reserved;
+	byte BlockDescLength;
+} MODE_PAGE_HEADER;
+
+typedef struct {
+	u8 PageCode;
+	u8 PageLength;
+	u16 TracksPerZone;
+	u16 AltSectorsPerZone;
+	u16 AltTracksPerZone;
+	u16 AltTracksPerVolume;
+	u16 SectorsPerTrack;
+	u16 BytesPerSector;
+	u16 Interleave;
+	u16 TrackSkew;
+	u16 CylinderSkew;
+	u8 flags;
+	u8 reserved[3];
+} MODE_PAGE3;
+/*
+ * Sense Data Format - Page 4
+ */
+typedef struct {
+	u8 PageCode;
+	u8 PageLength;
+	u16 CylindersHigh;
+	u8 CylindersLow;
+	u8 Heads;
+	u16 WritePrecompHigh;
+	u8 WritePrecompLow;
+	u16 ReducedWriteCurrentHigh;
+	u8 ReducedWriteCurrentLow;
+	u16 StepRate;
+	u16 LandingZoneHigh;
+	u8 LandingZoneLow;
+	u8 flags;
+	u8 RotationalOffset;
+	u8 Reserved;
+	u16 MediumRotationRate;
+	u8 Reserved2[2];
+} MODE_PAGE4;
+
+typedef struct {
+	u8 PageCode;
+	u8 PageLength;
+	u8 Flags;
+	u8 RetensionPriority;
+	u8 DisablePrefetchTransfer[2];
+	u8 MinimumPrefetch[2];
+	u8 MaximumPrefetch[2];
+	u8 MaximumPrefetchCeiling[2];
+	u8 temp[7];
+} MODE_PAGE8;
+
+typedef struct {
+	byte PageCode:6;
+	byte Reserved:1;
+	byte PageSavable:1;
+	byte PageLength;
+
+	byte ReadDisableCache:1;
+	byte MultiplicationFactor:1;
+	byte WriteCacheEnable:1;
+	byte Reserved2:5;
+	byte WriteRetensionPriority:4;
+	byte ReadRetensionPriority:4;
+	byte DisablePrefetchTransfer[2];
+	byte MinimumPrefetch[2];
+	byte MaximumPrefetch[2];
+	byte MaximumPrefetchCeiling[2];
+	byte FSW_LBCSS_RDA;
+	byte NumberofCacheSegments;
+	byte CacheSegmentSize[2];
+	byte Reserved16;
+	byte NonCacheSegmentSize[3];
+} MODE_PAGE_8;
+
+typedef struct {
+	byte PageCode:6;
+	byte Reserved:1;
+	byte PageSavable:1;
+	byte PageLength;
+
+	byte Reserved2;
+	byte ProtocolIdentifier;
+	byte SynchronousTransferTimeout[2];
+	byte Reserved6[2];
+} MODEPAGE19;
+
+typedef struct {
+	MODE_PAGE_HEADER hdr;
+	union {
+		MODEPAGE19 pg_9;
+		MODE_PAGE_8 pg_8;
+		MODE_PAGE8 pg8;
+		MODE_PAGE3 pg3;
+		MODE_PAGE4 pg4;
+	} pdata;
+} MODE_PAGE_DATA;
+
+/* Sense codes */
+#define SCSI_SENSE_NO_SENSE         0x00
+#define SCSI_SENSE_RECOVERED_ERROR  0x01
+#define SCSI_SENSE_NOT_READY        0x02
+#define SCSI_SENSE_MEDIUM_ERROR     0x03
+#define SCSI_SENSE_HARDWARE_ERROR   0x04
+#define SCSI_SENSE_ILLEGAL_REQUEST  0x05
+#define SCSI_SENSE_UNIT_ATTENTION   0x06
+#define SCSI_SENSE_DATA_PROTECT     0x07
+#define SCSI_SENSE_BLANK_CHECK      0x08
+#define SCSI_SENSE_UNIQUE           0x09
+#define SCSI_SENSE_COPY_ABORTED     0x0A
+#define SCSI_SENSE_ABORTED_COMMAND  0x0B
+#define SCSI_SENSE_EQUAL            0x0C
+#define SCSI_SENSE_VOL_OVERFLOW     0x0D
+#define SCSI_SENSE_MISCOMPARE       0x0E
+#define SCSI_SENSE_RESERVED         0x0F
+
+/* Additional Sense codes */
+#define SCSI_ADSENSE_NO_SENSE                              0x00
+#define SCSI_ADSENSE_NO_SEEK_COMPLETE                      0x02
+#define SCSI_ADSENSE_LUN_NOT_READY                         0x04
+#define SCSI_ADSENSE_LUN_COMMUNICATION                     0x08
+#define SCSI_ADSENSE_WRITE_ERROR                           0x0C
+#define SCSI_ADSENSE_TRACK_ERROR                           0x14
+#define SCSI_ADSENSE_SEEK_ERROR                            0x15
+#define SCSI_ADSENSE_REC_DATA_NOECC                        0x17
+#define SCSI_ADSENSE_REC_DATA_ECC                          0x18
+#define SCSI_ADSENSE_PARAMETER_LIST_LENGTH                 0x1A
+#define SCSI_ADSENSE_ILLEGAL_COMMAND                       0x20
+#define SCSI_ADSENSE_ILLEGAL_BLOCK                         0x21
+#define SCSI_ADSENSE_INVALID_CDB                           0x24
+#define SCSI_ADSENSE_INVALID_LUN                           0x25
+#define SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST          0x26
+#define SCSI_ADSENSE_WRITE_PROTECT                         0x27
+#define SCSI_ADSENSE_MEDIUM_CHANGED                        0x28
+#define SCSI_ADSENSE_BUS_RESET                             0x29
+#define SCSI_ADSENSE_PARAMETERS_CHANGED                    0x2A
+#define SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION       0x2E
+#define SCSI_ADSENSE_INVALID_MEDIA                         0x30
+#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE                    0x3a
+#define SCSI_ADSENSE_POSITION_ERROR                        0x3b
+#define SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED          0x3f
+#define SCSI_ADSENSE_OPERATOR_REQUEST                      0x5a
+#define SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED 0x5d
+#define SCSI_ADSENSE_ILLEGAL_MODE_FOR_THIS_TRACK           0x64
+#define SCSI_ADSENSE_COPY_PROTECTION_FAILURE               0x6f
+#define SCSI_ADSENSE_POWER_CALIBRATION_ERROR               0x73
+#define SCSI_ADSENSE_VENDOR_UNIQUE                         0x80
+#define SCSI_ADSENSE_MUSIC_AREA                            0xA0
+#define SCSI_ADSENSE_DATA_AREA                             0xA1
+#define SCSI_ADSENSE_VOLUME_OVERFLOW                       0xA7
+
+/* for legacy apps : */
+
+extern struct kmem_cache *bht_srb_ext_cachep;
+extern mempool_t *bht_sd_mem_pool;
+
+#endif
